From 65b0b44dbf9dfec3f78ded84ee22b0a0a1bf82bf Mon Sep 17 00:00:00 2001
From: Philipp Reisner <philipp.reisner@linbit.com>
Date: Thu, 25 Jun 2009 15:57:22 +0200
Subject: [PATCH] Tracking DRBD mainline (and minor cleanups)

   * drbd-8.3: (134 commits)
      Missing pices of the unaligned memory access stuff.
      possible fix for XEN crashes on disconnect
      fix regression: initial sync target hung in WFBitMapT
      fix a comment: there are no more ioctls.
      possible fix for XEN crashes on disconnect
      fix regression: initial sync target hung in WFBitMapT
      ...

Removed compat code from lru_cache.h
All STATIC -> static
DRBD_ENABLE_FAULTS -> CONFIG_DRBD_FAULT_INJECTION

    * drbd-8.3:
      Fixed some errors/warnings when compiles without DBG_ALL_SYMBOLS (i.e. STATIC = static)
      Fixed a regression introduced with fb51e2eb1fac83839231499333bf683629388484

No longer include drbd_config.h directly, include drbd.h instead
Got rid of drbd_config.h
Support lru_cache as module
Removing the drbd_buildtag.c file

    * drbd-8.3:
      Fixes for architectures that does not support unaligned memory accesses
      fix reading of the AL ring buffer
      sync handshake: fix detection of "unrelated" data - it was detected as "regular" split-brain

    * drbd-8.3:
      Preparing 8.3.2rc2
      compat: 2.6.31 -- q->limits.* and accessor functions

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
---
 drivers/block/drbd/Kconfig         |  32 +++
 drivers/block/drbd/Makefile        |   2 +-
 drivers/block/drbd/drbd_actlog.c   |  65 +++---
 drivers/block/drbd/drbd_bitmap.c   |  35 +--
 drivers/block/drbd/drbd_buildtag.c |   7 -
 drivers/block/drbd/drbd_int.h      |  65 +++---
 drivers/block/drbd/drbd_main.c     | 222 ++++++++++++------
 drivers/block/drbd/drbd_nl.c       | 362 +++++++++++++++--------------
 drivers/block/drbd/drbd_proc.c     |  11 +-
 drivers/block/drbd/drbd_receiver.c | 235 +++++++++++--------
 drivers/block/drbd/drbd_req.c      |  10 +-
 drivers/block/drbd/drbd_strings.c  |   6 +-
 drivers/block/drbd/drbd_tracing.c  |  10 +-
 drivers/block/drbd/drbd_worker.c   | 134 ++++++++---
 drivers/block/drbd/drbd_wrappers.h |   5 -
 include/linux/drbd.h               |  16 +-
 include/linux/drbd_config.h        |  37 ---
 include/linux/drbd_limits.h        |   4 +
 include/linux/drbd_nl.h            |   4 +-
 include/linux/lru_cache.h          |  87 +++----
 lib/lru_cache.c                    | 260 ++++++++++++++-------
 21 files changed, 951 insertions(+), 658 deletions(-)
 delete mode 100644 drivers/block/drbd/drbd_buildtag.c
 delete mode 100644 include/linux/drbd_config.h

diff --git a/drivers/block/drbd/Kconfig b/drivers/block/drbd/Kconfig
index b3676771731..f133a8925cb 100644
--- a/drivers/block/drbd/Kconfig
+++ b/drivers/block/drbd/Kconfig
@@ -46,3 +46,35 @@ config DRBD_TRACE
 	  Say Y here if you want to be able to trace various events in DRBD.
 
 	  If unsure, say N.
+
+config DRBD_FAULT_INJECTION
+	bool "DRBD fault injection"
+	depends on BLK_DEV_DRBD
+	help
+
+	  Say Y here if you want to simulate IO errors, in order to test DRBD's
+	  behavior.
+
+	  The actual simulation of IO errors is done by writing 3 values to
+	  /sys/module/drbd/parameters/
+
+	  enable_faults: bitmask of...
+	  1	meta data write
+	  2               read
+	  4	resync data write
+	  8	            read
+	  16	data write
+	  32	data read
+	  64	read ahead
+	  128	kmalloc of bitmap
+	  256	allocation of EE (epoch_entries)
+
+	  fault_devs: bitmask of minor numbers
+	  fault_rate: frequency in percent
+
+	  Example: Simulate data write errors on /dev/drbd0 with a probability of 5%.
+		echo 16 > /sys/module/drbd/parameters/enable_faults
+		echo 1 > /sys/module/drbd/parameters/fault_devs
+		echo 5 > /sys/module/drbd/parameters/fault_rate
+
+	  If unsure, say N.
diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile
index 9dd069b0ded..68d1e7ce9aa 100644
--- a/drivers/block/drbd/Makefile
+++ b/drivers/block/drbd/Makefile
@@ -1,4 +1,4 @@
-drbd-y := drbd_buildtag.o drbd_bitmap.o drbd_proc.o
+drbd-y := drbd_bitmap.o drbd_proc.o
 drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o
 drbd-y += drbd_main.o drbd_strings.o drbd_nl.o
 
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index 6b096b1720e..1e53d16c943 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -77,7 +77,7 @@ void trace_drbd_resync(struct drbd_conf *mdev, int level, const char *fmt, ...)
 	va_end(ap);
 }
 
-STATIC int _drbd_md_sync_page_io(struct drbd_conf *mdev,
+static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
 				 struct drbd_backing_dev *bdev,
 				 struct page *page, sector_t sector,
 				 int rw, int size)
@@ -133,7 +133,7 @@ STATIC int _drbd_md_sync_page_io(struct drbd_conf *mdev,
 int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
 			 sector_t sector, int rw)
 {
-	int hardsect_size, mask, ok;
+	int logical_block_size, mask, ok;
 	int offset = 0;
 	struct page *iop = mdev->md_io_page;
 
@@ -141,15 +141,15 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
 
 	BUG_ON(!bdev->md_bdev);
 
-	hardsect_size = drbd_get_hardsect_size(bdev->md_bdev);
-	if (hardsect_size == 0)
-		hardsect_size = MD_SECTOR_SIZE;
+	logical_block_size = bdev_logical_block_size(bdev->md_bdev);
+	if (logical_block_size == 0)
+		logical_block_size = MD_SECTOR_SIZE;
 
-	/* in case hardsect_size != 512 [ s390 only? ] */
-	if (hardsect_size != MD_SECTOR_SIZE) {
-		mask = (hardsect_size / MD_SECTOR_SIZE) - 1;
+	/* in case logical_block_size != 512 [ s390 only? ] */
+	if (logical_block_size != MD_SECTOR_SIZE) {
+		mask = (logical_block_size / MD_SECTOR_SIZE) - 1;
 		D_ASSERT(mask == 1 || mask == 3 || mask == 7);
-		D_ASSERT(hardsect_size == (mask+1) * MD_SECTOR_SIZE);
+		D_ASSERT(logical_block_size == (mask+1) * MD_SECTOR_SIZE);
 		offset = sector & mask;
 		sector = sector & ~mask;
 		iop = mdev->md_io_tmpp;
@@ -161,11 +161,11 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
 			void *hp = page_address(mdev->md_io_tmpp);
 
 			ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector,
-					READ, hardsect_size);
+					READ, logical_block_size);
 
 			if (unlikely(!ok)) {
 				dev_err(DEV, "drbd_md_sync_page_io(,%llus,"
-				    "READ [hardsect_size!=512]) failed!\n",
+				    "READ [logical_block_size!=512]) failed!\n",
 				    (unsigned long long)sector);
 				return 0;
 			}
@@ -180,14 +180,14 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
 		     current->comm, current->pid, __func__,
 		     (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
 
-	ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, hardsect_size);
+	ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, logical_block_size);
 	if (unlikely(!ok)) {
 		dev_err(DEV, "drbd_md_sync_page_io(,%llus,%s) failed!\n",
 		    (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
 		return 0;
 	}
 
-	if (hardsect_size != MD_SECTOR_SIZE && !(rw & WRITE)) {
+	if (logical_block_size != MD_SECTOR_SIZE && !(rw & WRITE)) {
 		void *p = page_address(mdev->md_io_page);
 		void *hp = page_address(mdev->md_io_tmpp);
 
@@ -378,7 +378,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
  *
  * Returns -1 on IO error, 0 on checksum error and 1 upon success.
  */
-STATIC int drbd_al_read_tr(struct drbd_conf *mdev,
+static int drbd_al_read_tr(struct drbd_conf *mdev,
 			   struct drbd_backing_dev *bdev,
 			   struct al_transaction *b,
 			   int index)
@@ -416,14 +416,14 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
 	int i;
 	int rv;
 	int mx;
-	int cnr;
 	int active_extents = 0;
 	int transactions = 0;
-	int overflow = 0;
-	int from = -1;
-	int to = -1;
-	u32 from_tnr = -1;
+	int found_valid = 0;
+	int from = 0;
+	int to = 0;
+	u32 from_tnr = 0;
 	u32 to_tnr = 0;
+	u32 cnr;
 
 	mx = div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT);
 
@@ -444,22 +444,27 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
 		}
 		cnr = be32_to_cpu(buffer->tr_number);
 
-		if (cnr == -1)
-			overflow = 1;
-
-		if (cnr < from_tnr && !overflow) {
+		if (++found_valid == 1) {
+			from = i;
+			to = i;
+			from_tnr = cnr;
+			to_tnr = cnr;
+			continue;
+		}
+		if ((int)cnr - (int)from_tnr < 0) {
+			D_ASSERT(from_tnr - cnr + i - from == mx+1);
 			from = i;
 			from_tnr = cnr;
 		}
-		if (cnr > to_tnr) {
+		if ((int)cnr - (int)to_tnr > 0) {
+			D_ASSERT(cnr - to_tnr == i - to);
 			to = i;
 			to_tnr = cnr;
 		}
 	}
 
-	if (from == -1 || to == -1) {
+	if (!found_valid) {
 		dev_warn(DEV, "No usable activity log found.\n");
-
 		mutex_unlock(&mdev->md_io_mutex);
 		return 1;
 	}
@@ -524,7 +529,7 @@ cancel:
 	return 1;
 }
 
-STATIC void atodb_endio(struct bio *bio, int error)
+static void atodb_endio(struct bio *bio, int error)
 {
 	struct drbd_atodb_wait *wc = bio->bi_private;
 	struct drbd_conf *mdev = wc->mdev;
@@ -555,7 +560,7 @@ STATIC void atodb_endio(struct bio *bio, int error)
 #define S2W(s)	((s)<<(BM_EXT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL))
 /* activity log to on disk bitmap -- prepare bio unless that sector
  * is already covered by previously prepared bios */
-STATIC int atodb_prepare_unless_covered(struct drbd_conf *mdev,
+static int atodb_prepare_unless_covered(struct drbd_conf *mdev,
 					struct bio **bios,
 					unsigned int enr,
 					struct drbd_atodb_wait *wc) __must_hold(local)
@@ -803,7 +808,7 @@ void drbd_al_shrink(struct drbd_conf *mdev)
 	wake_up(&mdev->al_wait);
 }
 
-STATIC int w_update_odbm(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int w_update_odbm(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct update_odbm_work *udw = (struct update_odbm_work *)w;
 
@@ -840,7 +845,7 @@ STATIC int w_update_odbm(struct drbd_conf *mdev, struct drbd_work *w, int unused
  *
  * TODO will be obsoleted once we have a caching lru of the on disk bitmap
  */
-STATIC void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
+static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
 				      int count, int success)
 {
 	struct lc_element *e;
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index d9b59b0611b..417da6e3cea 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -26,6 +26,7 @@
 #include <linux/vmalloc.h>
 #include <linux/string.h>
 #include <linux/drbd.h>
+#include <asm/kmap_types.h>
 #include "drbd_int.h"
 
 /* OPAQUE outside this file!
@@ -150,7 +151,7 @@ void drbd_bm_unlock(struct drbd_conf *mdev)
 }
 
 /* word offset to long pointer */
-STATIC unsigned long *__bm_map_paddr(struct drbd_bitmap *b, unsigned long offset, const enum km_type km)
+static unsigned long *__bm_map_paddr(struct drbd_bitmap *b, unsigned long offset, const enum km_type km)
 {
 	struct page *page;
 	unsigned long page_nr;
@@ -197,7 +198,7 @@ void bm_unmap(unsigned long *p_addr)
  * to be able to report device specific.
  */
 
-STATIC void bm_free_pages(struct page **pages, unsigned long number)
+static void bm_free_pages(struct page **pages, unsigned long number)
 {
 	unsigned long i;
 	if (!pages)
@@ -215,7 +216,7 @@ STATIC void bm_free_pages(struct page **pages, unsigned long number)
 	}
 }
 
-STATIC void bm_vk_free(void *ptr, int v)
+static void bm_vk_free(void *ptr, int v)
 {
 	if (v)
 		vfree(ptr);
@@ -226,7 +227,7 @@ STATIC void bm_vk_free(void *ptr, int v)
 /*
  * "have" and "want" are NUMBER OF PAGES.
  */
-STATIC struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
+static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
 {
 	struct page **old_pages = b->bm_pages;
 	struct page **new_pages, *page;
@@ -239,7 +240,11 @@ STATIC struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
 	if (have == want)
 		return old_pages;
 
-	/* Trying kmalloc first, falling back to vmalloc... */
+	/* Trying kmalloc first, falling back to vmalloc.
+	 * GFP_KERNEL is ok, as this is done when a lower level disk is
+	 * "attached" to the drbd.  Context is receiver thread or cqueue
+	 * thread.  As we have no disk yet, we are not in the IO path,
+	 * not even the IO path of the peer. */
 	bytes = sizeof(struct page *)*want;
 	new_pages = kmalloc(bytes, GFP_KERNEL);
 	if (!new_pages) {
@@ -320,7 +325,7 @@ void drbd_bm_cleanup(struct drbd_conf *mdev)
  * this masks out the remaining bits.
  * Rerturns the number of bits cleared.
  */
-STATIC int bm_clear_surplus(struct drbd_bitmap *b)
+static int bm_clear_surplus(struct drbd_bitmap *b)
 {
 	const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1;
 	size_t w = b->bm_bits >> LN2_BPL;
@@ -343,7 +348,7 @@ STATIC int bm_clear_surplus(struct drbd_bitmap *b)
 	return cleared;
 }
 
-STATIC void bm_set_surplus(struct drbd_bitmap *b)
+static void bm_set_surplus(struct drbd_bitmap *b)
 {
 	const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1;
 	size_t w = b->bm_bits >> LN2_BPL;
@@ -362,7 +367,7 @@ STATIC void bm_set_surplus(struct drbd_bitmap *b)
 	bm_unmap(p_addr);
 }
 
-STATIC unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endian)
+static unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endian)
 {
 	unsigned long *p_addr, *bm, offset = 0;
 	unsigned long bits = 0;
@@ -420,7 +425,7 @@ void _drbd_bm_recount_bits(struct drbd_conf *mdev, char *file, int line)
 }
 
 /* offset and len in long words.*/
-STATIC void bm_memset(struct drbd_bitmap *b, size_t offset, int c, size_t len)
+static void bm_memset(struct drbd_bitmap *b, size_t offset, int c, size_t len)
 {
 	unsigned long *p_addr, *bm;
 	size_t do_now, end;
@@ -752,7 +757,7 @@ static void bm_async_io_complete(struct bio *bio, int error)
 	bio_put(bio);
 }
 
-STATIC void bm_page_io_async(struct drbd_conf *mdev, struct drbd_bitmap *b, int page_nr, int rw) __must_hold(local)
+static void bm_page_io_async(struct drbd_conf *mdev, struct drbd_bitmap *b, int page_nr, int rw) __must_hold(local)
 {
 	/* we are process context. we always get a bio */
 	struct bio *bio = bio_alloc(GFP_KERNEL, 1);
@@ -790,6 +795,8 @@ void bm_cpu_to_lel(struct drbd_bitmap *b)
 	 * this may be optimized by using
 	 * cpu_to_lel(-1) == -1 and cpu_to_lel(0) == 0;
 	 * the following is still not optimal, but better than nothing */
+	unsigned int i;
+	unsigned long *p_addr, *bm;
 	if (b->bm_set == 0) {
 		/* no page at all; avoid swap if all is 0 */
 		i = b->bm_number_of_pages;
@@ -801,12 +808,10 @@ void bm_cpu_to_lel(struct drbd_bitmap *b)
 		i = 0;
 	}
 	for (; i < b->bm_number_of_pages; i++) {
-		unsigned long *bm;
-		/* if you'd want to use kmap_atomic, you'd have to disable irq! */
-		p_addr = kmap(b->bm_pages[i]);
+		p_addr = kmap_atomic(b->bm_pages[i], KM_USER0);
 		for (bm = p_addr; bm < p_addr + PAGE_SIZE/sizeof(long); bm++)
 			*bm = cpu_to_lel(*bm);
-		kunmap(p_addr);
+		kunmap_atomic(p_addr, KM_USER0);
 	}
 }
 # endif
@@ -816,7 +821,7 @@ void bm_cpu_to_lel(struct drbd_bitmap *b)
 /*
  * bm_rw: read/write the whole bitmap from/to its on disk location.
  */
-STATIC int bm_rw(struct drbd_conf *mdev, int rw) __must_hold(local)
+static int bm_rw(struct drbd_conf *mdev, int rw) __must_hold(local)
 {
 	struct drbd_bitmap *b = mdev->bitmap;
 	/* sector_t sector; */
diff --git a/drivers/block/drbd/drbd_buildtag.c b/drivers/block/drbd/drbd_buildtag.c
deleted file mode 100644
index 20fe72a104d..00000000000
--- a/drivers/block/drbd/drbd_buildtag.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/* automatically generated. DO NOT EDIT. */
-#include <linux/drbd_config.h>
-const char *drbd_buildtag(void)
-{
-	return "GIT-hash: b0abb3832a730d4fbd145013f6f51fc977bba3cc drbd/drbd_int.h"
-		" build by phil@fat-tyre, 2009-05-15 11:54:26";
-}
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 83f9f33e65e..a63595d8057 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -106,22 +106,6 @@ extern char usermode_helper[];
 
 struct drbd_conf;
 
-#ifdef DBG_ALL_SYMBOLS
-# define STATIC
-#else
-# define STATIC static
-#endif
-
-/*
- * Some Message Macros
- *************************/
-
-#define DUMPP(A)   dev_err(DEV, #A " = %p in %s:%d\n", (A), __FILE__, __LINE__);
-#define DUMPLU(A)  dev_err(DEV, #A " = %lu in %s:%d\n", (unsigned long)(A), __FILE__, __LINE__);
-#define DUMPLLU(A) dev_err(DEV, #A " = %llu in %s:%d\n", (unsigned long long)(A), __FILE__, __LINE__);
-#define DUMPLX(A)  dev_err(DEV, #A " = %lx in %s:%d\n", (A), __FILE__, __LINE__);
-#define DUMPI(A)   dev_err(DEV, #A " = %d in %s:%d\n", (int)(A), __FILE__, __LINE__);
-
 
 /* to shorten dev_warn(DEV, "msg"); and relatives statements */
 #define DEV (disk_to_dev(mdev->vdisk))
@@ -139,14 +123,14 @@ struct drbd_conf;
 /* Defines to control fault insertion */
 enum {
     DRBD_FAULT_MD_WR = 0,	/* meta data write */
-    DRBD_FAULT_MD_RD,		/*           read  */
-    DRBD_FAULT_RS_WR,		/* resync          */
-    DRBD_FAULT_RS_RD,
-    DRBD_FAULT_DT_WR,		/* data            */
-    DRBD_FAULT_DT_RD,
-    DRBD_FAULT_DT_RA,		/* data read ahead */
-    DRBD_FAULT_BM_ALLOC,        /* bitmap allocation */
-    DRBD_FAULT_AL_EE,		/* alloc ee */
+    DRBD_FAULT_MD_RD = 1,	/*           read  */
+    DRBD_FAULT_RS_WR = 2,	/* resync          */
+    DRBD_FAULT_RS_RD = 3,
+    DRBD_FAULT_DT_WR = 4,	/* data            */
+    DRBD_FAULT_DT_RD = 5,
+    DRBD_FAULT_DT_RA = 6,	/* data read ahead */
+    DRBD_FAULT_BM_ALLOC = 7,	/* bitmap allocation */
+    DRBD_FAULT_AL_EE = 8,	/* alloc ee */
 
     DRBD_FAULT_MAX,
 };
@@ -332,6 +316,10 @@ static inline void bm_xfer_ctx_bit_to_word_offset(struct bm_xfer_ctx *c)
 #endif
 }
 
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
 /* This is the layout for a packet on the wire.
  * The byteorder is the network byte order.
  *     (except block_id and barrier fields.
@@ -543,6 +531,7 @@ struct p_compressed_bm {
 	u8 code[0];
 } __packed;
 
+/* DCBP: Drbd Compressed Bitmap Packet ... */
 static inline enum drbd_bitmap_code
 DCBP_get_code(struct p_compressed_bm *p)
 {
@@ -795,6 +784,8 @@ enum {
 				 * but worker thread is still handling the cleanup.
 				 * reconfiguring (nl_disk_conf, nl_net_conf) is dissalowed,
 				 * while this is set. */
+	RESIZE_PENDING,		/* Size change detected locally, waiting for the response from
+				 * the peer, if it changed there as well. */
 };
 
 struct drbd_bitmap; /* opaque for drbd_conf */
@@ -946,12 +937,16 @@ struct drbd_conf {
 	unsigned long rs_mark_time;
 	/* skipped because csum was equeal [unit BM_BLOCK_SIZE] */
 	unsigned long rs_same_csum;
+
+	/* where does the admin want us to start? (sector) */
+	sector_t ov_start_sector;
+	/* where are we now? (sector) */
 	sector_t ov_position;
-	/* Start sector of out of sync range. */
+	/* Start sector of out of sync range (to merge printk reporting). */
 	sector_t ov_last_oos_start;
 	/* size of out-of-sync range in sectors. */
 	sector_t ov_last_oos_size;
-	unsigned long ov_left;
+	unsigned long ov_left; /* in bits */
 	struct crypto_hash *csums_tfm;
 	struct crypto_hash *verify_tfm;
 
@@ -991,7 +986,7 @@ struct drbd_conf {
 	atomic_t pp_in_use;
 	wait_queue_head_t ee_wait;
 	struct page *md_io_page;	/* one page buffer for md_io */
-	struct page *md_io_tmpp;	/* for hardsect_size != 512 [s390 only?] */
+	struct page *md_io_tmpp;	/* for logical_block_size != 512 */
 	struct mutex md_io_mutex;	/* protects the md_io_buffer */
 	spinlock_t al_lock;
 	wait_queue_head_t al_wait;
@@ -1103,7 +1098,7 @@ extern int drbd_send_protocol(struct drbd_conf *mdev);
 extern int drbd_send_uuids(struct drbd_conf *mdev);
 extern int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev);
 extern int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val);
-extern int drbd_send_sizes(struct drbd_conf *mdev);
+extern int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply);
 extern int _drbd_send_state(struct drbd_conf *mdev);
 extern int drbd_send_state(struct drbd_conf *mdev);
 extern int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock,
@@ -1127,8 +1122,6 @@ extern int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd,
 			struct p_data *dp);
 extern int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd,
 			    sector_t sector, int blksize, u64 block_id);
-extern int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
-			int offset, size_t size);
 extern int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd,
 			   struct drbd_epoch_entry *e);
 extern int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req);
@@ -1348,7 +1341,9 @@ extern int drbd_bm_count_bits(struct drbd_conf *mdev, const unsigned long s, con
 /* drbd_main.c */
 
 extern struct kmem_cache *drbd_request_cache;
-extern struct kmem_cache *drbd_ee_cache;
+extern struct kmem_cache *drbd_ee_cache;	/* epoch entries */
+extern struct kmem_cache *drbd_bm_ext_cache;	/* bitmap extents */
+extern struct kmem_cache *drbd_al_ext_cache;	/* activity log extents */
 extern mempool_t *drbd_request_mempool;
 extern mempool_t *drbd_ee_mempool;
 
@@ -1388,7 +1383,7 @@ extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);
 
 /* drbd_worker.c */
 extern int drbd_worker(struct drbd_thread *thi);
-extern void drbd_alter_sa(struct drbd_conf *mdev, int na);
+extern int drbd_alter_sa(struct drbd_conf *mdev, int na);
 extern void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side);
 extern void resume_next_sg(struct drbd_conf *mdev);
 extern void suspend_other_sg(struct drbd_conf *mdev);
@@ -1409,7 +1404,7 @@ static inline void ov_oos_print(struct drbd_conf *mdev)
 }
 
 
-void drbd_csum(struct drbd_conf *, struct crypto_hash *, struct bio *, void *);
+extern void drbd_csum(struct drbd_conf *, struct crypto_hash *, struct bio *, void *);
 /* worker callbacks */
 extern int w_req_cancel_conflict(struct drbd_conf *, struct drbd_work *, int);
 extern int w_read_retry_remote(struct drbd_conf *, struct drbd_work *, int);
@@ -1704,9 +1699,11 @@ static inline sector_t drbd_md_last_sector(struct drbd_backing_dev *bdev)
 	}
 }
 
+/* Returns the number of 512 byte sectors of the device */
 static inline sector_t drbd_get_capacity(struct block_device *bdev)
 {
-	return bdev ? get_capacity(bdev->bd_disk) : 0;
+	/* return bdev ? get_capacity(bdev->bd_disk) : 0; */
+	return bdev ? bdev->bd_inode->i_size >> 9 : 0;
 }
 
 /**
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index ad296842b96..73c6a9da764 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -26,7 +26,7 @@
 #include <linux/autoconf.h>
 #include <linux/module.h>
 #include <linux/version.h>
-
+#include <linux/drbd.h>
 #include <asm/uaccess.h>
 #include <asm/types.h>
 #include <net/sock.h>
@@ -37,7 +37,6 @@
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <linux/mm.h>
-#include <linux/drbd_config.h>
 #include <linux/memcontrol.h>
 #include <linux/mm_inline.h>
 #include <linux/slab.h>
@@ -50,7 +49,6 @@
 #include <linux/unistd.h>
 #include <linux/vmalloc.h>
 
-#include <linux/drbd.h>
 #include <linux/drbd_limits.h>
 #include "drbd_int.h"
 #include "drbd_tracing.h"
@@ -73,12 +71,12 @@ int drbd_asender(struct drbd_thread *);
 int drbd_init(void);
 static int drbd_open(struct block_device *bdev, fmode_t mode);
 static int drbd_release(struct gendisk *gd, fmode_t mode);
-STATIC int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused);
-STATIC void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
+static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 			   union drbd_state ns, enum chg_state_flags flags);
-STATIC int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused);
-STATIC void md_sync_timer_fn(unsigned long data);
-STATIC int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static void md_sync_timer_fn(unsigned long data);
+static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused);
 
 DEFINE_TRACE(drbd_unplug);
 DEFINE_TRACE(drbd_uuid);
@@ -95,6 +93,7 @@ DEFINE_TRACE(drbd_req);
 MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, "
 	      "Lars Ellenberg <lars@linbit.com>");
 MODULE_DESCRIPTION("drbd - Distributed Replicated Block Device v" REL_VERSION);
+MODULE_VERSION(REL_VERSION);
 MODULE_LICENSE("GPL");
 MODULE_PARM_DESC(minor_count, "Maximum number of drbd devices (1-255)");
 MODULE_ALIAS_BLOCKDEV_MAJOR(DRBD_MAJOR);
@@ -110,7 +109,7 @@ module_param(allow_oos, bool, 0);
 module_param(cn_idx, uint, 0444);
 module_param(proc_details, int, 0644);
 
-#ifdef DRBD_ENABLE_FAULTS
+#ifdef CONFIG_DRBD_FAULT_INJECTION
 int enable_faults;
 int fault_rate;
 static int fault_count;
@@ -144,7 +143,9 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0
 struct drbd_conf **minor_table;
 
 struct kmem_cache *drbd_request_cache;
-struct kmem_cache *drbd_ee_cache;
+struct kmem_cache *drbd_ee_cache;	/* epoch entries */
+struct kmem_cache *drbd_bm_ext_cache;	/* bitmap extents */
+struct kmem_cache *drbd_al_ext_cache;	/* activity log extents */
 mempool_t *drbd_request_mempool;
 mempool_t *drbd_ee_mempool;
 
@@ -161,7 +162,7 @@ wait_queue_head_t drbd_pp_wait;
 
 DEFINE_RATELIMIT_STATE(drbd_ratelimit_state, 5 * HZ, 5);
 
-STATIC struct block_device_operations drbd_ops = {
+static struct block_device_operations drbd_ops = {
 	.owner =   THIS_MODULE,
 	.open =    drbd_open,
 	.release = drbd_release,
@@ -198,10 +199,11 @@ int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins)
  * Each &struct drbd_tl_epoch has a circular double linked list of requests
  * attached.
  */
-STATIC int tl_init(struct drbd_conf *mdev)
+static int tl_init(struct drbd_conf *mdev)
 {
 	struct drbd_tl_epoch *b;
 
+	/* during device minor initialization, we may well use GFP_KERNEL */
 	b = kmalloc(sizeof(struct drbd_tl_epoch), GFP_KERNEL);
 	if (!b)
 		return 0;
@@ -222,7 +224,7 @@ STATIC int tl_init(struct drbd_conf *mdev)
 	return 1;
 }
 
-STATIC void tl_cleanup(struct drbd_conf *mdev)
+static void tl_cleanup(struct drbd_conf *mdev)
 {
 	D_ASSERT(mdev->oldest_tle == mdev->newest_tle);
 	D_ASSERT(list_empty(&mdev->out_of_sequence_requests));
@@ -472,7 +474,7 @@ int drbd_io_error(struct drbd_conf *mdev, int force_detach)
  * @os:		old (current) state.
  * @ns:		new (wanted) state.
  */
-STATIC int cl_wide_st_chg(struct drbd_conf *mdev,
+static int cl_wide_st_chg(struct drbd_conf *mdev,
 			  union drbd_state os, union drbd_state ns)
 {
 	return (os.conn >= C_CONNECTED && ns.conn >= C_CONNECTED &&
@@ -513,15 +515,15 @@ void drbd_force_state(struct drbd_conf *mdev,
 	drbd_change_state(mdev, CS_HARD, mask, val);
 }
 
-STATIC int is_valid_state(struct drbd_conf *mdev, union drbd_state ns);
-STATIC int is_valid_state_transition(struct drbd_conf *,
+static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns);
+static int is_valid_state_transition(struct drbd_conf *,
 				     union drbd_state, union drbd_state);
-STATIC union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
+static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
 				       union drbd_state ns, int *warn_sync_abort);
 int drbd_send_state_req(struct drbd_conf *,
 			union drbd_state, union drbd_state);
 
-STATIC enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev,
+static enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev,
 				    union drbd_state mask, union drbd_state val)
 {
 	union drbd_state os, ns;
@@ -565,7 +567,7 @@ STATIC enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev,
  * Should not be called directly, use drbd_request_state() or
  * _drbd_request_state().
  */
-STATIC int drbd_req_state(struct drbd_conf *mdev,
+static int drbd_req_state(struct drbd_conf *mdev,
 			  union drbd_state mask, union drbd_state val,
 			  enum chg_state_flags f)
 {
@@ -658,7 +660,7 @@ int _drbd_request_state(struct drbd_conf *mdev,	union drbd_state mask,
 	return rv;
 }
 
-STATIC void print_st(struct drbd_conf *mdev, char *name, union drbd_state ns)
+static void print_st(struct drbd_conf *mdev, char *name, union drbd_state ns)
 {
 	dev_err(DEV, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c }\n",
 	    name,
@@ -705,7 +707,7 @@ void print_st_err(struct drbd_conf *mdev,
  * @mdev:	DRBD device.
  * @ns:		State to consider.
  */
-STATIC int is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
+static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
 {
 	/* See drbd_state_sw_errors in drbd_strings.c */
 
@@ -740,11 +742,11 @@ STATIC int is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
 	else if (ns.role == R_PRIMARY && ns.disk <= D_INCONSISTENT && ns.pdsk <= D_INCONSISTENT)
 		rv = SS_NO_UP_TO_DATE_DISK;
 
-	else if (ns.conn > C_CONNECTED && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
-		rv = SS_BOTH_INCONSISTENT;
+	else if (ns.conn > C_CONNECTED && ns.disk < D_INCONSISTENT)
+		rv = SS_NO_LOCAL_DISK;
 
-	else if (ns.conn > C_CONNECTED && (ns.disk == D_DISKLESS || ns.pdsk == D_DISKLESS))
-		rv = SS_SYNCING_DISKLESS;
+	else if (ns.conn > C_CONNECTED && ns.pdsk < D_INCONSISTENT)
+		rv = SS_NO_REMOTE_DISK;
 
 	else if ((ns.conn == C_CONNECTED ||
 		  ns.conn == C_WF_BITMAP_S ||
@@ -770,7 +772,7 @@ STATIC int is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
  * @ns:		new state.
  * @os:		old state.
  */
-STATIC int is_valid_state_transition(struct drbd_conf *mdev,
+static int is_valid_state_transition(struct drbd_conf *mdev,
 				     union drbd_state ns, union drbd_state os)
 {
 	int rv = SS_SUCCESS;
@@ -821,7 +823,7 @@ STATIC int is_valid_state_transition(struct drbd_conf *mdev,
  * When we loose connection, we have to set the state of the peers disk (pdsk)
  * to D_UNKNOWN. This rule and many more along those lines are in this function.
  */
-STATIC union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
+static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
 				       union drbd_state ns, int *warn_sync_abort)
 {
 	enum drbd_fencing_p fp;
@@ -948,6 +950,25 @@ STATIC union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
 	return ns;
 }
 
+/* helper for __drbd_set_state */
+static void set_ov_position(struct drbd_conf *mdev, enum drbd_conns cs)
+{
+	if (cs == C_VERIFY_T) {
+		/* starting online verify from an arbitrary position
+		 * does not fit well into the existion protocol.
+		 * on C_VERIFY_T, we initialize ov_left and friends
+		 * implicitly in receive_DataRequest once the
+		 * first P_OV_REQUEST is received */
+		mdev->ov_start_sector = ~(sector_t)0;
+	} else {
+		unsigned long bit = BM_SECT_TO_BIT(mdev->ov_start_sector);
+		if (bit >= mdev->rs_total)
+			mdev->ov_start_sector =
+				BM_BIT_TO_SECT(mdev->rs_total - 1);
+		mdev->ov_position = mdev->ov_start_sector;
+	}
+}
+
 /**
  * __drbd_set_state() - Set a new DRBD state
  * @mdev:	DRBD device.
@@ -1043,6 +1064,15 @@ int __drbd_set_state(struct drbd_conf *mdev,
 		mod_timer(&mdev->resync_timer, jiffies);
 	}
 
+	/* aborted verify run. log the last position */
+	if ((os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) &&
+	    ns.conn < C_CONNECTED) {
+		mdev->ov_start_sector =
+			BM_BIT_TO_SECT(mdev->rs_total - mdev->ov_left);
+		dev_info(DEV, "Online Verify reached sector %llu\n",
+			(unsigned long long)mdev->ov_start_sector);
+	}
+
 	if ((os.conn == C_PAUSED_SYNC_T || os.conn == C_PAUSED_SYNC_S) &&
 	    (ns.conn == C_SYNC_TARGET  || ns.conn == C_SYNC_SOURCE)) {
 		dev_info(DEV, "Syncer continues.\n");
@@ -1068,16 +1098,24 @@ int __drbd_set_state(struct drbd_conf *mdev,
 	if (os.conn == C_CONNECTED &&
 	    (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T)) {
 		mdev->ov_position = 0;
-		mdev->ov_left  =
 		mdev->rs_total =
 		mdev->rs_mark_left = drbd_bm_bits(mdev);
+		if (mdev->agreed_pro_version >= 90)
+			set_ov_position(mdev, ns.conn);
+		else
+			mdev->ov_start_sector = 0;
+		mdev->ov_left = mdev->rs_total
+			      - BM_SECT_TO_BIT(mdev->ov_position);
 		mdev->rs_start     =
 		mdev->rs_mark_time = jiffies;
 		mdev->ov_last_oos_size = 0;
 		mdev->ov_last_oos_start = 0;
 
-		if (ns.conn == C_VERIFY_S)
+		if (ns.conn == C_VERIFY_S) {
+			dev_info(DEV, "Starting Online Verify from sector %llu\n",
+					(unsigned long long)mdev->ov_position);
 			mod_timer(&mdev->resync_timer, jiffies);
+		}
 	}
 
 	if (get_ldev(mdev)) {
@@ -1140,7 +1178,7 @@ int __drbd_set_state(struct drbd_conf *mdev,
 	return rv;
 }
 
-STATIC int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct after_state_chg_work *ascw;
 
@@ -1180,7 +1218,7 @@ static void abw_start_sync(struct drbd_conf *mdev, int rv)
  * @ns:		new state.
  * @flags:	Flags
  */
-STATIC void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
+static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 			   union drbd_state ns, enum chg_state_flags flags)
 {
 	enum drbd_fencing_p fp;
@@ -1260,7 +1298,7 @@ STATIC void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 	    os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
 		kfree(mdev->p_uuid); /* We expect to receive up-to-date UUIDs soon. */
 		mdev->p_uuid = NULL; /* ...to not use the old ones in the mean time */
-		drbd_send_sizes(mdev);  /* to start sync... */
+		drbd_send_sizes(mdev, 0);  /* to start sync... */
 		drbd_send_uuids(mdev);
 		drbd_send_state(mdev);
 	}
@@ -1327,7 +1365,7 @@ STATIC void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 	    (os.user_isp && !ns.user_isp))
 		resume_next_sg(mdev);
 
-	/* Upon network connection, we need to start the received */
+	/* Upon network connection, we need to start the receiver */
 	if (os.conn == C_STANDALONE && ns.conn == C_UNCONNECTED)
 		drbd_thread_start(&mdev->receiver);
 
@@ -1347,7 +1385,7 @@ STATIC void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
 }
 
 
-STATIC int drbd_thread_setup(void *arg)
+static int drbd_thread_setup(void *arg)
 {
 	struct drbd_thread *thi = (struct drbd_thread *) arg;
 	struct drbd_conf *mdev = thi->mdev;
@@ -1389,7 +1427,7 @@ restart:
 	return retval;
 }
 
-STATIC void drbd_thread_init(struct drbd_conf *mdev, struct drbd_thread *thi,
+static void drbd_thread_init(struct drbd_conf *mdev, struct drbd_thread *thi,
 		      int (*func) (struct drbd_thread *))
 {
 	spin_lock_init(&thi->t_lock);
@@ -1466,6 +1504,7 @@ int drbd_thread_start(struct drbd_thread *thi)
 void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait)
 {
 	unsigned long flags;
+
 	enum drbd_thread_state ns = restart ? Restarting : Exiting;
 
 	/* may be called from state engine, holding the req lock irqsave */
@@ -1685,7 +1724,9 @@ int drbd_send_protocol(struct drbd_conf *mdev)
 	if (mdev->agreed_pro_version >= 87)
 		size += strlen(mdev->net_conf->integrity_alg) + 1;
 
-	p = kmalloc(size, GFP_KERNEL);
+	/* we must not recurse into our own queue,
+	 * as that is blocked during handshake */
+	p = kmalloc(size, GFP_NOIO);
 	if (p == NULL)
 		return 0;
 
@@ -1750,7 +1791,7 @@ int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val)
 			     (struct p_header *)&p, sizeof(p));
 }
 
-int drbd_send_sizes(struct drbd_conf *mdev)
+int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply)
 {
 	struct p_sizes p;
 	sector_t d_size, u_size;
@@ -1772,8 +1813,8 @@ int drbd_send_sizes(struct drbd_conf *mdev)
 
 	p.d_size = cpu_to_be64(d_size);
 	p.u_size = cpu_to_be64(u_size);
-	p.c_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
-	p.max_segment_size = cpu_to_be32(mdev->rq_queue->max_segment_size);
+	p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
+	p.max_segment_size = cpu_to_be32(queue_max_segment_size(mdev->rq_queue));
 	p.queue_order_type = cpu_to_be32(q_order_type);
 
 	ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SIZES,
@@ -1846,7 +1887,7 @@ int fill_bitmap_rle_bits(struct drbd_conf *mdev,
 	int bits;
 
 	/* may we use this feature? */
-	if ((mdev->sync_conf.use_rle_encoding == 0) ||
+	if ((mdev->sync_conf.use_rle == 0) ||
 		(mdev->agreed_pro_version < 90))
 			return 0;
 
@@ -2057,7 +2098,7 @@ int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
  * @blksize:	size in byte, needs to be in big endian byte order
  * @block_id:	Id, big endian byte order
  */
-STATIC int _drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd,
+static int _drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd,
 			  u64 sector,
 			  u32 blksize,
 			  u64 block_id)
@@ -2179,7 +2220,7 @@ int drbd_send_ov_request(struct drbd_conf *mdev, sector_t sector, int size)
  * returns FALSE if we should retry,
  * TRUE if we think connection is dead
  */
-STATIC int we_should_drop_the_connection(struct drbd_conf *mdev, struct socket *sock)
+static int we_should_drop_the_connection(struct drbd_conf *mdev, struct socket *sock)
 {
 	int drop_it;
 	/* long elapsed = (long)(jiffies - mdev->last_received); */
@@ -2223,7 +2264,7 @@ STATIC int we_should_drop_the_connection(struct drbd_conf *mdev, struct socket *
  * As a workaround, we disable sendpage on pages
  * with page_count == 0 or PageSlab.
  */
-STATIC int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page,
+static int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page,
 		   int offset, size_t size)
 {
 	int sent = drbd_send(mdev, mdev->data.socket, kmap(page) + offset, size, 0);
@@ -2233,7 +2274,7 @@ STATIC int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page,
 	return sent == size;
 }
 
-int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
+static int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
 		    int offset, size_t size)
 {
 	mm_segment_t oldfs = get_fs();
@@ -2527,7 +2568,7 @@ static int drbd_release(struct gendisk *gd, fmode_t mode)
 	return 0;
 }
 
-STATIC void drbd_unplug_fn(struct request_queue *q)
+static void drbd_unplug_fn(struct request_queue *q)
 {
 	struct drbd_conf *mdev = q->queuedata;
 
@@ -2558,7 +2599,7 @@ STATIC void drbd_unplug_fn(struct request_queue *q)
 		drbd_kick_lo(mdev);
 }
 
-STATIC void drbd_set_defaults(struct drbd_conf *mdev)
+static void drbd_set_defaults(struct drbd_conf *mdev)
 {
 	mdev->sync_conf.after      = DRBD_AFTER_DEF;
 	mdev->sync_conf.rate       = DRBD_RATE_DEF;
@@ -2697,7 +2738,7 @@ void drbd_mdev_cleanup(struct drbd_conf *mdev)
 }
 
 
-STATIC void drbd_destroy_mempools(void)
+static void drbd_destroy_mempools(void)
 {
 	struct page *page;
 
@@ -2718,16 +2759,22 @@ STATIC void drbd_destroy_mempools(void)
 		kmem_cache_destroy(drbd_ee_cache);
 	if (drbd_request_cache)
 		kmem_cache_destroy(drbd_request_cache);
+	if (drbd_bm_ext_cache)
+		kmem_cache_destroy(drbd_bm_ext_cache);
+	if (drbd_al_ext_cache)
+		kmem_cache_destroy(drbd_al_ext_cache);
 
 	drbd_ee_mempool      = NULL;
 	drbd_request_mempool = NULL;
 	drbd_ee_cache        = NULL;
 	drbd_request_cache   = NULL;
+	drbd_bm_ext_cache    = NULL;
+	drbd_al_ext_cache    = NULL;
 
 	return;
 }
 
-STATIC int drbd_create_mempools(void)
+static int drbd_create_mempools(void)
 {
 	struct page *page;
 	const int number = (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE) * minor_count;
@@ -2737,19 +2784,31 @@ STATIC int drbd_create_mempools(void)
 	drbd_request_mempool = NULL;
 	drbd_ee_cache        = NULL;
 	drbd_request_cache   = NULL;
+	drbd_bm_ext_cache    = NULL;
+	drbd_al_ext_cache    = NULL;
 	drbd_pp_pool         = NULL;
 
 	/* caches */
 	drbd_request_cache = kmem_cache_create(
-		"drbd_req_cache", sizeof(struct drbd_request), 0, 0, NULL);
+		"drbd_req", sizeof(struct drbd_request), 0, 0, NULL);
 	if (drbd_request_cache == NULL)
 		goto Enomem;
 
 	drbd_ee_cache = kmem_cache_create(
-		"drbd_ee_cache", sizeof(struct drbd_epoch_entry), 0, 0, NULL);
+		"drbd_ee", sizeof(struct drbd_epoch_entry), 0, 0, NULL);
 	if (drbd_ee_cache == NULL)
 		goto Enomem;
 
+	drbd_bm_ext_cache = kmem_cache_create(
+		"drbd_bm", sizeof(struct bm_extent), 0, 0, NULL);
+	if (drbd_bm_ext_cache == NULL)
+		goto Enomem;
+
+	drbd_al_ext_cache = kmem_cache_create(
+		"drbd_al", sizeof(struct lc_element), 0, 0, NULL);
+	if (drbd_al_ext_cache == NULL)
+		goto Enomem;
+
 	/* mempools */
 	drbd_request_mempool = mempool_create(number,
 		mempool_alloc_slab, mempool_free_slab, drbd_request_cache);
@@ -2780,7 +2839,7 @@ Enomem:
 	return -ENOMEM;
 }
 
-STATIC int drbd_notify_sys(struct notifier_block *this, unsigned long code,
+static int drbd_notify_sys(struct notifier_block *this, unsigned long code,
 	void *unused)
 {
 	/* just so we have it.  you never know what interessting things we
@@ -2790,7 +2849,7 @@ STATIC int drbd_notify_sys(struct notifier_block *this, unsigned long code,
 	return NOTIFY_DONE;
 }
 
-STATIC struct notifier_block drbd_notifier = {
+static struct notifier_block drbd_notifier = {
 	.notifier_call = drbd_notify_sys,
 };
 
@@ -2836,7 +2895,7 @@ static void drbd_delete_device(unsigned int minor)
 	ERR_IF (!list_empty(&mdev->data.work.q)) {
 		struct list_head *lp;
 		list_for_each(lp, &mdev->data.work.q) {
-			DUMPP(lp);
+			dev_err(DEV, "lp = %p\n", lp);
 		}
 	};
 	/* end paranoia asserts */
@@ -2876,7 +2935,7 @@ static void drbd_delete_device(unsigned int minor)
 	drbd_free_mdev(mdev);
 }
 
-STATIC void drbd_cleanup(void)
+static void drbd_cleanup(void)
 {
 	unsigned int i;
 
@@ -2958,7 +3017,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
 		goto out_no_q;
 	mdev->rq_queue = q;
 	q->queuedata   = mdev;
-	q->max_segment_size = DRBD_MAX_SEGMENT_SIZE;
+	blk_queue_max_segment_size(q, DRBD_MAX_SEGMENT_SIZE);
 
 	disk = alloc_disk(1);
 	if (!disk)
@@ -2994,7 +3053,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
 
 	if (drbd_bm_init(mdev))
 		goto out_no_bitmap;
-	/* no need to lock access, we are still initializing the module. */
+	/* no need to lock access, we are still initializing this minor device. */
 	if (!tl_init(mdev))
 		goto out_no_tl;
 
@@ -3143,10 +3202,12 @@ void drbd_free_bc(struct drbd_backing_dev *ldev)
 void drbd_free_sock(struct drbd_conf *mdev)
 {
 	if (mdev->data.socket) {
+		kernel_sock_shutdown(mdev->data.socket, SHUT_RDWR);
 		sock_release(mdev->data.socket);
 		mdev->data.socket = NULL;
 	}
 	if (mdev->meta.socket) {
+		kernel_sock_shutdown(mdev->meta.socket, SHUT_RDWR);
 		sock_release(mdev->meta.socket);
 		mdev->meta.socket = NULL;
 	}
@@ -3344,7 +3405,7 @@ void drbd_md_mark_dirty(struct drbd_conf *mdev)
 }
 
 
-STATIC void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local)
+static void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local)
 {
 	int i;
 
@@ -3472,7 +3533,7 @@ int drbd_bmio_clear_n_write(struct drbd_conf *mdev)
 	return rv;
 }
 
-STATIC int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct bm_io_work *work = (struct bm_io_work *)w;
 	int rv;
@@ -3581,14 +3642,14 @@ int drbd_md_test_flag(struct drbd_backing_dev *bdev, int flag)
 	return (bdev->md.flags & flag) != 0;
 }
 
-STATIC void md_sync_timer_fn(unsigned long data)
+static void md_sync_timer_fn(unsigned long data)
 {
 	struct drbd_conf *mdev = (struct drbd_conf *) data;
 
 	drbd_queue_work_front(&mdev->data.work, &mdev->md_sync_work);
 }
 
-STATIC int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	dev_warn(DEV, "md_sync_timer expired! Worker calls drbd_md_sync().\n");
 	drbd_md_sync(mdev);
@@ -3596,7 +3657,7 @@ STATIC int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 	return 1;
 }
 
-#ifdef DRBD_ENABLE_FAULTS
+#ifdef CONFIG_DRBD_FAULT_INJECTION
 /* Fault insertion support including random number generator shamelessly
  * stolen from kernel/rcutorture.c */
 struct fault_random_state {
@@ -3612,7 +3673,7 @@ struct fault_random_state {
  * Crude but fast random-number generator.  Uses a linear congruential
  * generator, with occasional help from get_random_bytes().
  */
-STATIC unsigned long
+static unsigned long
 _drbd_fault_random(struct fault_random_state *rsp)
 {
 	long refresh;
@@ -3626,18 +3687,18 @@ _drbd_fault_random(struct fault_random_state *rsp)
 	return swahw32(rsp->state);
 }
 
-STATIC char *
+static char *
 _drbd_fault_str(unsigned int type) {
 	static char *_faults[] = {
-		"Meta-data write",
-		"Meta-data read",
-		"Resync write",
-		"Resync read",
-		"Data write",
-		"Data read",
-		"Data read ahead",
-		"BM allocation",
-		"EE allocation"
+		[DRBD_FAULT_MD_WR] = "Meta-data write",
+		[DRBD_FAULT_MD_RD] = "Meta-data read",
+		[DRBD_FAULT_RS_WR] = "Resync write",
+		[DRBD_FAULT_RS_RD] = "Resync read",
+		[DRBD_FAULT_DT_WR] = "Data write",
+		[DRBD_FAULT_DT_RD] = "Data read",
+		[DRBD_FAULT_DT_RA] = "Data read ahead",
+		[DRBD_FAULT_BM_ALLOC] = "BM allocation",
+		[DRBD_FAULT_AL_EE] = "EE allocation"
 	};
 
 	return (type < DRBD_FAULT_MAX) ? _faults[type] : "**Unknown**";
@@ -3665,5 +3726,22 @@ _drbd_insert_fault(struct drbd_conf *mdev, unsigned int type)
 }
 #endif
 
+const char *drbd_buildtag(void)
+{
+	/* DRBD built from external sources has here a reference to the
+	   git hash of the source code. */
+
+	static char buildtag[38] = "\0uilt-in";
+
+	if (buildtag[0] == 0) {
+		if (THIS_MODULE != NULL)
+			sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
+		else
+			buildtag[0] = 'b';
+	}
+
+	return buildtag;
+}
+
 module_init(drbd_init)
 module_exit(drbd_cleanup)
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index c6217d6a246..c3d438ccd40 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -25,34 +25,40 @@
 
 #include <linux/autoconf.h>
 #include <linux/module.h>
+#include <linux/drbd.h>
 #include <linux/in.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/slab.h>
 #include <linux/connector.h>
-#include <linux/drbd_config.h>
-#include <linux/drbd.h>
 #include <linux/blkpg.h>
 #include <linux/cpumask.h>
 #include "drbd_int.h"
 #include "drbd_tracing.h"
 #include "drbd_wrappers.h"
+#include <asm/unaligned.h>
 #include <linux/drbd_tag_magic.h>
 #include <linux/drbd_limits.h>
 
+static unsigned short *tl_add_blob(unsigned short *, enum drbd_tags, const void *, int);
+static unsigned short *tl_add_str(unsigned short *, enum drbd_tags, const char *);
+static unsigned short *tl_add_int(unsigned short *, enum drbd_tags, const void *);
+
 /* see get_sb_bdev and bd_claim */
 static char *drbd_m_holder = "Hands off! this is DRBD's meta data device.";
 
 /* Generate the tag_list to struct functions */
 #define NL_PACKET(name, number, fields) \
-STATIC int name ## _from_tags(struct drbd_conf *mdev, \
+static int name ## _from_tags(struct drbd_conf *mdev, \
+	unsigned short *tags, struct name *arg) __attribute__ ((unused)); \
+static int name ## _from_tags(struct drbd_conf *mdev, \
 	unsigned short *tags, struct name *arg) \
 { \
 	int tag; \
 	int dlen; \
 	\
-	while ((tag = *tags++) != TT_END) { \
-		dlen = *tags++; \
+	while ((tag = get_unaligned(tags++)) != TT_END) {	\
+		dlen = get_unaligned(tags++);			\
 		switch (tag_number(tag)) { \
 		fields \
 		default: \
@@ -67,16 +73,16 @@ STATIC int name ## _from_tags(struct drbd_conf *mdev, \
 }
 #define NL_INTEGER(pn, pr, member) \
 	case pn: /* D_ASSERT( tag_type(tag) == TT_INTEGER ); */ \
-		 arg->member = *(int *)(tags); \
-		 break;
+		arg->member = get_unaligned((int *)(tags));	\
+		break;
 #define NL_INT64(pn, pr, member) \
 	case pn: /* D_ASSERT( tag_type(tag) == TT_INT64 ); */ \
-		 arg->member = *(u64 *)(tags); \
-		 break;
+		arg->member = get_unaligned((u64 *)(tags));	\
+		break;
 #define NL_BIT(pn, pr, member) \
 	case pn: /* D_ASSERT( tag_type(tag) == TT_BIT ); */ \
-		 arg->member = *(char *)(tags) ? 1 : 0; \
-		 break;
+		arg->member = *(char *)(tags) ? 1 : 0; \
+		break;
 #define NL_STRING(pn, pr, member, len) \
 	case pn: /* D_ASSERT( tag_type(tag) == TT_STRING ); */ \
 		if (dlen > len) { \
@@ -91,7 +97,10 @@ STATIC int name ## _from_tags(struct drbd_conf *mdev, \
 
 /* Generate the struct to tag_list functions */
 #define NL_PACKET(name, number, fields) \
-STATIC unsigned short* \
+static unsigned short* \
+name ## _to_tags(struct drbd_conf *mdev, \
+	struct name *arg, unsigned short *tags) __attribute__ ((unused)); \
+static unsigned short* \
 name ## _to_tags(struct drbd_conf *mdev, \
 	struct name *arg, unsigned short *tags) \
 { \
@@ -100,23 +109,23 @@ name ## _to_tags(struct drbd_conf *mdev, \
 }
 
 #define NL_INTEGER(pn, pr, member) \
-	*tags++ = pn | pr | TT_INTEGER; \
-	*tags++ = sizeof(int); \
-	*(int *)tags = arg->member; \
+	put_unaligned(pn | pr | TT_INTEGER, tags++);	\
+	put_unaligned(sizeof(int), tags++);		\
+	put_unaligned(arg->member, (int *)tags);	\
 	tags = (unsigned short *)((char *)tags+sizeof(int));
 #define NL_INT64(pn, pr, member) \
-	*tags++ = pn | pr | TT_INT64; \
-	*tags++ = sizeof(u64); \
-	*(u64 *)tags = arg->member; \
+	put_unaligned(pn | pr | TT_INT64, tags++);	\
+	put_unaligned(sizeof(u64), tags++);		\
+	put_unaligned(arg->member, (u64 *)tags);	\
 	tags = (unsigned short *)((char *)tags+sizeof(u64));
 #define NL_BIT(pn, pr, member) \
-	*tags++ = pn | pr | TT_BIT; \
-	*tags++ = sizeof(char); \
+	put_unaligned(pn | pr | TT_BIT, tags++);	\
+	put_unaligned(sizeof(char), tags++);		\
 	*(char *)tags = arg->member; \
 	tags = (unsigned short *)((char *)tags+sizeof(char));
 #define NL_STRING(pn, pr, member, len) \
-	*tags++ = pn | pr | TT_STRING; \
-	*tags++ = arg->member ## _len; \
+	put_unaligned(pn | pr | TT_STRING, tags++);	\
+	put_unaligned(arg->member ## _len, tags++);	\
 	memcpy(tags, arg->member, arg->member ## _len); \
 	tags = (unsigned short *)((char *)tags + arg->member ## _len);
 #include "linux/drbd_nl.h"
@@ -126,16 +135,42 @@ void drbd_nl_send_reply(struct cn_msg *, int);
 
 int drbd_khelper(struct drbd_conf *mdev, char *cmd)
 {
-	char mb[12];
+	char *envp[] = { "HOME=/",
+			"TERM=linux",
+			"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+			NULL, /* Will be set to address family */
+			NULL, /* Will be set to address */
+			NULL };
+
+	char mb[12], af[20], ad[60], *afs;
 	char *argv[] = {usermode_helper, cmd, mb, NULL };
 	int ret;
-	static char *envp[] = { "HOME=/",
-				"TERM=linux",
-				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
-				NULL };
 
 	snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
 
+	if (get_net_conf(mdev)) {
+		switch (((struct sockaddr *)mdev->net_conf->peer_addr)->sa_family) {
+		case AF_INET6:
+			afs = "ipv6";
+			snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI6",
+				 &((struct sockaddr_in6 *)mdev->net_conf->peer_addr)->sin6_addr);
+			break;
+		case AF_INET:
+			afs = "ipv4";
+			snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
+				 &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr);
+			break;
+		default:
+			afs = "ssocks";
+			snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
+				 &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr);
+		}
+		snprintf(af, 20, "DRBD_PEER_AF=%s", afs);
+		envp[3]=af;
+		envp[4]=ad;
+		put_net_conf(mdev);
+	}
+
 	dev_info(DEV, "helper command: %s %s %s\n", usermode_helper, cmd, mb);
 
 	drbd_bcast_ev_helper(mdev, cmd);
@@ -354,7 +389,7 @@ int drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
 }
 
 
-STATIC int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			   struct drbd_nl_cfg_reply *reply)
 {
 	struct primary primary_args;
@@ -371,7 +406,7 @@ STATIC int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 	return 0;
 }
 
-STATIC int drbd_nl_secondary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_secondary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			     struct drbd_nl_cfg_reply *reply)
 {
 	reply->ret_code = drbd_set_role(mdev, R_SECONDARY, 0);
@@ -381,7 +416,7 @@ STATIC int drbd_nl_secondary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 
 /* initializes the md.*_offset members, so we are able to find
  * the on disk meta data */
-STATIC void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
+static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
 				       struct drbd_backing_dev *bdev)
 {
 	sector_t md_size_sect = 0;
@@ -533,15 +568,12 @@ enum determine_dev_size drbd_determin_dev_size(struct drbd_conf *mdev) __must_ho
 	md_moved = prev_first_sect != drbd_md_first_sector(mdev->ldev)
 		|| prev_size	   != mdev->ldev->md.md_size_sect;
 
-	if (md_moved) {
-		dev_warn(DEV, "Moving meta-data.\n");
-		/* assert: (flexible) internal meta data */
-	}
-
 	if (la_size_changed || md_moved) {
 		drbd_al_shrink(mdev); /* All extents inactive. */
-		dev_info(DEV, "Writing the whole bitmap, size changed\n");
-		rv = drbd_bitmap_io(mdev, &drbd_bm_write, "size changed");
+		dev_info(DEV, "Writing the whole bitmap, %s\n",
+			 la_size_changed && md_moved ? "size changed and md moved" :
+			 la_size_changed ? "size changed" : "md moved");
+		rv = drbd_bitmap_io(mdev, &drbd_bm_write, "size changed"); /* does drbd_resume_io() ! */
 		drbd_md_mark_dirty(mdev);
 	}
 
@@ -607,7 +639,7 @@ drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
  * failed, and 0 on success. You should call drbd_md_sync() after you called
  * this function.
  */
-STATIC int drbd_check_al_size(struct drbd_conf *mdev)
+static int drbd_check_al_size(struct drbd_conf *mdev)
 {
 	struct lru_cache *n, *t;
 	struct lc_element *e;
@@ -623,8 +655,8 @@ STATIC int drbd_check_al_size(struct drbd_conf *mdev)
 
 	in_use = 0;
 	t = mdev->act_log;
-	n = lc_create("act_log", mdev->sync_conf.al_extents,
-		     sizeof(struct lc_element), 0);
+	n = lc_create("act_log", drbd_al_ext_cache,
+		mdev->sync_conf.al_extents, sizeof(struct lc_element), 0);
 
 	if (n == NULL) {
 		dev_err(DEV, "Cannot allocate act_log lru!\n");
@@ -659,31 +691,25 @@ void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_seg_s) __mu
 {
 	struct request_queue * const q = mdev->rq_queue;
 	struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue;
-	/* unsigned int old_max_seg_s = q->max_segment_size; */
 	int max_segments = mdev->ldev->dc.max_bio_bvecs;
 
 	if (b->merge_bvec_fn && !mdev->ldev->dc.use_bmbv)
 		max_seg_s = PAGE_SIZE;
 
-	max_seg_s = min(b->max_sectors * b->hardsect_size, max_seg_s);
+	max_seg_s = min(queue_max_sectors(b) * queue_logical_block_size(b), max_seg_s);
 
-	q->max_sectors	     = max_seg_s >> 9;
-	if (max_segments) {
-		q->max_phys_segments = max_segments;
-		q->max_hw_segments   = max_segments;
-	} else {
-		q->max_phys_segments = MAX_PHYS_SEGMENTS;
-		q->max_hw_segments   = MAX_HW_SEGMENTS;
-	}
-	q->max_segment_size  = max_seg_s;
-	q->hardsect_size     = 512;
-	q->seg_boundary_mask = PAGE_SIZE-1;
+	blk_queue_max_sectors(q, max_seg_s >> 9);
+	blk_queue_max_phys_segments(q, max_segments ? max_segments : MAX_PHYS_SEGMENTS);
+	blk_queue_max_hw_segments(q, max_segments ? max_segments : MAX_HW_SEGMENTS);
+	blk_queue_max_segment_size(q, max_seg_s);
+	blk_queue_logical_block_size(q, 512);
+	blk_queue_segment_boundary(q, PAGE_SIZE-1);
 	blk_queue_stack_limits(q, b);
 
 	if (b->merge_bvec_fn)
 		dev_warn(DEV, "Backing device's merge_bvec_fn() = %p\n",
 		     b->merge_bvec_fn);
-	dev_info(DEV, "max_segment_size ( = BIO size ) = %u\n", q->max_segment_size);
+	dev_info(DEV, "max_segment_size ( = BIO size ) = %u\n", queue_max_segment_size(q));
 
 	if (q->backing_dev_info.ra_pages != b->backing_dev_info.ra_pages) {
 		dev_info(DEV, "Adjusting my ra_pages to backing device's (%lu -> %lu)\n",
@@ -725,7 +751,7 @@ static void drbd_reconfig_done(struct drbd_conf *mdev)
 
 /* does always return 0;
  * interesting return code is in reply->ret_code */
-STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			     struct drbd_nl_cfg_reply *reply)
 {
 	enum drbd_ret_codes retcode;
@@ -738,7 +764,7 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 	union drbd_state ns, os;
 	int rv;
 	int cp_discovered = 0;
-	int hardsect_size;
+	int logical_block_size;
 
 	drbd_reconfig_start(mdev);
 
@@ -748,14 +774,13 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 		goto fail;
 	}
 
-	nbc = kmalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
+	/* allocation not in the IO path, cqueue thread context */
+	nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
 	if (!nbc) {
 		retcode = ERR_NOMEM;
 		goto fail;
 	}
 
-	memset(&nbc->md, 0, sizeof(struct drbd_md));
-	memset(&nbc->dc, 0, sizeof(struct disk_conf));
 	nbc->dc.disk_size     = DRBD_DISK_SIZE_SECT_DEF;
 	nbc->dc.on_io_error   = DRBD_ON_IO_ERROR_DEF;
 	nbc->dc.fencing       = DRBD_FENCING_DEF;
@@ -766,9 +791,6 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 		goto fail;
 	}
 
-	nbc->lo_file = NULL;
-	nbc->md_file = NULL;
-
 	if (nbc->dc.meta_dev_idx < DRBD_MD_INDEX_FLEX_INT) {
 		retcode = ERR_MD_IDX_INVALID;
 		goto fail;
@@ -817,18 +839,24 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 		goto fail;
 	}
 
-	resync_lru = lc_create("resync", 61, sizeof(struct bm_extent),
+	resync_lru = lc_create("resync", drbd_bm_ext_cache,
+			61, sizeof(struct bm_extent),
 			offsetof(struct bm_extent, lce));
 	if (!resync_lru) {
 		retcode = ERR_NOMEM;
 		goto release_bdev_fail;
 	}
 
+	/* meta_dev_idx >= 0: external fixed size,
+	 * possibly multiple drbd sharing one meta device.
+	 * TODO in that case, paranoia check that [md_bdev, meta_dev_idx] is
+	 * not yet used by some other drbd minor!
+	 * (if you use drbd.conf + drbdadm,
+	 * that should check it for you already; but if you don't, or someone
+	 * fooled it, we need to double check here) */
 	nbc->md_bdev = inode2->i_bdev;
-	if (bd_claim(nbc->md_bdev,
-		     (nbc->dc.meta_dev_idx == DRBD_MD_INDEX_INTERNAL ||
-		      nbc->dc.meta_dev_idx == DRBD_MD_INDEX_FLEX_INT) ?
-		     (void *)mdev : (void *) drbd_m_holder)) {
+	if (bd_claim(nbc->md_bdev, (nbc->dc.meta_dev_idx < 0) ? (void *)mdev
+				: (void *) drbd_m_holder)) {
 		retcode = ERR_BDCLAIM_MD_DISK;
 		goto release_bdev_fail;
 	}
@@ -936,19 +964,19 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 		goto force_diskless_dec;
 	}
 
-	/* allocate a second IO page if hardsect_size != 512 */
-	hardsect_size = drbd_get_hardsect_size(nbc->md_bdev);
-	if (hardsect_size == 0)
-		hardsect_size = MD_SECTOR_SIZE;
+	/* allocate a second IO page if logical_block_size != 512 */
+	logical_block_size = bdev_logical_block_size(nbc->md_bdev);
+	if (logical_block_size == 0)
+		logical_block_size = MD_SECTOR_SIZE;
 
-	if (hardsect_size != MD_SECTOR_SIZE) {
+	if (logical_block_size != MD_SECTOR_SIZE) {
 		if (!mdev->md_io_tmpp) {
 			struct page *page = alloc_page(GFP_NOIO);
 			if (!page)
 				goto force_diskless_dec;
 
-			dev_warn(DEV, "Meta data's bdev hardsect_size = %d != %d\n",
-			     hardsect_size, MD_SECTOR_SIZE);
+			dev_warn(DEV, "Meta data's bdev logical_block_size = %d != %d\n",
+			     logical_block_size, MD_SECTOR_SIZE);
 			dev_warn(DEV, "Workaround engaged (has performace impact).\n");
 
 			mdev->md_io_tmpp = page;
@@ -1122,14 +1150,14 @@ STATIC int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 	return 0;
 }
 
-STATIC int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			  struct drbd_nl_cfg_reply *reply)
 {
 	reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS));
 	return 0;
 }
 
-STATIC int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			    struct drbd_nl_cfg_reply *reply)
 {
 	int i, ns;
@@ -1154,6 +1182,7 @@ STATIC int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 		goto fail;
 	}
 
+	/* allocation not in the IO path, cqueue thread context */
 	new_conf = kmalloc(sizeof(struct net_conf), GFP_KERNEL);
 	if (!new_conf) {
 		retcode = ERR_NOMEM;
@@ -1168,6 +1197,7 @@ STATIC int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 	new_conf->max_buffers	   = DRBD_MAX_BUFFERS_DEF;
 	new_conf->unplug_watermark = DRBD_UNPLUG_WATERMARK_DEF;
 	new_conf->sndbuf_size	   = DRBD_SNDBUF_SIZE_DEF;
+	new_conf->rcvbuf_size	   = DRBD_RCVBUF_SIZE_DEF;
 	new_conf->ko_count	   = DRBD_KO_COUNT_DEF;
 	new_conf->after_sb_0p	   = DRBD_AFTER_SB_0P_DEF;
 	new_conf->after_sb_1p	   = DRBD_AFTER_SB_1P_DEF;
@@ -1184,7 +1214,7 @@ STATIC int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 	}
 
 	if (new_conf->two_primaries
-	&& (new_conf->wire_protocol != DRBD_PROT_C)) {
+	    && (new_conf->wire_protocol != DRBD_PROT_C)) {
 		retcode = ERR_NOT_PROTO_C;
 		goto fail;
 	};
@@ -1366,7 +1396,7 @@ fail:
 	return 0;
 }
 
-STATIC int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			      struct drbd_nl_cfg_reply *reply)
 {
 	int retcode;
@@ -1427,7 +1457,7 @@ void resync_after_online_grow(struct drbd_conf *mdev)
 		_drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE + CS_SERIALIZE);
 }
 
-STATIC int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			  struct drbd_nl_cfg_reply *reply)
 {
 	struct resize rs;
@@ -1472,10 +1502,11 @@ STATIC int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 	}
 
 	if (mdev->state.conn == C_CONNECTED && (dd != unchanged || ldsc)) {
-		drbd_send_uuids(mdev);
-		drbd_send_sizes(mdev);
 		if (dd == grew)
-			resync_after_online_grow(mdev);
+			set_bit(RESIZE_PENDING, &mdev->flags);
+
+		drbd_send_uuids(mdev);
+		drbd_send_sizes(mdev, 1);
 	}
 
  fail:
@@ -1483,14 +1514,13 @@ STATIC int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 	return 0;
 }
 
-STATIC int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			       struct drbd_nl_cfg_reply *reply)
 {
 	int retcode = NO_ERROR;
 	int err;
 	int ovr; /* online verify running */
 	int rsr; /* re-sync running */
-	struct drbd_conf *odev;
 	struct crypto_hash *verify_tfm = NULL;
 	struct crypto_hash *csums_tfm = NULL;
 	struct syncer_conf sc;
@@ -1510,23 +1540,6 @@ STATIC int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
 		goto fail;
 	}
 
-	if (sc.after != -1) {
-		if (sc.after < -1 || minor_to_mdev(sc.after) == NULL) {
-			retcode = ERR_SYNC_AFTER;
-			goto fail;
-		}
-		odev = minor_to_mdev(sc.after); /* check against loops in */
-		while (1) {
-			if (odev == mdev) {
-				retcode = ERR_SYNC_AFTER_CYCLE;
-				goto fail;
-			}
-			if (odev->sync_conf.after == -1)
-				break; /* no cycles. */
-			odev = minor_to_mdev(odev->sync_conf.after);
-		}
-	}
-
 	/* re-sync running */
 	rsr = (	mdev->state.conn == C_SYNC_SOURCE ||
 		mdev->state.conn == C_SYNC_TARGET ||
@@ -1576,7 +1589,8 @@ STATIC int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
 		}
 	}
 
-	if (sc.cpu_mask[0] != 0) {
+	/* silently ignore cpu mask on UP kernel */
+	if (NR_CPUS > 1 && sc.cpu_mask[0] != 0) {
 		err = __bitmap_parse(sc.cpu_mask, 32, 0, (unsigned long *)&n_cpu_mask, NR_CPUS);
 		if (err) {
 			dev_warn(DEV, "__bitmap_parse() failed with %d\n", err);
@@ -1594,8 +1608,16 @@ STATIC int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
 	}
 #undef AL_MAX
 
+	/* most sanity checks done, try to assign the new sync-after
+	 * dependency.  need to hold the global lock in there,
+	 * to avoid a race in the dependency loop check. */
+	retcode = drbd_alter_sa(mdev, sc.after);
+	if (retcode != NO_ERROR)
+		goto fail;
+
+	/* ok, assign the rest of it as well.
+	 * lock against receive_SyncParam() */
 	spin_lock(&mdev->peer_seq_lock);
-	/* lock against receive_SyncParam() */
 	mdev->sync_conf = sc;
 
 	if (!rsr) {
@@ -1630,8 +1652,6 @@ STATIC int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
 	if (mdev->state.conn >= C_CONNECTED)
 		drbd_send_sync_param(mdev, &sc);
 
-	drbd_alter_sa(mdev, sc.after);
-
 	if (!cpus_equal(mdev->cpu_mask, n_cpu_mask)) {
 		mdev->cpu_mask = n_cpu_mask;
 		mdev->cpu_mask = drbd_calc_cpu_mask(mdev);
@@ -1648,7 +1668,7 @@ fail:
 	return 0;
 }
 
-STATIC int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			      struct drbd_nl_cfg_reply *reply)
 {
 	int retcode;
@@ -1674,7 +1694,7 @@ STATIC int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 	return 0;
 }
 
-STATIC int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 				   struct drbd_nl_cfg_reply *reply)
 {
 
@@ -1683,7 +1703,7 @@ STATIC int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
 	return 0;
 }
 
-STATIC int drbd_nl_pause_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_pause_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			      struct drbd_nl_cfg_reply *reply)
 {
 	int retcode = NO_ERROR;
@@ -1695,7 +1715,7 @@ STATIC int drbd_nl_pause_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 	return 0;
 }
 
-STATIC int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			       struct drbd_nl_cfg_reply *reply)
 {
 	int retcode = NO_ERROR;
@@ -1707,7 +1727,7 @@ STATIC int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n
 	return 0;
 }
 
-STATIC int drbd_nl_suspend_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_suspend_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			      struct drbd_nl_cfg_reply *reply)
 {
 	reply->ret_code = drbd_request_state(mdev, NS(susp, 1));
@@ -1715,21 +1735,21 @@ STATIC int drbd_nl_suspend_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 	return 0;
 }
 
-STATIC int drbd_nl_resume_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_resume_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			     struct drbd_nl_cfg_reply *reply)
 {
 	reply->ret_code = drbd_request_state(mdev, NS(susp, 0));
 	return 0;
 }
 
-STATIC int drbd_nl_outdate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_outdate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			   struct drbd_nl_cfg_reply *reply)
 {
 	reply->ret_code = drbd_request_state(mdev, NS(disk, D_OUTDATED));
 	return 0;
 }
 
-STATIC int drbd_nl_get_config(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_get_config(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			   struct drbd_nl_cfg_reply *reply)
 {
 	unsigned short *tl;
@@ -1747,12 +1767,12 @@ STATIC int drbd_nl_get_config(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 	}
 	tl = syncer_conf_to_tags(mdev, &mdev->sync_conf, tl);
 
-	*tl++ = TT_END; /* Close the tag list */
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	return (int)((char *)tl - (char *)reply->tag_list);
 }
 
-STATIC int drbd_nl_get_state(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_get_state(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			     struct drbd_nl_cfg_reply *reply)
 {
 	unsigned short *tl = reply->tag_list;
@@ -1766,19 +1786,16 @@ STATIC int drbd_nl_get_state(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 	if (s.conn >= C_SYNC_SOURCE && s.conn <= C_PAUSED_SYNC_T) {
 		if (get_ldev(mdev)) {
 			drbd_get_syncer_progress(mdev, &rs_left, &res);
-			*tl++ = T_sync_progress;
-			*tl++ = sizeof(int);
-			memcpy(tl, &res, sizeof(int));
-			tl = (unsigned short *)((char *)tl + sizeof(int));
+			tl = tl_add_int(tl, T_sync_progress, &res);
 			put_ldev(mdev);
 		}
 	}
-	*tl++ = TT_END; /* Close the tag list */
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	return (int)((char *)tl - (char *)reply->tag_list);
 }
 
-STATIC int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			     struct drbd_nl_cfg_reply *reply)
 {
 	unsigned short *tl;
@@ -1786,18 +1803,11 @@ STATIC int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
 	tl = reply->tag_list;
 
 	if (get_ldev(mdev)) {
-		/* This is a hand crafted add tag ;) */
-		*tl++ = T_uuids;
-		*tl++ = UI_SIZE*sizeof(u64);
-		memcpy(tl, mdev->ldev->md.uuid, UI_SIZE*sizeof(u64));
-		tl = (unsigned short *)((char *)tl + UI_SIZE*sizeof(u64));
-		*tl++ = T_uuids_flags;
-		*tl++ = sizeof(int);
-		memcpy(tl, &mdev->ldev->md.flags, sizeof(int));
-		tl = (unsigned short *)((char *)tl + sizeof(int));
+		tl = tl_add_blob(tl, T_uuids, mdev->ldev->md.uuid, UI_SIZE*sizeof(u64));
+		tl = tl_add_int(tl, T_uuids_flags, &mdev->ldev->md.flags);
 		put_ldev(mdev);
 	}
-	*tl++ = TT_END; /* Close the tag list */
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	return (int)((char *)tl - (char *)reply->tag_list);
 }
@@ -1808,7 +1818,7 @@ STATIC int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
  * @nlp:	Netlink/connector packet from drbdsetup
  * @reply:	Reply packet for drbdsetup
  */
-STATIC int drbd_nl_get_timeout_flag(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_get_timeout_flag(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 				    struct drbd_nl_cfg_reply *reply)
 {
 	unsigned short *tl;
@@ -1819,26 +1829,31 @@ STATIC int drbd_nl_get_timeout_flag(struct drbd_conf *mdev, struct drbd_nl_cfg_r
 	rv = mdev->state.pdsk == D_OUTDATED        ? UT_PEER_OUTDATED :
 	  test_bit(USE_DEGR_WFC_T, &mdev->flags) ? UT_DEGRADED : UT_DEFAULT;
 
-	/* This is a hand crafted add tag ;) */
-	*tl++ = T_use_degraded;
-	*tl++ = sizeof(char);
-	*((char *)tl) = rv;
-	tl = (unsigned short *)((char *)tl + sizeof(char));
-	*tl++ = TT_END;
+	tl = tl_add_blob(tl, T_use_degraded, &rv, sizeof(rv));
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	return (int)((char *)tl - (char *)reply->tag_list);
 }
 
-STATIC int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 				    struct drbd_nl_cfg_reply *reply)
 {
-	reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S));
+	/* default to resume from last known position, if possible */
+	struct start_ov args =
+		{ .start_sector = mdev->ov_start_sector };
 
+	if (!start_ov_from_tags(mdev, nlp->tag_list, &args)) {
+		reply->ret_code = ERR_MANDATORY_TAG;
+		return 0;
+	}
+	/* w_make_ov_request expects position to be aligned */
+	mdev->ov_start_sector = args.start_sector & ~BM_SECT_PER_BIT;
+	reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S));
 	return 0;
 }
 
 
-STATIC int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+static int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
 			      struct drbd_nl_cfg_reply *reply)
 {
 	int retcode = NO_ERROR;
@@ -1865,7 +1880,7 @@ STATIC int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
 	    mdev->ldev->md.uuid[UI_CURRENT] == UUID_JUST_CREATED && args.clear_bm) {
 		dev_info(DEV, "Preparing to skip initial sync\n");
 		skip_initial_sync = 1;
-	} else if (mdev->state.conn >= C_CONNECTED) {
+	} else if (mdev->state.conn != C_STANDALONE) {
 		retcode = ERR_CONNECTED;
 		goto out_dec;
 	}
@@ -1899,7 +1914,7 @@ out:
 	return 0;
 }
 
-STATIC struct drbd_conf *ensure_mdev(struct drbd_nl_cfg_req *nlp)
+static struct drbd_conf *ensure_mdev(struct drbd_nl_cfg_req *nlp)
 {
 	struct drbd_conf *mdev;
 
@@ -1971,7 +1986,7 @@ static struct cn_handler_struct cnd_table[] = {
 	[ P_new_c_uuid ]	= { &drbd_nl_new_c_uuid,	0 },
 };
 
-STATIC void drbd_connector_callback(void *data)
+static void drbd_connector_callback(void *data)
 {
 	struct cn_msg *req = data;
 	struct drbd_nl_cfg_req *nlp = (struct drbd_nl_cfg_req *)req->data;
@@ -2012,6 +2027,7 @@ STATIC void drbd_connector_callback(void *data)
 
 	reply_size += cm->reply_body_size;
 
+	/* allocation not in the IO path, cqueue thread context */
 	cn_reply = kmalloc(reply_size, GFP_KERNEL);
 	if (!cn_reply) {
 		retcode = ERR_NOMEM;
@@ -2050,18 +2066,13 @@ static atomic_t drbd_nl_seq = ATOMIC_INIT(2); /* two. */
 
 static unsigned short *
 __tl_add_blob(unsigned short *tl, enum drbd_tags tag, const void *data,
-	int len, int nul_terminated)
+	unsigned short len, int nul_terminated)
 {
-	int l = tag_descriptions[tag_number(tag)].max_len;
-	l = (len < l) ? len :  l;
-	*tl++ = tag;
-	*tl++ = len;
+	unsigned short l = tag_descriptions[tag_number(tag)].max_len;
+	len = (len < l) ? len :  l;
+	put_unaligned(tag, tl++);
+	put_unaligned(len, tl++);
 	memcpy(tl, data, len);
-	/* TODO
-	 * maybe we need to add some padding to the data stream.
-	 * otherwise we may get strange effects on architectures
-	 * that require certain data types to be strictly aligned,
-	 * because now the next "unsigned short" may be misaligned. */
 	tl = (unsigned short*)((char*)tl + len);
 	if (nul_terminated)
 		*((char*)tl - 1) = 0;
@@ -2083,17 +2094,16 @@ tl_add_str(unsigned short *tl, enum drbd_tags tag, const char *str)
 static unsigned short *
 tl_add_int(unsigned short *tl, enum drbd_tags tag, const void *val)
 {
+	put_unaligned(tag, tl++);
 	switch(tag_type(tag)) {
 	case TT_INTEGER:
-		*tl++ = tag;
-		*tl++ = sizeof(int);
-		*(int*)tl = *(int*)val;
+		put_unaligned(sizeof(int), tl++);
+		put_unaligned(*(int *)val, (int *)tl++);
 		tl = (unsigned short*)((char*)tl+sizeof(int));
 		break;
 	case TT_INT64:
-		*tl++ = tag;
-		*tl++ = sizeof(u64);
-		*(u64*)tl = *(u64*)val;
+		put_unaligned(sizeof(u64), tl++);
+		put_unaligned(*(u64 *)val, (u64 *)tl++);
 		tl = (unsigned short*)((char*)tl+sizeof(u64));
 		break;
 	default:
@@ -2117,7 +2127,8 @@ void drbd_bcast_state(struct drbd_conf *mdev, union drbd_state state)
 	/* dev_warn(DEV, "drbd_bcast_state() got called\n"); */
 
 	tl = get_state_to_tags(mdev, (struct get_state *)&state, tl);
-	*tl++ = TT_END; /* Close the tag list */
+
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	cn_reply->id.idx = CN_IDX_DRBD;
 	cn_reply->id.val = CN_VAL_DRBD;
@@ -2146,16 +2157,11 @@ void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name)
 	struct drbd_nl_cfg_reply *reply =
 		(struct drbd_nl_cfg_reply *)cn_reply->data;
 	unsigned short *tl = reply->tag_list;
-	int str_len;
 
 	/* dev_warn(DEV, "drbd_bcast_state() got called\n"); */
 
-	str_len = strlen(helper_name)+1;
-	*tl++ = T_helper;
-	*tl++ = str_len;
-	memcpy(tl, helper_name, str_len);
-	tl = (unsigned short *)((char *)tl + str_len);
-	*tl++ = TT_END; /* Close the tag list */
+	tl = tl_add_str(tl, T_helper, helper_name);
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	cn_reply->id.idx = CN_IDX_DRBD;
 	cn_reply->id.val = CN_VAL_DRBD;
@@ -2193,12 +2199,15 @@ void drbd_bcast_ee(struct drbd_conf *mdev,
 	/* aparently we have to memcpy twice, first to prepare the data for the
 	 * struct cn_msg, then within cn_netlink_send from the cn_msg to the
 	 * netlink skb. */
+	/* receiver thread context, which is not in the writeout path (of this node),
+	 * but may be in the writeout path of the _other_ node.
+	 * GFP_NOIO to avoid potential "distributed deadlock". */
 	cn_reply = kmalloc(
 		sizeof(struct cn_msg)+
 		sizeof(struct drbd_nl_cfg_reply)+
 		sizeof(struct dump_ee_tag_len_struct)+
-		sizeof(short int)
-		, GFP_KERNEL);
+		sizeof(short int),
+		GFP_NOIO);
 
 	if (!cn_reply) {
 		dev_err(DEV, "could not kmalloc buffer for drbd_bcast_ee, sector %llu, size %u\n",
@@ -2215,8 +2224,8 @@ void drbd_bcast_ee(struct drbd_conf *mdev,
 	tl = tl_add_int(tl, T_ee_sector, &e->sector);
 	tl = tl_add_int(tl, T_ee_block_id, &e->block_id);
 
-	*tl++ = T_ee_data;
-	*tl++ = e->size;
+	put_unaligned(T_ee_data, tl++);
+	put_unaligned(e->size, tl++);
 
 	__bio_for_each_segment(bvec, e->private_bio, i, 0) {
 		void *d = kmap(bvec->bv_page);
@@ -2224,7 +2233,7 @@ void drbd_bcast_ee(struct drbd_conf *mdev,
 		kunmap(bvec->bv_page);
 		tl=(unsigned short*)((char*)tl + bvec->bv_len);
 	}
-	*tl++ = TT_END; /* Close the tag list */
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	cn_reply->id.idx = CN_IDX_DRBD;
 	cn_reply->id.val = CN_VAL_DRBD;
@@ -2263,11 +2272,8 @@ void drbd_bcast_sync_progress(struct drbd_conf *mdev)
 	drbd_get_syncer_progress(mdev, &rs_left, &res);
 	put_ldev(mdev);
 
-	*tl++ = T_sync_progress;
-	*tl++ = sizeof(int);
-	memcpy(tl, &res, sizeof(int));
-	tl = (unsigned short *)((char *)tl + sizeof(int));
-	*tl++ = TT_END; /* Close the tag list */
+	tl = tl_add_int(tl, T_sync_progress, &res);
+	put_unaligned(TT_END, tl++); /* Close the tag list */
 
 	cn_reply->id.idx = CN_IDX_DRBD;
 	cn_reply->id.val = CN_VAL_DRBD;
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index b59b9d9f078..432a7dd39f7 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -32,11 +32,10 @@
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/drbd_config.h>
 #include <linux/drbd.h>
 #include "drbd_int.h"
 
-STATIC int drbd_proc_open(struct inode *inode, struct file *file);
+static int drbd_proc_open(struct inode *inode, struct file *file);
 
 
 struct proc_dir_entry *drbd_proc;
@@ -55,7 +54,7 @@ struct file_operations drbd_proc_fops = {
  *	[=====>..............] 33.5% (23456/123456)
  *	finish: 2:20:20 speed: 6,345 (6,456) K/sec
  */
-STATIC void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
+static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
 {
 	unsigned long db, dt, dbdt, rt, rs_left;
 	unsigned int res;
@@ -134,7 +133,7 @@ STATIC void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
 	seq_printf(seq, " K/sec\n");
 }
 
-STATIC void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
+static void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
 {
 	struct bm_extent *bme = lc_entry(e, struct bm_extent, lce);
 
@@ -144,7 +143,7 @@ STATIC void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
 		   );
 }
 
-STATIC int drbd_seq_show(struct seq_file *seq, void *v)
+static int drbd_seq_show(struct seq_file *seq, void *v)
 {
 	int i, hole = 0;
 	const char *sn;
@@ -259,7 +258,7 @@ STATIC int drbd_seq_show(struct seq_file *seq, void *v)
 	return 0;
 }
 
-STATIC int drbd_proc_open(struct inode *inode, struct file *file)
+static int drbd_proc_open(struct inode *inode, struct file *file)
 {
 	return single_open(file, drbd_seq_show, PDE(inode)->data);
 }
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 24dc84698de..b222b24ddc5 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -30,11 +30,11 @@
 #include <net/sock.h>
 
 #include <linux/version.h>
+#include <linux/drbd.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/in.h>
 #include <linux/mm.h>
-#include <linux/drbd_config.h>
 #include <linux/memcontrol.h>
 #include <linux/mm_inline.h>
 #include <linux/slab.h>
@@ -47,7 +47,6 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/scatterlist.h>
-#include <linux/drbd.h>
 #include "drbd_int.h"
 #include "drbd_tracing.h"
 #include "drbd_req.h"
@@ -65,11 +64,11 @@ enum finish_epoch {
 	FE_RECYCLED,
 };
 
-STATIC int drbd_do_handshake(struct drbd_conf *mdev);
-STATIC int drbd_do_auth(struct drbd_conf *mdev);
+static int drbd_do_handshake(struct drbd_conf *mdev);
+static int drbd_do_auth(struct drbd_conf *mdev);
 
-STATIC enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *, struct drbd_epoch *, enum epoch_event);
-STATIC int e_end_block(struct drbd_conf *, struct drbd_work *, int);
+static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *, struct drbd_epoch *, enum epoch_event);
+static int e_end_block(struct drbd_conf *, struct drbd_work *, int);
 
 static struct drbd_epoch *previous_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch)
 {
@@ -93,7 +92,7 @@ static struct drbd_epoch *previous_epoch(struct drbd_conf *mdev, struct drbd_epo
  * allocation would go beyond the max_buffers setting, this function sleeps
  * until DRBD frees a page somewhere else.
  */
-STATIC struct page *drbd_pp_alloc(struct drbd_conf *mdev, gfp_t gfp_mask)
+static struct page *drbd_pp_alloc(struct drbd_conf *mdev, gfp_t gfp_mask)
 {
 	unsigned long flags = 0;
 	struct page *page;
@@ -162,7 +161,7 @@ STATIC struct page *drbd_pp_alloc(struct drbd_conf *mdev, gfp_t gfp_mask)
 	return page;
 }
 
-STATIC void drbd_pp_free(struct drbd_conf *mdev, struct page *page)
+static void drbd_pp_free(struct drbd_conf *mdev, struct page *page)
 {
 	unsigned long flags = 0;
 	int free_it;
@@ -260,10 +259,10 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
 			}
 
 			/* dump more of the bio. */
-			DUMPI(bio->bi_max_vecs);
-			DUMPI(bio->bi_vcnt);
-			DUMPI(bio->bi_size);
-			DUMPI(bio->bi_phys_segments);
+			dev_err(DEV, "bio->bi_max_vecs = %d\n", bio->bi_max_vecs);
+			dev_err(DEV, "bio->bi_vcnt = %d\n", bio->bi_vcnt);
+			dev_err(DEV, "bio->bi_size = %d\n", bio->bi_size);
+			dev_err(DEV, "bio->bi_phys_segments = %d\n", bio->bi_phys_segments);
 
 			goto fail2;
 			break;
@@ -339,7 +338,7 @@ int drbd_release_ee(struct drbd_conf *mdev, struct list_head *list)
 }
 
 
-STATIC void reclaim_net_ee(struct drbd_conf *mdev)
+static void reclaim_net_ee(struct drbd_conf *mdev)
 {
 	struct drbd_epoch_entry *e;
 	struct list_head *le, *tle;
@@ -368,7 +367,7 @@ STATIC void reclaim_net_ee(struct drbd_conf *mdev)
  * Grab done_ee, call all callbacks, free the entries.
  * The callbacks typically send out ACKs.
  */
-STATIC int drbd_process_done_ee(struct drbd_conf *mdev)
+static int drbd_process_done_ee(struct drbd_conf *mdev)
 {
 	LIST_HEAD(work_list);
 	struct drbd_epoch_entry *e, *t;
@@ -458,7 +457,7 @@ void drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head)
 
 /* see also kernel_accept; which is only present since 2.6.18.
  * also we want to log which part of it failed, exactly */
-STATIC int drbd_accept(struct drbd_conf *mdev, const char **what,
+static int drbd_accept(struct drbd_conf *mdev, const char **what,
 		struct socket *sock, struct socket **newsock)
 {
 	struct sock *sk = sock->sk;
@@ -488,7 +487,7 @@ out:
 	return err;
 }
 
-STATIC int drbd_recv_short(struct drbd_conf *mdev, struct socket *sock,
+static int drbd_recv_short(struct drbd_conf *mdev, struct socket *sock,
 		    void *buf, size_t size, int flags)
 {
 	mm_segment_t oldfs;
@@ -511,7 +510,7 @@ STATIC int drbd_recv_short(struct drbd_conf *mdev, struct socket *sock,
 	return rv;
 }
 
-STATIC int drbd_recv(struct drbd_conf *mdev, void *buf, size_t size)
+static int drbd_recv(struct drbd_conf *mdev, void *buf, size_t size)
 {
 	mm_segment_t oldfs;
 	struct kvec iov = {
@@ -564,7 +563,7 @@ STATIC int drbd_recv(struct drbd_conf *mdev, void *buf, size_t size)
 	return rv;
 }
 
-STATIC struct socket *drbd_try_connect(struct drbd_conf *mdev)
+static struct socket *drbd_try_connect(struct drbd_conf *mdev)
 {
 	const char *what;
 	struct socket *sock;
@@ -640,7 +639,7 @@ out:
 	return sock;
 }
 
-STATIC struct socket *drbd_wait_for_connect(struct drbd_conf *mdev)
+static struct socket *drbd_wait_for_connect(struct drbd_conf *mdev)
 {
 	int timeo, err;
 	struct socket *s_estab = NULL, *s_listen;
@@ -687,7 +686,7 @@ out:
 	return s_estab;
 }
 
-STATIC int drbd_send_fp(struct drbd_conf *mdev,
+static int drbd_send_fp(struct drbd_conf *mdev,
 	struct socket *sock, enum drbd_packets cmd)
 {
 	struct p_header *h = (struct p_header *) &mdev->data.sbuf.header;
@@ -695,7 +694,7 @@ STATIC int drbd_send_fp(struct drbd_conf *mdev,
 	return _drbd_send_cmd(mdev, sock, cmd, h, sizeof(*h), 0);
 }
 
-STATIC enum drbd_packets drbd_recv_fp(struct drbd_conf *mdev, struct socket *sock)
+static enum drbd_packets drbd_recv_fp(struct drbd_conf *mdev, struct socket *sock)
 {
 	struct p_header *h = (struct p_header *) &mdev->data.sbuf.header;
 	int rr;
@@ -740,7 +739,7 @@ static int drbd_socket_okay(struct drbd_conf *mdev, struct socket **sock)
  *     no point in trying again, please go standalone.
  *  -2 We do not have a network config...
  */
-STATIC int drbd_connect(struct drbd_conf *mdev)
+static int drbd_connect(struct drbd_conf *mdev)
 {
 	struct socket *s, *sock, *msock;
 	int try, h, ok;
@@ -856,8 +855,12 @@ retry:
 
 	if (mdev->net_conf->sndbuf_size) {
 		sock->sk->sk_sndbuf = mdev->net_conf->sndbuf_size;
-		sock->sk->sk_rcvbuf = mdev->net_conf->sndbuf_size;
-		sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK;
+		sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
+	}
+
+	if (mdev->net_conf->rcvbuf_size) {
+		sock->sk->sk_rcvbuf = mdev->net_conf->rcvbuf_size;
+		sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
 	}
 
 	/* NOT YET ...
@@ -906,15 +909,16 @@ retry:
 
 	drbd_send_protocol(mdev);
 	drbd_send_sync_param(mdev, &mdev->sync_conf);
-	drbd_send_sizes(mdev);
+	drbd_send_sizes(mdev, 0);
 	drbd_send_uuids(mdev);
 	drbd_send_state(mdev);
 	clear_bit(USE_DEGR_WFC_T, &mdev->flags);
+	clear_bit(RESIZE_PENDING, &mdev->flags);
 
 	return 1;
 }
 
-STATIC int drbd_recv_header(struct drbd_conf *mdev, struct p_header *h)
+static int drbd_recv_header(struct drbd_conf *mdev, struct p_header *h)
 {
 	int r;
 
@@ -937,7 +941,7 @@ STATIC int drbd_recv_header(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch)
+static enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch)
 {
 	int rv;
 
@@ -956,7 +960,7 @@ STATIC enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct d
 	return drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE);
 }
 
-STATIC int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+static int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 {
 	struct flush_work *fw = (struct flush_work *)w;
 	struct drbd_epoch *epoch = fw->epoch;
@@ -978,7 +982,7 @@ STATIC int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
  * @epoch:	Epoch object.
  * @ev:		Epoch event.
  */
-STATIC enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev,
+static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev,
 					       struct drbd_epoch *epoch,
 					       enum epoch_event ev)
 {
@@ -1173,7 +1177,7 @@ int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __relea
 	return 1;
 }
 
-STATIC int receive_Barrier(struct drbd_conf *mdev, struct p_header *h)
+static int receive_Barrier(struct drbd_conf *mdev, struct p_header *h)
 {
 	int rv, issue_flush;
 	struct p_barrier *p = (struct p_barrier *)h;
@@ -1219,7 +1223,9 @@ STATIC int receive_Barrier(struct drbd_conf *mdev, struct p_header *h)
 		break;
 	}
 
-	epoch = kmalloc(sizeof(struct drbd_epoch), GFP_KERNEL);
+	/* receiver context, in the writeout path of the other node.
+	 * avoid potential distributed deadlock */
+	epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO);
 	if (!epoch) {
 		dev_warn(DEV, "Allocation of an epoch failed, slowing down\n");
 		issue_flush = !test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
@@ -1256,7 +1262,7 @@ STATIC int receive_Barrier(struct drbd_conf *mdev, struct p_header *h)
 
 /* used from receive_RSDataReply (recv_resync_read)
  * and from receive_Data */
-STATIC struct drbd_epoch_entry *
+static struct drbd_epoch_entry *
 read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __must_hold(local)
 {
 	struct drbd_epoch_entry *e;
@@ -1319,7 +1325,7 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
 /* drbd_drain_block() just takes a data block
  * out of the socket input buffer, and discards it.
  */
-STATIC int drbd_drain_block(struct drbd_conf *mdev, int data_size)
+static int drbd_drain_block(struct drbd_conf *mdev, int data_size)
 {
 	struct page *page;
 	int rr, rv = 1;
@@ -1352,7 +1358,7 @@ static void maybe_kick_lo(struct drbd_conf *mdev)
 		drbd_kick_lo(mdev);
 }
 
-STATIC int recv_dless_read(struct drbd_conf *mdev, struct drbd_request *req,
+static int recv_dless_read(struct drbd_conf *mdev, struct drbd_request *req,
 			   sector_t sector, int data_size)
 {
 	struct bio_vec *bvec;
@@ -1407,7 +1413,7 @@ STATIC int recv_dless_read(struct drbd_conf *mdev, struct drbd_request *req,
 
 /* e_end_resync_block() is called via
  * drbd_process_done_ee() by asender only */
-STATIC int e_end_resync_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int e_end_resync_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
 	sector_t sector = e->sector;
@@ -1430,7 +1436,7 @@ STATIC int e_end_resync_block(struct drbd_conf *mdev, struct drbd_work *w, int u
 	return ok;
 }
 
-STATIC int recv_resync_read(struct drbd_conf *mdev, sector_t sector, int data_size) __releases(local)
+static int recv_resync_read(struct drbd_conf *mdev, sector_t sector, int data_size) __releases(local)
 {
 	struct drbd_epoch_entry *e;
 
@@ -1463,7 +1469,7 @@ STATIC int recv_resync_read(struct drbd_conf *mdev, sector_t sector, int data_si
 	return TRUE;
 }
 
-STATIC int receive_DataReply(struct drbd_conf *mdev, struct p_header *h)
+static int receive_DataReply(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct drbd_request *req;
 	sector_t sector;
@@ -1503,7 +1509,7 @@ STATIC int receive_DataReply(struct drbd_conf *mdev, struct p_header *h)
 	return ok;
 }
 
-STATIC int receive_RSDataReply(struct drbd_conf *mdev, struct p_header *h)
+static int receive_RSDataReply(struct drbd_conf *mdev, struct p_header *h)
 {
 	sector_t sector;
 	unsigned int header_size, data_size;
@@ -1541,7 +1547,7 @@ STATIC int receive_RSDataReply(struct drbd_conf *mdev, struct p_header *h)
 /* e_end_block() is called via drbd_process_done_ee().
  * this means this function only runs in the asender thread
  */
-STATIC int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
 	sector_t sector = e->sector;
@@ -1590,7 +1596,7 @@ STATIC int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 	return ok;
 }
 
-STATIC int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+static int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int unused)
 {
 	struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
 	int ok = 1;
@@ -1662,7 +1668,7 @@ static int drbd_wait_peer_seq(struct drbd_conf *mdev, const u32 packet_seq)
 }
 
 /* mirrored write */
-STATIC int receive_Data(struct drbd_conf *mdev, struct p_header *h)
+static int receive_Data(struct drbd_conf *mdev, struct p_header *h)
 {
 	sector_t sector;
 	struct drbd_epoch_entry *e;
@@ -1918,7 +1924,7 @@ out_interrupted:
 	return FALSE;
 }
 
-STATIC int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
+static int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
 {
 	sector_t sector;
 	const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
@@ -1992,7 +1998,7 @@ STATIC int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
 	case P_CSUM_RS_REQUEST:
 		fault_type = DRBD_FAULT_RS_RD;
 		digest_size = h->length - brps ;
-		di = kmalloc(sizeof(*di) + digest_size, GFP_KERNEL);
+		di = kmalloc(sizeof(*di) + digest_size, GFP_NOIO);
 		if (!di) {
 			put_ldev(mdev);
 			drbd_free_ee(mdev, e);
@@ -2030,6 +2036,18 @@ STATIC int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
 		break;
 
 	case P_OV_REQUEST:
+		if (mdev->state.conn >= C_CONNECTED &&
+		    mdev->state.conn != C_VERIFY_T)
+			dev_warn(DEV, "ASSERT FAILED: got P_OV_REQUEST while being %s\n",
+				conns_to_name(mdev->state.conn));
+		if (mdev->ov_start_sector == ~(sector_t)0 &&
+		    mdev->agreed_pro_version >= 90) {
+			mdev->ov_start_sector = sector;
+			mdev->ov_position = sector;
+			mdev->ov_left = mdev->rs_total - BM_SECT_TO_BIT(sector);
+			dev_info(DEV, "Online Verify start sector: %llu\n",
+					(unsigned long long)sector);
+		}
 		e->w.cb = w_e_end_ov_req;
 		fault_type = DRBD_FAULT_RS_RD;
 		/* Eventually this should become asynchrously. Currently it
@@ -2068,7 +2086,7 @@ STATIC int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int drbd_asb_recover_0p(struct drbd_conf *mdev) __must_hold(local)
+static int drbd_asb_recover_0p(struct drbd_conf *mdev) __must_hold(local)
 {
 	int self, peer, rv = -100;
 	unsigned long ch_self, ch_peer;
@@ -2140,7 +2158,7 @@ STATIC int drbd_asb_recover_0p(struct drbd_conf *mdev) __must_hold(local)
 	return rv;
 }
 
-STATIC int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
+static int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
 {
 	int self, peer, hg, rv = -100;
 
@@ -2173,6 +2191,10 @@ STATIC int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
 		hg = drbd_asb_recover_0p(mdev);
 		if (hg == -1 && mdev->state.role == R_PRIMARY) {
 			self = drbd_set_role(mdev, R_SECONDARY, 0);
+			 /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE,
+			  * we might be here in C_WF_REPORT_PARAMS which is transient.
+			  * we do not need to wait for the after state change work either. */
+			self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY));
 			if (self != SS_SUCCESS) {
 				drbd_khelper(mdev, "pri-lost-after-sb");
 			} else {
@@ -2186,7 +2208,7 @@ STATIC int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
 	return rv;
 }
 
-STATIC int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local)
+static int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local)
 {
 	int self, peer, hg, rv = -100;
 
@@ -2211,7 +2233,10 @@ STATIC int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local)
 	case ASB_CALL_HELPER:
 		hg = drbd_asb_recover_0p(mdev);
 		if (hg == -1) {
-			self = drbd_set_role(mdev, R_SECONDARY, 0);
+			 /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE,
+			  * we might be here in C_WF_REPORT_PARAMS which is transient.
+			  * we do not need to wait for the after state change work either. */
+			self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY));
 			if (self != SS_SUCCESS) {
 				drbd_khelper(mdev, "pri-lost-after-sb");
 			} else {
@@ -2225,7 +2250,7 @@ STATIC int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local)
 	return rv;
 }
 
-STATIC void drbd_uuid_dump(struct drbd_conf *mdev, char *text, u64 *uuid,
+static void drbd_uuid_dump(struct drbd_conf *mdev, char *text, u64 *uuid,
 			   u64 bits, u64 flags)
 {
 	if (!uuid) {
@@ -2252,7 +2277,7 @@ STATIC void drbd_uuid_dump(struct drbd_conf *mdev, char *text, u64 *uuid,
  -100	after split brain, disconnect
 -1000	unrelated data
  */
-STATIC int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(local)
+static int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(local)
 {
 	u64 self, peer;
 	int i, j;
@@ -2326,7 +2351,7 @@ STATIC int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(l
 
 	*rule_nr = 10;
 	for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) {
-		self = mdev->p_uuid[i] & ~((u64)1);
+		self = mdev->ldev->md.uuid[i] & ~((u64)1);
 		for (j = UI_HISTORY_START; j <= UI_HISTORY_END; j++) {
 			peer = mdev->p_uuid[j] & ~((u64)1);
 			if (self == peer)
@@ -2340,7 +2365,7 @@ STATIC int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(l
 /* drbd_sync_handshake() returns the new conn state on success, or
    CONN_MASK (-1) on failure.
  */
-STATIC enum drbd_conns drbd_sync_handshake(struct drbd_conf *mdev, enum drbd_role peer_role,
+static enum drbd_conns drbd_sync_handshake(struct drbd_conf *mdev, enum drbd_role peer_role,
 					   enum drbd_disk_state peer_disk) __must_hold(local)
 {
 	int hg, rule_nr;
@@ -2465,7 +2490,7 @@ STATIC enum drbd_conns drbd_sync_handshake(struct drbd_conf *mdev, enum drbd_rol
 }
 
 /* returns 1 if invalid */
-STATIC int cmp_after_sb(enum drbd_after_sb_p peer, enum drbd_after_sb_p self)
+static int cmp_after_sb(enum drbd_after_sb_p peer, enum drbd_after_sb_p self)
 {
 	/* ASB_DISCARD_REMOTE - ASB_DISCARD_LOCAL is valid */
 	if ((peer == ASB_DISCARD_REMOTE && self == ASB_DISCARD_LOCAL) ||
@@ -2485,7 +2510,7 @@ STATIC int cmp_after_sb(enum drbd_after_sb_p peer, enum drbd_after_sb_p self)
 	return 1;
 }
 
-STATIC int receive_protocol(struct drbd_conf *mdev, struct p_header *h)
+static int receive_protocol(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_protocol *p = (struct p_protocol *)h;
 	int header_size, data_size;
@@ -2577,7 +2602,7 @@ struct crypto_hash *drbd_crypto_alloc_digest_safe(const struct drbd_conf *mdev,
 			alg, name, PTR_ERR(tfm));
 		return tfm;
 	}
-	if (crypto_tfm_alg_type(crypto_hash_tfm(tfm)) != CRYPTO_ALG_TYPE_DIGEST) {
+	if (!drbd_crypto_is_hash(crypto_hash_tfm(tfm))) {
 		crypto_free_hash(tfm);
 		dev_err(DEV, "\"%s\" is not a digest (%s)\n", alg, name);
 		return ERR_PTR(-EINVAL);
@@ -2585,7 +2610,7 @@ struct crypto_hash *drbd_crypto_alloc_digest_safe(const struct drbd_conf *mdev,
 	return tfm;
 }
 
-STATIC int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
+static int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
 {
 	int ok = TRUE;
 	struct p_rs_param_89 *p = (struct p_rs_param_89 *)h;
@@ -2656,8 +2681,10 @@ STATIC int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
 			}
 			verify_tfm = drbd_crypto_alloc_digest_safe(mdev,
 					p->verify_alg, "verify-alg");
-			if (IS_ERR(verify_tfm))
+			if (IS_ERR(verify_tfm)) {
+				verify_tfm = NULL;
 				goto disconnect;
+			}
 		}
 
 		if (apv >= 89 && strcmp(mdev->sync_conf.csums_alg, p->csums_alg)) {
@@ -2668,8 +2695,10 @@ STATIC int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
 			}
 			csums_tfm = drbd_crypto_alloc_digest_safe(mdev,
 					p->csums_alg, "csums-alg");
-			if (IS_ERR(csums_tfm))
+			if (IS_ERR(csums_tfm)) {
+				csums_tfm = NULL;
 				goto disconnect;
+			}
 		}
 
 
@@ -2694,12 +2723,16 @@ STATIC int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
 
 	return ok;
 disconnect:
+	/* just for completeness: actually not needed,
+	 * as this is not reached if csums_tfm was ok. */
+	crypto_free_hash(csums_tfm);
+	/* but free the verify_tfm again, if csums_tfm did not work out */
 	crypto_free_hash(verify_tfm);
 	drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
 	return FALSE;
 }
 
-STATIC void drbd_setup_order_type(struct drbd_conf *mdev, int peer)
+static void drbd_setup_order_type(struct drbd_conf *mdev, int peer)
 {
 	/* sorry, we currently have no working implementation
 	 * of distributed TCQ */
@@ -2718,7 +2751,7 @@ static void warn_if_differ_considerably(struct drbd_conf *mdev,
 		     (unsigned long long)a, (unsigned long long)b);
 }
 
-STATIC int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
+static int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_sizes *p = (struct p_sizes *)h;
 	enum determine_dev_size dd = unchanged;
@@ -2815,7 +2848,7 @@ STATIC int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
 		}
 
 		max_seg_s = be32_to_cpu(p->max_segment_size);
-		if (max_seg_s != mdev->rq_queue->max_segment_size)
+		if (max_seg_s != queue_max_segment_size(mdev->rq_queue))
 			drbd_setup_queue_param(mdev, max_seg_s);
 
 		drbd_setup_order_type(mdev, be32_to_cpu(p->queue_order_type));
@@ -2827,9 +2860,10 @@ STATIC int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
 		    drbd_get_capacity(mdev->this_bdev) || ldsc) {
 			/* we have different sizes, probabely peer
 			 * needs to know my new size... */
-			drbd_send_sizes(mdev);
+			drbd_send_sizes(mdev, 0);
 		}
-		if (dd == grew && mdev->state.conn == C_CONNECTED) {
+		if (test_and_clear_bit(RESIZE_PENDING, &mdev->flags) ||
+		    (dd == grew && mdev->state.conn == C_CONNECTED)) {
 			if (mdev->state.pdsk >= D_INCONSISTENT &&
 			    mdev->state.disk >= D_INCONSISTENT)
 				resync_after_online_grow(mdev);
@@ -2841,7 +2875,7 @@ STATIC int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int receive_uuids(struct drbd_conf *mdev, struct p_header *h)
+static int receive_uuids(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_uuids *p = (struct p_uuids *)h;
 	u64 *p_uuid;
@@ -2851,7 +2885,7 @@ STATIC int receive_uuids(struct drbd_conf *mdev, struct p_header *h)
 	if (drbd_recv(mdev, h->payload, h->length) != h->length)
 		return FALSE;
 
-	p_uuid = kmalloc(sizeof(u64)*UI_EXTENDED_SIZE, GFP_KERNEL);
+	p_uuid = kmalloc(sizeof(u64)*UI_EXTENDED_SIZE, GFP_NOIO);
 
 	for (i = UI_CURRENT; i < UI_EXTENDED_SIZE; i++)
 		p_uuid[i] = be64_to_cpu(p->uuid[i]);
@@ -2903,7 +2937,7 @@ STATIC int receive_uuids(struct drbd_conf *mdev, struct p_header *h)
  * convert_state() - Converts the peer's view of the cluster state to our point of view
  * @ps:		The state as seen by the peer.
  */
-STATIC union drbd_state convert_state(union drbd_state ps)
+static union drbd_state convert_state(union drbd_state ps)
 {
 	union drbd_state ms;
 
@@ -2929,7 +2963,7 @@ STATIC union drbd_state convert_state(union drbd_state ps)
 	return ms;
 }
 
-STATIC int receive_req_state(struct drbd_conf *mdev, struct p_header *h)
+static int receive_req_state(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_req_state *p = (struct p_req_state *)h;
 	union drbd_state mask, val;
@@ -2959,7 +2993,7 @@ STATIC int receive_req_state(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int receive_state(struct drbd_conf *mdev, struct p_header *h)
+static int receive_state(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_state *p = (struct p_state *)h;
 	enum drbd_conns nconn, oconn;
@@ -2993,12 +3027,21 @@ STATIC int receive_state(struct drbd_conf *mdev, struct p_header *h)
 	    get_ldev_if_state(mdev, D_NEGOTIATING)) {
 		int cr; /* consider resync */
 
+		/* if we established a new connection */
 		cr  = (oconn < C_CONNECTED);
+		/* if we had an established connection
+		 * and one of the nodes newly attaches a disk */
 		cr |= (oconn == C_CONNECTED &&
 		       (peer_state.disk == D_NEGOTIATING ||
 			mdev->state.disk == D_NEGOTIATING));
-		cr |= test_bit(CONSIDER_RESYNC, &mdev->flags); /* peer forced */
-		cr |= (oconn == C_CONNECTED && peer_state.conn > C_CONNECTED);
+		/* if we have both been inconsistent, and the peer has been
+		 * forced to be UpToDate with --overwrite-data */
+		cr |= test_bit(CONSIDER_RESYNC, &mdev->flags);
+		/* if we had been plain connected, and the admin requested to
+		 * start a sync by "invalidate" or "invalidate-remote" */
+		cr |= (oconn == C_CONNECTED &&
+				(peer_state.conn >= C_STARTING_SYNC_S &&
+				 peer_state.conn <= C_WF_BITMAP_T));
 
 		if (cr)
 			nconn = drbd_sync_handshake(mdev, peer_state.role, real_peer_disk);
@@ -3058,7 +3101,7 @@ STATIC int receive_state(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int receive_sync_uuid(struct drbd_conf *mdev, struct p_header *h)
+static int receive_sync_uuid(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_rs_uuid *p = (struct p_rs_uuid *)h;
 
@@ -3233,7 +3276,7 @@ void INFO_bm_xfer_stats(struct drbd_conf *mdev,
    in order to be agnostic to the 32 vs 64 bits issue.
 
    returns 0 on failure, 1 if we suceessfully received it. */
-STATIC int receive_bitmap(struct drbd_conf *mdev, struct p_header *h)
+static int receive_bitmap(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct bm_xfer_ctx c;
 	void *buffer;
@@ -3321,7 +3364,7 @@ STATIC int receive_bitmap(struct drbd_conf *mdev, struct p_header *h)
 	return ok;
 }
 
-STATIC int receive_skip(struct drbd_conf *mdev, struct p_header *h)
+static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
 {
 	/* TODO zero copy sink :) */
 	static char sink[128];
@@ -3340,7 +3383,7 @@ STATIC int receive_skip(struct drbd_conf *mdev, struct p_header *h)
 	return size == 0;
 }
 
-STATIC int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
+static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
 {
 	if (mdev->state.disk >= D_INCONSISTENT)
 		drbd_kick_lo(mdev);
@@ -3383,7 +3426,7 @@ static drbd_cmd_handler_f drbd_default_handler[] = {
 static drbd_cmd_handler_f *drbd_cmd_handler = drbd_default_handler;
 static drbd_cmd_handler_f *drbd_opt_cmd_handler;
 
-STATIC void drbdd(struct drbd_conf *mdev)
+static void drbdd(struct drbd_conf *mdev)
 {
 	drbd_cmd_handler_f handler;
 	struct p_header *header = &mdev->data.rbuf.header;
@@ -3421,7 +3464,7 @@ STATIC void drbdd(struct drbd_conf *mdev)
 	}
 }
 
-STATIC void drbd_fail_pending_reads(struct drbd_conf *mdev)
+static void drbd_fail_pending_reads(struct drbd_conf *mdev)
 {
 	struct hlist_head *slot;
 	struct hlist_node *pos;
@@ -3454,7 +3497,7 @@ STATIC void drbd_fail_pending_reads(struct drbd_conf *mdev)
 	spin_unlock_irq(&mdev->req_lock);
 }
 
-STATIC void drbd_disconnect(struct drbd_conf *mdev)
+static void drbd_disconnect(struct drbd_conf *mdev)
 {
 	struct drbd_work prev_work_done;
 	enum drbd_fencing_p fp;
@@ -3611,7 +3654,7 @@ STATIC void drbd_disconnect(struct drbd_conf *mdev)
  *
  * for now, they are expected to be zero, but ignored.
  */
-STATIC int drbd_send_handshake(struct drbd_conf *mdev)
+static int drbd_send_handshake(struct drbd_conf *mdev)
 {
 	/* ASSERT current == mdev->receiver ... */
 	struct p_handshake *p = &mdev->data.sbuf.handshake;
@@ -3761,7 +3804,7 @@ int drbd_do_auth(struct drbd_conf *mdev)
 		goto fail;
 	}
 
-	peers_ch = kmalloc(p.length, GFP_KERNEL);
+	peers_ch = kmalloc(p.length, GFP_NOIO);
 	if (peers_ch == NULL) {
 		dev_err(DEV, "kmalloc of peers_ch failed\n");
 		rv = 0;
@@ -3777,7 +3820,7 @@ int drbd_do_auth(struct drbd_conf *mdev)
 	}
 
 	resp_size = crypto_hash_digestsize(mdev->cram_hmac_tfm);
-	response = kmalloc(resp_size, GFP_KERNEL);
+	response = kmalloc(resp_size, GFP_NOIO);
 	if (response == NULL) {
 		dev_err(DEV, "kmalloc of response failed\n");
 		rv = 0;
@@ -3823,7 +3866,7 @@ int drbd_do_auth(struct drbd_conf *mdev)
 		goto fail;
 	}
 
-	right_response = kmalloc(resp_size, GFP_KERNEL);
+	right_response = kmalloc(resp_size, GFP_NOIO);
 	if (response == NULL) {
 		dev_err(DEV, "kmalloc of right_response failed\n");
 		rv = 0;
@@ -3854,7 +3897,7 @@ int drbd_do_auth(struct drbd_conf *mdev)
 }
 #endif
 
-STATIC int drbdd_init(struct drbd_thread *thi)
+int drbdd_init(struct drbd_thread *thi)
 {
 	struct drbd_conf *mdev = thi->mdev;
 	unsigned int minor = mdev_to_minor(mdev);
@@ -3892,7 +3935,7 @@ STATIC int drbdd_init(struct drbd_thread *thi)
 
 /* ********* acknowledge sender ******** */
 
-STATIC int got_RqSReply(struct drbd_conf *mdev, struct p_header *h)
+static int got_RqSReply(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_req_state_reply *p = (struct p_req_state_reply *)h;
 
@@ -3910,13 +3953,13 @@ STATIC int got_RqSReply(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_Ping(struct drbd_conf *mdev, struct p_header *h)
+static int got_Ping(struct drbd_conf *mdev, struct p_header *h)
 {
 	return drbd_send_ping_ack(mdev);
 
 }
 
-STATIC int got_PingAck(struct drbd_conf *mdev, struct p_header *h)
+static int got_PingAck(struct drbd_conf *mdev, struct p_header *h)
 {
 	/* restore idle timeout */
 	mdev->meta.socket->sk->sk_rcvtimeo = mdev->net_conf->ping_int*HZ;
@@ -3924,7 +3967,7 @@ STATIC int got_PingAck(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_IsInSync(struct drbd_conf *mdev, struct p_header *h)
+static int got_IsInSync(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_block_ack *p = (struct p_block_ack *)h;
 	sector_t sector = be64_to_cpu(p->sector);
@@ -3969,7 +4012,7 @@ static struct drbd_request *_ack_id_to_req(struct drbd_conf *mdev,
 	return NULL;
 }
 
-STATIC int got_BlockAck(struct drbd_conf *mdev, struct p_header *h)
+static int got_BlockAck(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct drbd_request *req;
 	struct p_block_ack *p = (struct p_block_ack *)h;
@@ -4021,7 +4064,7 @@ STATIC int got_BlockAck(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_NegAck(struct drbd_conf *mdev, struct p_header *h)
+static int got_NegAck(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_block_ack *p = (struct p_block_ack *)h;
 	sector_t sector = be64_to_cpu(p->sector);
@@ -4055,7 +4098,7 @@ STATIC int got_NegAck(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_NegDReply(struct drbd_conf *mdev, struct p_header *h)
+static int got_NegDReply(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct drbd_request *req;
 	struct p_block_ack *p = (struct p_block_ack *)h;
@@ -4080,7 +4123,7 @@ STATIC int got_NegDReply(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_NegRSDReply(struct drbd_conf *mdev, struct p_header *h)
+static int got_NegRSDReply(struct drbd_conf *mdev, struct p_header *h)
 {
 	sector_t sector;
 	int size;
@@ -4103,7 +4146,7 @@ STATIC int got_NegRSDReply(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_BarrierAck(struct drbd_conf *mdev, struct p_header *h)
+static int got_BarrierAck(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_barrier_ack *p = (struct p_barrier_ack *)h;
 
@@ -4112,7 +4155,7 @@ STATIC int got_BarrierAck(struct drbd_conf *mdev, struct p_header *h)
 	return TRUE;
 }
 
-STATIC int got_OVResult(struct drbd_conf *mdev, struct p_header *h)
+static int got_OVResult(struct drbd_conf *mdev, struct p_header *h)
 {
 	struct p_block_ack *p = (struct p_block_ack *)h;
 	struct drbd_work *w;
@@ -4133,12 +4176,13 @@ STATIC int got_OVResult(struct drbd_conf *mdev, struct p_header *h)
 	dec_rs_pending(mdev);
 
 	if (--mdev->ov_left == 0) {
-		w = kmalloc(sizeof(*w), GFP_KERNEL);
+		w = kmalloc(sizeof(*w), GFP_NOIO);
 		if (w) {
 			w->cb = w_ov_finished;
 			drbd_queue_work_front(&mdev->data.work, w);
 		} else {
 			dev_err(DEV, "kmalloc(w) failed.");
+			ov_oos_print(mdev);
 			drbd_resync_finished(mdev);
 		}
 	}
@@ -4165,16 +4209,18 @@ static struct asender_cmd *get_asender_cmd(int cmd)
 	[P_NEG_ACK]	    = { sizeof(struct p_block_ack), got_NegAck },
 	[P_NEG_DREPLY]	    = { sizeof(struct p_block_ack), got_NegDReply },
 	[P_NEG_RS_DREPLY]   = { sizeof(struct p_block_ack), got_NegRSDReply},
+	[P_OV_RESULT]	    = { sizeof(struct p_block_ack), got_OVResult },
 	[P_BARRIER_ACK]	    = { sizeof(struct p_barrier_ack), got_BarrierAck },
 	[P_STATE_CHG_REPLY] = { sizeof(struct p_req_state_reply), got_RqSReply },
 	[P_RS_IS_IN_SYNC]   = { sizeof(struct p_block_ack), got_IsInSync },
+	[P_MAX_CMD]	    = { 0, NULL },
 	};
-	if (cmd > P_MAX_CMD)
+	if (cmd > P_MAX_CMD || asender_tbl[cmd].process == NULL)
 		return NULL;
 	return &asender_tbl[cmd];
 }
 
-STATIC int drbd_asender(struct drbd_thread *thi)
+int drbd_asender(struct drbd_thread *thi)
 {
 	struct drbd_conf *mdev = thi->mdev;
 	struct p_header *h = &mdev->meta.rbuf.header;
@@ -4285,7 +4331,6 @@ STATIC int drbd_asender(struct drbd_thread *thi)
 			expect = cmd->pkt_size;
 			ERR_IF(len != expect-sizeof(struct p_header)) {
 				trace_drbd_packet(mdev, mdev->meta.socket, 1, (void *)h, __FILE__, __LINE__);
-				DUMPI(expect);
 				goto reconnect;
 			}
 		}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 5c4039ad052..d2b941cbc0a 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -121,8 +121,8 @@ static void _req_is_done(struct drbd_conf *mdev, struct drbd_request *req, const
 		      list_empty(&req->w.list))) {
 			/* DEBUG ASSERT only; if this triggers, we
 			 * probably corrupt the worker list here */
-			DUMPP(req->w.list.next);
-			DUMPP(req->w.list.prev);
+			dev_err(DEV, "req->w.list.next = %p\n", req->w.list.next);
+			dev_err(DEV, "req->w.list.prev = %p\n", req->w.list.prev);
 		}
 		req->w.cb = w_io_error;
 		drbd_queue_work(&mdev->data.work, &req->w);
@@ -326,7 +326,7 @@ void _req_may_be_done(struct drbd_request *req, int error)
  * second hlist_for_each_entry becomes a noop. This is even simpler than to
  * grab a reference on the net_conf, and check for the two_primaries flag...
  */
-STATIC int _req_conflicts(struct drbd_request *req)
+static int _req_conflicts(struct drbd_request *req)
 {
 	struct drbd_conf *mdev = req->mdev;
 	const sector_t sector = req->sector;
@@ -689,7 +689,7 @@ void _req_mod(struct drbd_request *req, enum drbd_req_event what, int error)
  *   since size may be bigger than BM_BLOCK_SIZE,
  *   we may need to check several bits.
  */
-STATIC int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int size)
+static int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int size)
 {
 	unsigned long sbnr, ebnr;
 	sector_t esector, nr_sectors;
@@ -713,7 +713,7 @@ STATIC int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int s
 	return 0 == drbd_bm_count_bits(mdev, sbnr, ebnr);
 }
 
-STATIC int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio)
+static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio)
 {
 	const int rw = bio_rw(bio);
 	const int size = bio->bi_size;
diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c
index b230693f35e..09922d2d5bf 100644
--- a/drivers/block/drbd/drbd_strings.c
+++ b/drivers/block/drbd/drbd_strings.c
@@ -71,13 +71,13 @@ static const char *drbd_disk_s_names[] = {
 static const char *drbd_state_sw_errors[] = {
 	[-SS_TWO_PRIMARIES] = "Multiple primaries not allowed by config",
 	[-SS_NO_UP_TO_DATE_DISK] = "Refusing to be Primary without at least one UpToDate disk",
-	[-SS_BOTH_INCONSISTENT] = "Refusing to be inconsistent on both nodes",
-	[-SS_SYNCING_DISKLESS] = "Refusing to be syncing and diskless",
+	[-SS_NO_LOCAL_DISK] = "Can not resync without local disk",
+	[-SS_NO_REMOTE_DISK] = "Can not resync without remote disk",
 	[-SS_CONNECTED_OUTDATES] = "Refusing to be Outdated while Connected",
 	[-SS_PRIMARY_NOP] = "Refusing to be Primary while peer is not outdated",
 	[-SS_RESYNC_RUNNING] = "Can not start OV/resync since it is already active",
 	[-SS_ALREADY_STANDALONE] = "Can not disconnect a StandAlone device",
-	[-SS_CW_FAILED_BY_PEER] = "State changed was refused by peer node",
+	[-SS_CW_FAILED_BY_PEER] = "State change was refused by peer node",
 	[-SS_IS_DISKLESS] = "Device is diskless, the requesed operation requires a disk",
 	[-SS_DEVICE_IN_USE] = "Device is held open by someone",
 	[-SS_NO_NET_CONFIG] = "Have no net/connection configuration",
diff --git a/drivers/block/drbd/drbd_tracing.c b/drivers/block/drbd/drbd_tracing.c
index b467e92dda7..f2827209ca3 100644
--- a/drivers/block/drbd/drbd_tracing.c
+++ b/drivers/block/drbd/drbd_tracing.c
@@ -71,7 +71,7 @@ enum dbg_print_flags {
 };
 
 /* Macro stuff */
-STATIC char *nl_packet_name(int packet_type)
+static char *nl_packet_name(int packet_type)
 {
 /* Generate packet type strings */
 #define NL_PACKET(name, number, fields) \
@@ -371,7 +371,7 @@ static void probe_drbd_resync(struct drbd_conf *mdev, int level, const char *fmt
 static void probe_drbd_bio(struct drbd_conf *mdev, const char *pfx, struct bio *bio, int complete,
 			   struct drbd_request *r)
 {
-#ifdef CONFIG_LBD
+#if defined(CONFIG_LBDAF) || defined(CONFIG_LBD)
 #define SECTOR_FORMAT "%Lx"
 #else
 #define SECTOR_FORMAT "%lx"
@@ -387,7 +387,7 @@ static void probe_drbd_bio(struct drbd_conf *mdev, const char *pfx, struct bio *
 	const int rw = bio->bi_rw;
 	const int biorw      = (rw & (RW_MASK|RWA_MASK));
 	const int biobarrier = (rw & (1<<BIO_RW_BARRIER));
-	const int biosync    = (rw & ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO)));
+	const int biosync = (rw & ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO)));
 
 	if (!is_mdev_trace(mdev, TRACE_LVL_ALWAYS))
 		return;
@@ -504,7 +504,7 @@ do {								\
 	}							\
 } while (0)
 
-STATIC char *dump_st(char *p, int len, union drbd_state mask, union drbd_state val)
+static char *dump_st(char *p, int len, union drbd_state mask, union drbd_state val)
 {
 	char *op = p;
 	*p = '\0';
@@ -531,7 +531,7 @@ do { \
 	} \
 } while (0)
 
-STATIC char *_dump_block_id(u64 block_id, char *buff)
+static char *_dump_block_id(u64 block_id, char *buff)
 {
 	if (is_syncer_block_id(block_id))
 		strcpy(buff, "SyncerId");
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 96065835fb6..29c5bba8899 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -26,12 +26,11 @@
 #include <linux/autoconf.h>
 #include <linux/module.h>
 #include <linux/version.h>
-
+#include <linux/drbd.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
 #include <linux/wait.h>
 #include <linux/mm.h>
-#include <linux/drbd_config.h>
 #include <linux/memcontrol.h>
 #include <linux/mm_inline.h>
 #include <linux/slab.h>
@@ -40,14 +39,13 @@
 #include <linux/string.h>
 #include <linux/scatterlist.h>
 
-#include <linux/drbd.h>
 #include "drbd_int.h"
 #include "drbd_req.h"
 #include "drbd_tracing.h"
 
 #define SLEEP_TIME (HZ/10)
 
-STATIC int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel);
+static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel);
 
 
 
@@ -293,7 +291,7 @@ int w_resync_inactive(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 	return 1; /* Simply ignore this! */
 }
 
-STATIC void drbd_csum(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *bio, void *digest)
+void drbd_csum(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *bio, void *digest)
 {
 	struct hash_desc desc;
 	struct scatterlist sg;
@@ -313,7 +311,7 @@ STATIC void drbd_csum(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bi
 	crypto_hash_final(&desc, digest);
 }
 
-STATIC int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+static int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 {
 	struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
 	int digest_size;
@@ -329,7 +327,7 @@ STATIC int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel
 
 	if (likely(drbd_bio_uptodate(e->private_bio))) {
 		digest_size = crypto_hash_digestsize(mdev->csums_tfm);
-		digest = kmalloc(digest_size, GFP_KERNEL);
+		digest = kmalloc(digest_size, GFP_NOIO);
 		if (digest) {
 			drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest);
 
@@ -359,7 +357,7 @@ STATIC int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel
 
 #define GFP_TRY	(__GFP_HIGHMEM | __GFP_NOWARN)
 
-STATIC int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
+static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
 {
 	struct drbd_epoch_entry *e;
 
@@ -421,9 +419,9 @@ int w_make_resync_request(struct drbd_conf *mdev,
 	unsigned long bit;
 	sector_t sector;
 	const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
-	int max_segment_size = mdev->rq_queue->max_segment_size;
-	int number, i, size;
-	int align;
+	int max_segment_size = queue_max_segment_size(mdev->rq_queue);
+	int number, i, size, pe, mx;
+	int align, queued, sndbuf;
 
 	if (unlikely(cancel))
 		return 1;
@@ -446,15 +444,40 @@ int w_make_resync_request(struct drbd_conf *mdev,
 		mdev->resync_work.cb = w_resync_inactive;
 		return 1;
 	}
-	/* All goto requeses have to happend after this block: get_ldev() */
 
-	number = SLEEP_TIME*mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ);
+	number = SLEEP_TIME * mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ);
+	pe = atomic_read(&mdev->rs_pending_cnt);
 
-	if (atomic_read(&mdev->rs_pending_cnt) > number)
-		goto requeue;
-	number -= atomic_read(&mdev->rs_pending_cnt);
+	mutex_lock(&mdev->data.mutex);
+	if (mdev->data.socket)
+		mx = mdev->data.socket->sk->sk_rcvbuf / sizeof(struct p_block_req);
+	else
+		mx = 1;
+	mutex_unlock(&mdev->data.mutex);
+
+	/* For resync rates >160MB/sec, allow more pending RS requests */
+	if (number > mx)
+		mx = number;
+
+	/* Limit the nunber of pending RS requests to no more than the peer's receive buffer */
+	if ((pe + number) > mx) {
+		number = mx - pe;
+	}
 
 	for (i = 0; i < number; i++) {
+		/* Stop generating RS requests, when half of the sendbuffer is filled */
+		mutex_lock(&mdev->data.mutex);
+		if (mdev->data.socket) {
+			queued = mdev->data.socket->sk->sk_wmem_queued;
+			sndbuf = mdev->data.socket->sk->sk_sndbuf;
+		} else {
+			queued = 1;
+			sndbuf = 0;
+		}
+		mutex_unlock(&mdev->data.mutex);
+		if (queued > sndbuf / 2)
+			goto requeue;
+
 next_sector:
 		size = BM_BLOCK_SIZE;
 		bit  = drbd_bm_find_next(mdev, mdev->bm_resync_fo);
@@ -589,6 +612,11 @@ int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 
 	sector = mdev->ov_position;
 	for (i = 0; i < number; i++) {
+		if (sector >= capacity) {
+			mdev->resync_work.cb = w_resync_inactive;
+			return 1;
+		}
+
 		size = BM_BLOCK_SIZE;
 
 		if (drbd_try_rs_begin_io(mdev, sector)) {
@@ -605,11 +633,6 @@ int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 			return 0;
 		}
 		sector += BM_SECT_PER_BIT;
-		if (sector >= capacity) {
-			mdev->resync_work.cb = w_resync_inactive;
-
-			return 1;
-		}
 	}
 	mdev->ov_position = sector;
 
@@ -628,7 +651,7 @@ int w_ov_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 	return 1;
 }
 
-STATIC int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+static int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 {
 	kfree(w);
 
@@ -766,6 +789,7 @@ out:
 	mdev->rs_total  = 0;
 	mdev->rs_failed = 0;
 	mdev->rs_paused = 0;
+	mdev->ov_start_sector = 0;
 
 	if (test_and_clear_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags)) {
 		dev_warn(DEV, "Writing the whole bitmap, due to failed kmalloc\n");
@@ -911,7 +935,7 @@ int w_e_end_csum_rs_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 		if (mdev->csums_tfm) {
 			digest_size = crypto_hash_digestsize(mdev->csums_tfm);
 			D_ASSERT(digest_size == di->digest_size);
-			digest = kmalloc(digest_size, GFP_KERNEL);
+			digest = kmalloc(digest_size, GFP_NOIO);
 		}
 		if (digest) {
 			drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest);
@@ -967,13 +991,15 @@ int w_e_end_ov_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 		goto out;
 
 	digest_size = crypto_hash_digestsize(mdev->verify_tfm);
-	digest = kmalloc(digest_size, GFP_KERNEL);
+	/* FIXME if this allocation fails, online verify will not terminate! */
+	digest = kmalloc(digest_size, GFP_NOIO);
 	if (digest) {
 		drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest);
+		inc_rs_pending(mdev);
 		ok = drbd_send_drequest_csum(mdev, e->sector, e->size,
 					     digest, digest_size, P_OV_REPLY);
-		if (ok)
-			inc_rs_pending(mdev);
+		if (!ok)
+			dec_rs_pending(mdev);
 		kfree(digest);
 	}
 
@@ -1021,7 +1047,7 @@ int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 
 	if (likely(drbd_bio_uptodate(e->private_bio))) {
 		digest_size = crypto_hash_digestsize(mdev->verify_tfm);
-		digest = kmalloc(digest_size, GFP_KERNEL);
+		digest = kmalloc(digest_size, GFP_NOIO);
 		if (digest) {
 			drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest);
 
@@ -1157,7 +1183,7 @@ int w_send_read_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 	return ok;
 }
 
-STATIC int _drbd_may_sync_now(struct drbd_conf *mdev)
+static int _drbd_may_sync_now(struct drbd_conf *mdev)
 {
 	struct drbd_conf *odev = mdev;
 
@@ -1180,7 +1206,7 @@ STATIC int _drbd_may_sync_now(struct drbd_conf *mdev)
  *
  * Called from process context only (admin command and after_state_ch).
  */
-STATIC int _drbd_pause_after(struct drbd_conf *mdev)
+static int _drbd_pause_after(struct drbd_conf *mdev)
 {
 	struct drbd_conf *odev;
 	int i, rv = 0;
@@ -1205,7 +1231,7 @@ STATIC int _drbd_pause_after(struct drbd_conf *mdev)
  *
  * Called from process context only (admin command and worker).
  */
-STATIC int _drbd_resume_next(struct drbd_conf *mdev)
+static int _drbd_resume_next(struct drbd_conf *mdev)
 {
 	struct drbd_conf *odev;
 	int i, rv = 0;
@@ -1240,19 +1266,46 @@ void suspend_other_sg(struct drbd_conf *mdev)
 	write_unlock_irq(&global_state_lock);
 }
 
-void drbd_alter_sa(struct drbd_conf *mdev, int na)
+static int sync_after_error(struct drbd_conf *mdev, int o_minor)
 {
-	int changes;
+	struct drbd_conf *odev;
 
-	write_lock_irq(&global_state_lock);
-	mdev->sync_conf.after = na;
+	if (o_minor == -1)
+		return NO_ERROR;
+	if (o_minor < -1 || minor_to_mdev(o_minor) == NULL)
+		return ERR_SYNC_AFTER;
+
+	/* check for loops */
+	odev = minor_to_mdev(o_minor);
+	while (1) {
+		if (odev == mdev)
+			return ERR_SYNC_AFTER_CYCLE;
 
-	do {
-		changes  = _drbd_pause_after(mdev);
-		changes |= _drbd_resume_next(mdev);
-	} while (changes);
+		/* dependency chain ends here, no cycles. */
+		if (odev->sync_conf.after == -1)
+			return NO_ERROR;
 
+		/* follow the dependency chain */
+		odev = minor_to_mdev(odev->sync_conf.after);
+	}
+}
+
+int drbd_alter_sa(struct drbd_conf *mdev, int na)
+{
+	int changes;
+	int retcode;
+
+	write_lock_irq(&global_state_lock);
+	retcode = sync_after_error(mdev, na);
+	if (retcode == NO_ERROR) {
+		mdev->sync_conf.after = na;
+		do {
+			changes  = _drbd_pause_after(mdev);
+			changes |= _drbd_resume_next(mdev);
+		} while (changes);
+	}
 	write_unlock_irq(&global_state_lock);
+	return retcode;
 }
 
 /**
@@ -1268,6 +1321,11 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
 	union drbd_state ns;
 	int r;
 
+	if (mdev->state.conn >= C_SYNC_SOURCE) {
+		dev_err(DEV, "Resync already running!\n");
+		return;
+	}
+
 	trace_drbd_resync(mdev, TRACE_LVL_SUMMARY, "Resync starting: side=%s\n",
 			  side == C_SYNC_TARGET ? "SyncTarget" : "SyncSource");
 
diff --git a/drivers/block/drbd/drbd_wrappers.h b/drivers/block/drbd/drbd_wrappers.h
index 724fb44aad0..f93fa111ce5 100644
--- a/drivers/block/drbd/drbd_wrappers.h
+++ b/drivers/block/drbd/drbd_wrappers.h
@@ -7,11 +7,6 @@
 /* see get_sb_bdev and bd_claim */
 extern char *drbd_sec_holder;
 
-static inline sector_t drbd_get_hardsect_size(struct block_device *bdev)
-{
-	return bdev->bd_disk->queue->hardsect_size;
-}
-
 /* sets the number of 512 byte sectors of our virtual device */
 static inline void drbd_set_my_capacity(struct drbd_conf *mdev,
 					sector_t size)
diff --git a/include/linux/drbd.h b/include/linux/drbd.h
index 250002101e4..dc478c648e3 100644
--- a/include/linux/drbd.h
+++ b/include/linux/drbd.h
@@ -26,7 +26,6 @@
 #ifndef DRBD_H
 #define DRBD_H
 #include <linux/connector.h>
-
 #include <asm/types.h>
 
 #ifdef __KERNEL__
@@ -53,6 +52,13 @@
 #endif
 
 
+extern const char *drbd_buildtag(void);
+#define REL_VERSION "8.3.2rc2"
+#define API_VERSION 88
+#define PRO_VERSION_MIN 86
+#define PRO_VERSION_MAX 90
+
+
 enum drbd_io_error_p {
 	EP_PASS_ON, /* FIXME should the better be named "Ignore"? */
 	EP_CALL_HELPER,
@@ -171,8 +177,8 @@ enum drbd_conns {
 	C_WF_CONNECTION,
 	C_WF_REPORT_PARAMS, /* we have a socket */
 	C_CONNECTED,      /* we have introduced each other */
-	C_STARTING_SYNC_S,  /* starting full sync by IOCTL. */
-	C_STARTING_SYNC_T,  /* stariing full sync by IOCTL. */
+	C_STARTING_SYNC_S,  /* starting full sync by admin request. */
+	C_STARTING_SYNC_T,  /* stariing full sync by admin request. */
 	C_WF_BITMAP_S,
 	C_WF_BITMAP_T,
 	C_WF_SYNC_UUID,
@@ -249,8 +255,8 @@ enum drbd_state_ret_codes {
 	SS_UNKNOWN_ERROR = 0, /* Used to sleep longer in _drbd_request_state */
 	SS_TWO_PRIMARIES = -1,
 	SS_NO_UP_TO_DATE_DISK = -2,
-	SS_BOTH_INCONSISTENT = -4,
-	SS_SYNCING_DISKLESS = -5,
+	SS_NO_LOCAL_DISK = -4,
+	SS_NO_REMOTE_DISK = -5,
 	SS_CONNECTED_OUTDATES = -6,
 	SS_PRIMARY_NOP = -7,
 	SS_RESYNC_RUNNING = -8,
diff --git a/include/linux/drbd_config.h b/include/linux/drbd_config.h
deleted file mode 100644
index 06a750ed58b..00000000000
--- a/include/linux/drbd_config.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-  drbd_config.h
-  DRBD's compile time configuration.
-
-  drbd is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2, or (at your option)
-  any later version.
-
-  drbd is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with drbd; see the file COPYING.  If not, write to
-  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef DRBD_CONFIG_H
-#define DRBD_CONFIG_H
-
-extern const char *drbd_buildtag(void);
-
-#define REL_VERSION "8.3.1"
-#define API_VERSION 88
-#define PRO_VERSION_MIN 86
-#define PRO_VERSION_MAX 90
-
-#ifndef __CHECKER__   /* for a sparse run, we need all STATICs */
-#define DBG_ALL_SYMBOLS /* no static functs, improves quality of OOPS traces */
-#endif
-
-/* Enable fault insertion code */
-#define DRBD_ENABLE_FAULTS
-
-#endif
diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h
index 2fafc2b9cdb..9d067ce4696 100644
--- a/include/linux/drbd_limits.h
+++ b/include/linux/drbd_limits.h
@@ -72,6 +72,10 @@
 #define DRBD_SNDBUF_SIZE_MAX  (10<<20)
 #define DRBD_SNDBUF_SIZE_DEF  (2*65535)
 
+#define DRBD_RCVBUF_SIZE_MIN  0
+#define DRBD_RCVBUF_SIZE_MAX  (10<<20)
+#define DRBD_RCVBUF_SIZE_DEF  (2*65535)
+
   /* @4k PageSize -> 128kB - 512MB */
 #define DRBD_MAX_BUFFERS_MIN  32
 #define DRBD_MAX_BUFFERS_MAX  131072
diff --git a/include/linux/drbd_nl.h b/include/linux/drbd_nl.h
index cc99f3ecd8c..db5721ad50d 100644
--- a/include/linux/drbd_nl.h
+++ b/include/linux/drbd_nl.h
@@ -55,6 +55,7 @@ NL_PACKET(net_conf, 5,
 	NL_INTEGER(	26,	T_MAY_IGNORE,	after_sb_2p)
 	NL_INTEGER(	39,	T_MAY_IGNORE,	rr_conflict)
 	NL_INTEGER(	40,	T_MAY_IGNORE,	ping_timeo)
+	NL_INTEGER(	67,	T_MAY_IGNORE,	rcvbuf_size)
 	  /* 59 addr_family was available in GIT, never released */
 	NL_BIT(		60,	T_MANDATORY,	mind_af)
 	NL_BIT(		27,	T_MAY_IGNORE,	want_lose)
@@ -77,7 +78,7 @@ NL_PACKET(syncer_conf, 8,
 	NL_STRING(      52,     T_MAY_IGNORE,   verify_alg,     SHARED_SECRET_MAX)
 	NL_STRING(      51,     T_MAY_IGNORE,   cpu_mask,       32)
 	NL_STRING(	64,	T_MAY_IGNORE,	csums_alg,	SHARED_SECRET_MAX)
-	NL_BIT(         65,     T_MAY_IGNORE,   use_rle_encoding)
+	NL_BIT(         65,     T_MAY_IGNORE,   use_rle)
 )
 
 NL_PACKET(invalidate, 9, )
@@ -121,6 +122,7 @@ NL_PACKET(dump_ee, 24,
 )
 
 NL_PACKET(start_ov, 25,
+	NL_INT64(	66,	T_MAY_IGNORE,	start_sector)
 )
 
 NL_PACKET(new_c_uuid, 26,
diff --git a/include/linux/lru_cache.h b/include/linux/lru_cache.h
index 69e2455b00b..3a2b2d9b047 100644
--- a/include/linux/lru_cache.h
+++ b/include/linux/lru_cache.h
@@ -27,6 +27,10 @@
 #define LRU_CACHE_H
 
 #include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/string.h> /* for memset */
+#include <linux/seq_file.h>
 
 /*
 This header file (and its .c file; kernel-doc of functions see there)
@@ -142,22 +146,29 @@ write intent log information, three of which are mentioned here.
  * an element is said to be "in the active set",
  * if either on "in_use" or "lru", i.e. lc_number != LC_FREE.
  *
- * DRBD currently only uses 61 elements on the resync lru_cache (total memory
- * usage 2 pages), and up to 3833 elements on the act_log lru_cache, totalling
- * ~215 kB for 64bit architechture, ~53 pages.
+ * DRBD currently (May 2009) only uses 61 elements on the resync lru_cache
+ * (total memory usage 2 pages), and up to 3833 elements on the act_log
+ * lru_cache, totalling ~215 kB for 64bit architechture, ~53 pages.
  *
  * We usually do not actually free these objects again, but only "recycle"
  * them, as the change "index: -old_label, +LC_FREE" would need a transaction
- * as well.  Which also means that using a kmem_cache or even mempool to
- * allocate the objects from wastes some resources. But it would avoid high
- * order page allocations in kmalloc, so we may change to a kmem_cache backed
- * allocation of the elements in the near future.
+ * as well.  Which also means that using a kmem_cache to allocate the objects
+ * from wastes some resources.
+ * But it avoids high order page allocations in kmalloc.
  */
 struct lc_element {
 	struct hlist_node colision;
 	struct list_head list;		 /* LRU list or free list */
-	unsigned int refcnt;
-	unsigned int lc_number;
+	unsigned refcnt;
+	/* back "pointer" into ts_cache->element[index],
+	 * for paranoia, and for "ts_element_to_index" */
+	unsigned lc_index;
+	/* if we want to track a larger set of objects,
+	 * it needs to become arch independend u64 */
+	unsigned lc_number;
+
+	/* special label when on free list */
+#define LC_FREE (~0U)
 };
 
 struct lru_cache {
@@ -166,16 +177,25 @@ struct lru_cache {
 	struct list_head free;
 	struct list_head in_use;
 
-	/* size of tracked objects */
+	/* the pre-created kmem cache to allocate the objects from */
+	struct kmem_cache *lc_cache;
+
+	/* size of tracked objects, used to memset(,0,) them in lc_reset */
 	size_t element_size;
 	/* offset of struct lc_element member in the tracked object */
 	size_t element_off;
 
 	/* number of elements (indices) */
 	unsigned int  nr_elements;
+	/* Arbitrary limit on maximum tracked objects. Practical limit is much
+	 * lower due to allocation failures, probably. For typical use cases,
+	 * nr_elements should be a few thousand at most.
+	 * This also limits the maximum value of ts_element.ts_index, allowing the
+	 * 8 high bits of .ts_index to be overloaded with flags in the future. */
+#define LC_MAX_ACTIVE	(1<<24)
 
 	/* statistics */
-	unsigned int used;
+	unsigned used; /* number of lelements currently on in_use list */
 	unsigned long hits, misses, starving, dirty, changed;
 
 	/* see below: flag-bits for lru_cache */
@@ -190,8 +210,9 @@ struct lru_cache {
 	void  *lc_private;
 	const char *name;
 
-	struct hlist_head slot[0];
-	/* hash colision chains here, then element storage. */
+	/* nr_elements there */
+	struct hlist_head *lc_slot;
+	struct lc_element **lc_element;
 };
 
 
@@ -217,8 +238,8 @@ enum {
 #define LC_DIRTY    (1<<__LC_DIRTY)
 #define LC_STARVING (1<<__LC_STARVING)
 
-extern struct lru_cache *lc_create(const char *name, unsigned int e_count,
-				  size_t e_size, size_t e_off);
+extern struct lru_cache *lc_create(const char *name, struct kmem_cache *cache,
+		unsigned e_count, size_t e_size, size_t e_off);
 extern void lc_reset(struct lru_cache *lc);
 extern void lc_destroy(struct lru_cache *lc);
 extern void lc_set(struct lru_cache *lc, unsigned int enr, int index);
@@ -236,15 +257,22 @@ extern size_t lc_seq_printf_stats(struct seq_file *seq, struct lru_cache *lc);
 extern void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext,
 				void (*detail) (struct seq_file *, struct lc_element *));
 
-/* This can be used to stop lc_get from changing the set of active elements.
- * Note that the reference counts and order on the lru list may still change.
- * returns true if we aquired the lock.
+/**
+ * lc_try_lock - can be used to stop lc_get() from changing the tracked set
+ * @lc: the lru cache to operate on
+ *
+ * Note that the reference counts and order on the active and lru lists may
+ * still change.  Returns true if we aquired the lock.
  */
 static inline int lc_try_lock(struct lru_cache *lc)
 {
 	return !test_and_set_bit(__LC_DIRTY, &lc->flags);
 }
 
+/**
+ * lc_unlock - unlock @lc, allow lc_get() to change the set again
+ * @lc: the lru cache to operate on
+ */
 static inline void lc_unlock(struct lru_cache *lc)
 {
 	clear_bit(__LC_DIRTY, &lc->flags);
@@ -257,29 +285,10 @@ static inline int lc_is_used(struct lru_cache *lc, unsigned int enr)
 	return e && e->refcnt;
 }
 
-#define LC_FREE (-1U)
-
 #define lc_entry(ptr, type, member) \
 	container_of(ptr, type, member)
 
-static inline struct lc_element *
-lc_element_by_index(struct lru_cache *lc, unsigned int i)
-{
-	BUG_ON(i >= lc->nr_elements);
-	return (struct lc_element *)(
-			((char *)(lc->slot + lc->nr_elements)) +
-			i * lc->element_size
-			+ lc->element_off);
-}
-
-static inline size_t lc_index_of(struct lru_cache *lc, struct lc_element *e)
-{
-	size_t i = ((char *)(e) - lc->element_off
-		- ((char *)(lc->slot + lc->nr_elements)))
-		/ lc->element_size;
-	BUG_ON(i >= lc->nr_elements);
-	BUG_ON(e != lc_element_by_index(lc, i));
-	return i;
-}
+extern struct lc_element *lc_element_by_index(struct lru_cache *lc, unsigned i);
+extern unsigned int lc_index_of(struct lru_cache *lc, struct lc_element *e);
 
 #endif
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index f8632f1f7f7..ab11a710b6e 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -30,78 +30,134 @@
 #include <linux/seq_file.h> /* for seq_printf */
 #include <linux/lru_cache.h>
 
-/* this is developers aid only! */
-#define PARANOIA_ENTRY() BUG_ON(test_and_set_bit(__LC_PARANOIA, &lc->flags))
-#define PARANOIA_LEAVE() do { clear_bit(__LC_PARANOIA, &lc->flags); smp_mb__after_clear_bit(); } while (0)
-#define RETURN(x...)     do { PARANOIA_LEAVE(); return x ; } while (0)
+MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, "
+	      "Lars Ellenberg <lars@linbit.com>");
+MODULE_DESCRIPTION("lru_cache - Track sets of hot objects");
+MODULE_LICENSE("GPL");
+
+/* this is developers aid only.
+ * it catches concurrent access (lack of locking on the users part) */
+#define PARANOIA_ENTRY() do {		\
+	BUG_ON(!lc);			\
+	BUG_ON(!lc->nr_elements);	\
+	BUG_ON(test_and_set_bit(__LC_PARANOIA, &lc->flags)); \
+} while (0)
+
+#define RETURN(x...)     do { \
+	clear_bit(__LC_PARANOIA, &lc->flags); \
+	smp_mb__after_clear_bit(); return x ; } while (0)
+
+/* BUG() if e is not one of the elements tracked by lc */
+#define PARANOIA_LC_ELEMENT(lc, e) do {	\
+	struct lru_cache *lc_ = (lc);	\
+	struct lc_element *e_ = (e);	\
+	unsigned i = e_->lc_index;	\
+	BUG_ON(i >= lc_->nr_elements);	\
+	BUG_ON(lc_->lc_element[i] != e_); } while (0)
 
-static size_t size_of_lc(unsigned int e_count, size_t e_size)
-{
-	return sizeof(struct lru_cache)
-	     + e_count * (e_size + sizeof(struct hlist_head));
-}
-
-static void lc_init(struct lru_cache *lc,
-		const size_t bytes, const char *name,
-		const unsigned int e_count, const size_t e_size,
-		const size_t e_off)
+/**
+ * lc_create - prepares to track objects in an active set
+ * @name: descriptive name only used in lc_seq_printf_stats and lc_seq_dump_details
+ * @e_count: number of elements allowed to be active simultaneously
+ * @e_size: size of the tracked objects
+ * @e_off: offset to the &struct lc_element member in a tracked object
+ *
+ * Returns a pointer to a newly initialized struct lru_cache on success,
+ * or NULL on (allocation) failure.
+ */
+struct lru_cache *lc_create(const char *name, struct kmem_cache *cache,
+		unsigned e_count, size_t e_size, size_t e_off)
 {
+	struct hlist_head *slot = NULL;
+	struct lc_element **element = NULL;
+	struct lru_cache *lc;
 	struct lc_element *e;
-	unsigned int i;
+	unsigned cache_obj_size = kmem_cache_size(cache);
+	unsigned i;
 
-	BUG_ON(!e_count);
+	WARN_ON(cache_obj_size < e_size);
+	if (cache_obj_size < e_size)
+		return NULL;
+
+	/* e_count too big; would probably fail the allocation below anyways.
+	 * for typical use cases, e_count should be few thousand at most. */
+	if (e_count > LC_MAX_ACTIVE)
+		return NULL;
+
+	slot = kzalloc(e_count * sizeof(struct hlist_head*), GFP_KERNEL);
+	if (!slot)
+		goto out_fail;
+	element = kzalloc(e_count * sizeof(struct lc_element *), GFP_KERNEL);
+	if (!element)
+		goto out_fail;
+
+	lc = kzalloc(sizeof(*lc), GFP_KERNEL);
+	if (!lc)
+		goto out_fail;
 
-	memset(lc, 0, bytes);
 	INIT_LIST_HEAD(&lc->in_use);
 	INIT_LIST_HEAD(&lc->lru);
 	INIT_LIST_HEAD(&lc->free);
+
+	lc->name = name;
 	lc->element_size = e_size;
-	lc->element_off  = e_off;
-	lc->nr_elements  = e_count;
-	lc->new_number	 = -1;
-	lc->name         = name;
+	lc->element_off = e_off;
+	lc->nr_elements = e_count;
+	lc->new_number = LC_FREE;
+	lc->lc_cache = cache;
+	lc->lc_element = element;
+	lc->lc_slot = slot;
+
+	/* preallocate all objects */
 	for (i = 0; i < e_count; i++) {
-		e = lc_element_by_index(lc, i);
+		void *p = kmem_cache_alloc(cache, GFP_KERNEL);
+		if (!p)
+			break;
+		memset(p, 0, lc->element_size);
+		e = p + e_off;
+		e->lc_index = i;
 		e->lc_number = LC_FREE;
 		list_add(&e->list, &lc->free);
-		/* memset(,0,) did the rest of init for us */
+		element[i] = e;
+	}
+	if (i == e_count)
+		return lc;
+
+	/* else: could not allocate all elements, give up */
+	for (i--; i; i--) {
+		void *p = element[i];
+		kmem_cache_free(cache, p - e_off);
 	}
+	kfree(lc);
+out_fail:
+	kfree(element);
+	kfree(slot);
+	return NULL;
 }
 
-/**
- * lc_create - prepares to track objects in an active set
- * @name: descriptive name only used in lc_seq_printf_stats and lc_seq_dump
- * @e_count: number of elements allowed to be active simultaneously
- * @e_size: size of the tracked objects
- * @e_off: offset to the &struct lc_element member in a tracked object
- *
- * Returns a pointer to a newly initialized struct lru_cache on success,
- * or NULL on (allocation) failure.
- */
-struct lru_cache *lc_create(const char *name, unsigned int e_count,
-			   size_t e_size, size_t e_off)
+void lc_free_by_index(struct lru_cache *lc, unsigned i)
 {
-	struct lru_cache   *lc;
-	size_t bytes;
-
-	BUG_ON(!e_count);
-	BUG_ON(e_size < sizeof(struct lc_element));
-	BUG_ON(e_size - sizeof(struct lc_element) < e_off);
-	e_size = ALIGN(e_size, sizeof(void *));
-	e_size = max(sizeof(struct lc_element), e_size);
-	bytes = size_of_lc(e_count, e_size);
-	lc = kmalloc(bytes, GFP_KERNEL);
-	if (lc)
-		lc_init(lc, bytes, name, e_count, e_size, e_off);
-	return lc;
+	void *p = lc->lc_element[i];
+	WARN_ON(!p);
+	if (p) {
+		p -= lc->element_off;
+		kmem_cache_free(lc->lc_cache, p);
+	}
 }
 
 /**
  * lc_destroy - frees memory allocated by lc_create()
- * @lc: the lru cache to operate on
+ * @lc: the lru cache to destroy
  */
 void lc_destroy(struct lru_cache *lc)
 {
+	unsigned i;
+	if (!lc)
+		return;
+	for (i = 0; i < lc->nr_elements; i++)
+		lc_free_by_index(lc, i);
+	kfree(lc->lc_element);
+	kfree(lc->lc_slot);
 	kfree(lc);
 }
 
@@ -114,14 +170,38 @@ void lc_destroy(struct lru_cache *lc)
  */
 void lc_reset(struct lru_cache *lc)
 {
-	lc_init(lc, size_of_lc(lc->nr_elements, lc->element_size), lc->name,
-			lc->nr_elements, lc->element_size, lc->element_off);
+	unsigned i;
+
+	INIT_LIST_HEAD(&lc->in_use);
+	INIT_LIST_HEAD(&lc->lru);
+	INIT_LIST_HEAD(&lc->free);
+	lc->used = 0;
+	lc->hits = 0;
+	lc->misses = 0;
+	lc->starving = 0;
+	lc->dirty = 0;
+	lc->changed = 0;
+	lc->flags = 0;
+	lc->changing_element = NULL;
+	lc->new_number = LC_FREE;
+	memset(lc->lc_slot, 0, sizeof(struct hlist_head) * lc->nr_elements);
+
+	for (i = 0; i < lc->nr_elements; i++) {
+		struct lc_element *e = lc->lc_element[i];
+		void *p = e;
+		p -= lc->element_off;
+		memset(p, 0, lc->element_size);
+		/* re-init it */
+		e->lc_index = i;
+		e->lc_number = LC_FREE;
+		list_add(&e->list, &lc->free);
+	}
 }
 
 /**
- * lc_seq_printf_stats - print stats about @ts into @seq
+ * lc_seq_printf_stats - print stats about @lc into @seq
  * @seq: the seq_file to print into
- * @ts: the tracked set to print statistics of
+ * @lc: the lru cache to print statistics of
  */
 size_t lc_seq_printf_stats(struct seq_file *seq, struct lru_cache *lc)
 {
@@ -138,9 +218,9 @@ size_t lc_seq_printf_stats(struct seq_file *seq, struct lru_cache *lc)
 		lc->hits, lc->misses, lc->starving, lc->dirty, lc->changed);
 }
 
-static unsigned int lc_hash_fn(struct lru_cache *lc, unsigned int enr)
+static struct hlist_head *lc_hash_slot(struct lru_cache *lc, unsigned int enr)
 {
-	return enr % lc->nr_elements;
+	return  lc->lc_slot + (enr % lc->nr_elements);
 }
 
 
@@ -159,7 +239,8 @@ struct lc_element *lc_find(struct lru_cache *lc, unsigned int enr)
 	struct lc_element *e;
 
 	BUG_ON(!lc);
-	hlist_for_each_entry(e, n, lc->slot + lc_hash_fn(lc, enr), colision) {
+	BUG_ON(!lc->nr_elements);
+	hlist_for_each_entry(e, n, lc_hash_slot(lc, enr), colision) {
 		if (e->lc_number == enr)
 			return e;
 	}
@@ -178,6 +259,8 @@ static struct lc_element *lc_evict(struct lru_cache *lc)
 	n = lc->lru.prev;
 	e = list_entry(n, struct lc_element, list);
 
+	PARANOIA_LC_ELEMENT(lc, e);
+
 	list_del(&e->list);
 	hlist_del(&e->colision);
 	return e;
@@ -194,14 +277,12 @@ static struct lc_element *lc_evict(struct lru_cache *lc)
 void lc_del(struct lru_cache *lc, struct lc_element *e)
 {
 	PARANOIA_ENTRY();
-	BUG_ON(e < lc_element_by_index(lc, 0));
-	BUG_ON(e > lc_element_by_index(lc, lc->nr_elements-1));
+	PARANOIA_LC_ELEMENT(lc, e);
 	BUG_ON(e->refcnt);
-	list_del(&e->list);
-	hlist_del_init(&e->colision);
+
 	e->lc_number = LC_FREE;
-	e->refcnt = 0;
-	list_add(&e->list, &lc->free);
+	hlist_del_init(&e->colision);
+	list_move(&e->list, &lc->free);
 	RETURN();
 }
 
@@ -243,11 +324,11 @@ static int lc_unused_element_available(struct lru_cache *lc)
  *
  * Return values:
  *  NULL
- *     The cache was marked %TS_STARVING,
+ *     The cache was marked %LC_STARVING,
  *     or the requested label was not in the active set
  *     and a changing transaction is still pending (@lc was marked %LC_DIRTY).
- *     Or no unused or free element could be recycled (@ts will be marked as
- *     %TS_STARVING, blocking further ts_get() operations).
+ *     Or no unused or free element could be recycled (@lc will be marked as
+ *     %LC_STARVING, blocking further lc_get() operations).
  *
  *  pointer to the element with the REQUESTED element number.
  *     In this case, it can be used right away
@@ -269,9 +350,6 @@ struct lc_element *lc_get(struct lru_cache *lc, unsigned int enr)
 {
 	struct lc_element *e;
 
-	BUG_ON(!lc);
-	BUG_ON(!lc->nr_elements);
-
 	PARANOIA_ENTRY();
 	if (lc->flags & LC_STARVING) {
 		++lc->starving;
@@ -328,9 +406,6 @@ struct lc_element *lc_try_get(struct lru_cache *lc, unsigned int enr)
 {
 	struct lc_element *e;
 
-	BUG_ON(!lc);
-	BUG_ON(!lc->nr_elements);
-
 	PARANOIA_ENTRY();
 	if (lc->flags & LC_STARVING) {
 		++lc->starving;
@@ -356,13 +431,13 @@ void lc_changed(struct lru_cache *lc, struct lc_element *e)
 {
 	PARANOIA_ENTRY();
 	BUG_ON(e != lc->changing_element);
+	PARANOIA_LC_ELEMENT(lc, e);
 	++lc->changed;
 	e->lc_number = lc->new_number;
 	list_add(&e->list, &lc->in_use);
-	hlist_add_head(&e->colision,
-		lc->slot + lc_hash_fn(lc, lc->new_number));
+	hlist_add_head(&e->colision, lc_hash_slot(lc, lc->new_number));
 	lc->changing_element = NULL;
-	lc->new_number = -1;
+	lc->new_number = LC_FREE;
 	clear_bit(__LC_DIRTY, &lc->flags);
 	smp_mb__after_clear_bit();
 	RETURN();
@@ -375,16 +450,13 @@ void lc_changed(struct lru_cache *lc, struct lc_element *e)
  * @e: the element to put
  *
  * If refcnt reaches zero, the element is moved to the lru list,
- * and a %TS_STARVING (if set) is cleared.
+ * and a %LC_STARVING (if set) is cleared.
  * Returns the new (post-decrement) refcnt.
  */
 unsigned int lc_put(struct lru_cache *lc, struct lc_element *e)
 {
-	BUG_ON(!lc);
-	BUG_ON(!lc->nr_elements);
-	BUG_ON(!e);
-
 	PARANOIA_ENTRY();
+	PARANOIA_LC_ELEMENT(lc, e);
 	BUG_ON(e->refcnt == 0);
 	BUG_ON(e == lc->changing_element);
 	if (--e->refcnt == 0) {
@@ -397,6 +469,29 @@ unsigned int lc_put(struct lru_cache *lc, struct lc_element *e)
 	RETURN(e->refcnt);
 }
 
+/**
+ * lc_element_by_index
+ * @lc: the lru cache to operate on
+ * @i: the index of the element to return
+ */
+struct lc_element *lc_element_by_index(struct lru_cache *lc, unsigned i)
+{
+	BUG_ON(i >= lc->nr_elements);
+	BUG_ON(lc->lc_element[i] == NULL);
+	BUG_ON(lc->lc_element[i]->lc_index != i);
+	return lc->lc_element[i];
+}
+
+/**
+ * lc_index_of
+ * @lc: the lru cache to operate on
+ * @e: the element to query for its index position in lc->element
+ */
+unsigned int lc_index_of(struct lru_cache *lc, struct lc_element *e)
+{
+	PARANOIA_LC_ELEMENT(lc, e);
+	return e->lc_index;
+}
 
 /**
  * lc_set - associate index with label
@@ -417,7 +512,7 @@ void lc_set(struct lru_cache *lc, unsigned int enr, int index)
 	e->lc_number = enr;
 
 	hlist_del_init(&e->colision);
-	hlist_add_head(&e->colision, lc->slot + lc_hash_fn(lc, enr));
+	hlist_add_head(&e->colision, lc_hash_slot(lc, enr));
 	list_move(&e->list, e->refcnt ? &lc->in_use : &lc->lru);
 }
 
@@ -443,8 +538,7 @@ void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext
 			seq_printf(seq, "\t%2d: FREE\n", i);
 		} else {
 			seq_printf(seq, "\t%2d: %4u %4u    ", i,
-				   e->lc_number,
-				   e->refcnt);
+				   e->lc_number, e->refcnt);
 			detail(seq, e);
 		}
 	}
@@ -460,5 +554,7 @@ EXPORT_SYMBOL(lc_find);
 EXPORT_SYMBOL(lc_get);
 EXPORT_SYMBOL(lc_put);
 EXPORT_SYMBOL(lc_changed);
+EXPORT_SYMBOL(lc_element_by_index);
+EXPORT_SYMBOL(lc_index_of);
 EXPORT_SYMBOL(lc_seq_printf_stats);
 EXPORT_SYMBOL(lc_seq_dump_details);
-- 
2.25.4