drbd: Basic refcounting for drbd_tconn
[firefly-linux-kernel-4.4.55.git] / drivers / block / drbd / drbd_main.c
index 4718aa4e5272c1ac4e2412b47ee8ef024372c941..11427f59c5af538f7155e124bbe4d8b28510aab1 100644 (file)
@@ -74,7 +74,7 @@ MODULE_AUTHOR("Philipp Reisner <phil@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 ("
+MODULE_PARM_DESC(minor_count, "Approximate number of drbd devices ("
                 __stringify(DRBD_MINOR_COUNT_MIN) "-" __stringify(DRBD_MINOR_COUNT_MAX) ")");
 MODULE_ALIAS_BLOCKDEV_MAJOR(DRBD_MAJOR);
 
@@ -86,7 +86,6 @@ MODULE_PARM_DESC(allow_oos, "DONT USE!");
 module_param(minor_count, uint, 0444);
 module_param(disable_sendpage, bool, 0644);
 module_param(allow_oos, bool, 0);
-module_param(cn_idx, uint, 0444);
 module_param(proc_details, int, 0644);
 
 #ifdef CONFIG_DRBD_FAULT_INJECTION
@@ -108,7 +107,6 @@ module_param(fault_devs, int, 0644);
 unsigned int minor_count = DRBD_MINOR_COUNT_DEF;
 int disable_sendpage;
 int allow_oos;
-unsigned int cn_idx = CN_IDX_DRBD;
 int proc_details;       /* Detail level in proc drbd*/
 
 /* Module parameter for setting the user mode helper program
@@ -120,8 +118,9 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0
 /* in 2.6.x, our device mapping and config info contains our virtual gendisks
  * as member "struct gendisk *vdisk;"
  */
-struct drbd_conf **minor_table;
+struct idr minors;
 struct list_head drbd_tconns;  /* list of struct drbd_tconn */
+DECLARE_RWSEM(drbd_cfg_rwsem);
 
 struct kmem_cache *drbd_request_cache;
 struct kmem_cache *drbd_ee_cache;      /* peer requests */
@@ -129,6 +128,8 @@ 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;
+mempool_t *drbd_md_io_page_pool;
+struct bio_set *drbd_md_io_bio_set;
 
 /* I do not use a standard mempool, because:
    1) I want to hand out the pre-allocated objects first.
@@ -149,7 +150,24 @@ static const struct block_device_operations drbd_ops = {
        .release = drbd_release,
 };
 
-#define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+static void bio_destructor_drbd(struct bio *bio)
+{
+       bio_free(bio, drbd_md_io_bio_set);
+}
+
+struct bio *bio_alloc_drbd(gfp_t gfp_mask)
+{
+       struct bio *bio;
+
+       if (!drbd_md_io_bio_set)
+               return bio_alloc(gfp_mask, 1);
+
+       bio = bio_alloc_bioset(gfp_mask, 1, drbd_md_io_bio_set);
+       if (!bio)
+               return NULL;
+       bio->bi_destructor = bio_destructor_drbd;
+       return bio;
+}
 
 #ifdef __CHECKER__
 /* When checking with sparse, and this is an inline function, sparse will
@@ -180,7 +198,7 @@ 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_tconn *tconn)
 {
        struct drbd_tl_epoch *b;
 
@@ -195,21 +213,23 @@ static int tl_init(struct drbd_conf *mdev)
        b->n_writes = 0;
        b->w.cb = NULL; /* if this is != NULL, we need to dec_ap_pending in tl_clear */
 
-       mdev->tconn->oldest_tle = b;
-       mdev->tconn->newest_tle = b;
-       INIT_LIST_HEAD(&mdev->tconn->out_of_sequence_requests);
+       tconn->oldest_tle = b;
+       tconn->newest_tle = b;
+       INIT_LIST_HEAD(&tconn->out_of_sequence_requests);
 
        return 1;
 }
 
-static void tl_cleanup(struct drbd_conf *mdev)
+static void tl_cleanup(struct drbd_tconn *tconn)
 {
-       D_ASSERT(mdev->tconn->oldest_tle == mdev->tconn->newest_tle);
-       D_ASSERT(list_empty(&mdev->tconn->out_of_sequence_requests));
-       kfree(mdev->tconn->oldest_tle);
-       mdev->tconn->oldest_tle = NULL;
-       kfree(mdev->tconn->unused_spare_tle);
-       mdev->tconn->unused_spare_tle = NULL;
+       if (tconn->oldest_tle != tconn->newest_tle)
+               conn_err(tconn, "ASSERT FAILED: oldest_tle == newest_tle\n");
+       if (!list_empty(&tconn->out_of_sequence_requests))
+               conn_err(tconn, "ASSERT FAILED: list_empty(out_of_sequence_requests)\n");
+       kfree(tconn->oldest_tle);
+       tconn->oldest_tle = NULL;
+       kfree(tconn->unused_spare_tle);
+       tconn->unused_spare_tle = NULL;
 }
 
 /**
@@ -219,7 +239,7 @@ static void tl_cleanup(struct drbd_conf *mdev)
  *
  * The caller must hold the req_lock.
  */
-void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
+void _tl_add_barrier(struct drbd_tconn *tconn, struct drbd_tl_epoch *new)
 {
        struct drbd_tl_epoch *newest_before;
 
@@ -229,13 +249,13 @@ void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
        new->next = NULL;
        new->n_writes = 0;
 
-       newest_before = mdev->tconn->newest_tle;
+       newest_before = tconn->newest_tle;
        /* never send a barrier number == 0, because that is special-cased
         * when using TCQ for our write ordering code */
        new->br_number = (newest_before->br_number+1) ?: 1;
-       if (mdev->tconn->newest_tle != new) {
-               mdev->tconn->newest_tle->next = new;
-               mdev->tconn->newest_tle = new;
+       if (tconn->newest_tle != new) {
+               tconn->newest_tle->next = new;
+               tconn->newest_tle = new;
        }
 }
 
@@ -249,31 +269,32 @@ void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
  * &struct drbd_tl_epoch objects this function will cause a termination
  * of the connection.
  */
-void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
-                      unsigned int set_size)
+void tl_release(struct drbd_tconn *tconn, unsigned int barrier_nr,
+               unsigned int set_size)
 {
+       struct drbd_conf *mdev;
        struct drbd_tl_epoch *b, *nob; /* next old barrier */
        struct list_head *le, *tle;
        struct drbd_request *r;
 
-       spin_lock_irq(&mdev->tconn->req_lock);
+       spin_lock_irq(&tconn->req_lock);
 
-       b = mdev->tconn->oldest_tle;
+       b = tconn->oldest_tle;
 
        /* first some paranoia code */
        if (b == NULL) {
-               dev_err(DEV, "BAD! BarrierAck #%u received, but no epoch in tl!?\n",
-                       barrier_nr);
+               conn_err(tconn, "BAD! BarrierAck #%u received, but no epoch in tl!?\n",
+                        barrier_nr);
                goto bail;
        }
        if (b->br_number != barrier_nr) {
-               dev_err(DEV, "BAD! BarrierAck #%u received, expected #%u!\n",
-                       barrier_nr, b->br_number);
+               conn_err(tconn, "BAD! BarrierAck #%u received, expected #%u!\n",
+                        barrier_nr, b->br_number);
                goto bail;
        }
        if (b->n_writes != set_size) {
-               dev_err(DEV, "BAD! BarrierAck #%u received with n_writes=%u, expected n_writes=%u!\n",
-                       barrier_nr, set_size, b->n_writes);
+               conn_err(tconn, "BAD! BarrierAck #%u received with n_writes=%u, expected n_writes=%u!\n",
+                        barrier_nr, set_size, b->n_writes);
                goto bail;
        }
 
@@ -296,28 +317,29 @@ void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
           _req_mod(, BARRIER_ACKED) above.
           */
        list_del_init(&b->requests);
+       mdev = b->w.mdev;
 
        nob = b->next;
        if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) {
-               _tl_add_barrier(mdev, b);
+               _tl_add_barrier(tconn, b);
                if (nob)
-                       mdev->tconn->oldest_tle = nob;
+                       tconn->oldest_tle = nob;
                /* if nob == NULL b was the only barrier, and becomes the new
-                  barrier. Therefore mdev->tconn->oldest_tle points already to b */
+                  barrier. Therefore tconn->oldest_tle points already to b */
        } else {
                D_ASSERT(nob != NULL);
-               mdev->tconn->oldest_tle = nob;
+               tconn->oldest_tle = nob;
                kfree(b);
        }
 
-       spin_unlock_irq(&mdev->tconn->req_lock);
+       spin_unlock_irq(&tconn->req_lock);
        dec_ap_pending(mdev);
 
        return;
 
 bail:
-       spin_unlock_irq(&mdev->tconn->req_lock);
-       drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+       spin_unlock_irq(&tconn->req_lock);
+       conn_request_state(tconn, NS(conn, C_PROTOCOL_ERROR), CS_HARD);
 }
 
 
@@ -329,15 +351,15 @@ bail:
  * @what might be one of CONNECTION_LOST_WHILE_PENDING, RESEND, FAIL_FROZEN_DISK_IO,
  * RESTART_FROZEN_DISK_IO.
  */
-void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
+void _tl_restart(struct drbd_tconn *tconn, enum drbd_req_event what)
 {
        struct drbd_tl_epoch *b, *tmp, **pn;
        struct list_head *le, *tle, carry_reads;
        struct drbd_request *req;
        int rv, n_writes, n_reads;
 
-       b = mdev->tconn->oldest_tle;
-       pn = &mdev->tconn->oldest_tle;
+       b = tconn->oldest_tle;
+       pn = &tconn->oldest_tle;
        while (b) {
                n_writes = 0;
                n_reads = 0;
@@ -356,11 +378,11 @@ void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
                                b->n_writes = n_writes;
                                if (b->w.cb == NULL) {
                                        b->w.cb = w_send_barrier;
-                                       inc_ap_pending(mdev);
-                                       set_bit(CREATE_BARRIER, &mdev->flags);
+                                       inc_ap_pending(b->w.mdev);
+                                       set_bit(CREATE_BARRIER, &b->w.mdev->flags);
                                }
 
-                               drbd_queue_work(&mdev->tconn->data.work, &b->w);
+                               drbd_queue_work(&tconn->data.work, &b->w);
                        }
                        pn = &b->next;
                } else {
@@ -374,11 +396,12 @@ void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
                         * the newest barrier may not have been queued yet,
                         * in which case w.cb is still NULL. */
                        if (b->w.cb != NULL)
-                               dec_ap_pending(mdev);
+                               dec_ap_pending(b->w.mdev);
 
-                       if (b == mdev->tconn->newest_tle) {
+                       if (b == tconn->newest_tle) {
                                /* recycle, but reinit! */
-                               D_ASSERT(tmp == NULL);
+                               if (tmp != NULL)
+                                       conn_err(tconn, "ASSERT FAILED tmp == NULL");
                                INIT_LIST_HEAD(&b->requests);
                                list_splice(&carry_reads, &b->requests);
                                INIT_LIST_HEAD(&b->w.list);
@@ -406,20 +429,23 @@ void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
  * by the requests on the transfer gets marked as our of sync. Called from the
  * receiver thread and the worker thread.
  */
-void tl_clear(struct drbd_conf *mdev)
+void tl_clear(struct drbd_tconn *tconn)
 {
+       struct drbd_conf *mdev;
        struct list_head *le, *tle;
        struct drbd_request *r;
+       int vnr;
 
-       spin_lock_irq(&mdev->tconn->req_lock);
+       spin_lock_irq(&tconn->req_lock);
 
-       _tl_restart(mdev, CONNECTION_LOST_WHILE_PENDING);
+       _tl_restart(tconn, CONNECTION_LOST_WHILE_PENDING);
 
        /* we expect this list to be empty. */
-       D_ASSERT(list_empty(&mdev->tconn->out_of_sequence_requests));
+       if (!list_empty(&tconn->out_of_sequence_requests))
+               conn_err(tconn, "ASSERT FAILED list_empty(&out_of_sequence_requests)\n");
 
        /* but just in case, clean it up anyways! */
-       list_for_each_safe(le, tle, &mdev->tconn->out_of_sequence_requests) {
+       list_for_each_safe(le, tle, &tconn->out_of_sequence_requests) {
                r = list_entry(le, struct drbd_request, tl_requests);
                /* It would be nice to complete outside of spinlock.
                 * But this is easier for now. */
@@ -427,16 +453,19 @@ void tl_clear(struct drbd_conf *mdev)
        }
 
        /* ensure bit indicating barrier is required is clear */
-       clear_bit(CREATE_BARRIER, &mdev->flags);
+       rcu_read_lock();
+       idr_for_each_entry(&tconn->volumes, mdev, vnr)
+               clear_bit(CREATE_BARRIER, &mdev->flags);
+       rcu_read_unlock();
 
-       spin_unlock_irq(&mdev->tconn->req_lock);
+       spin_unlock_irq(&tconn->req_lock);
 }
 
-void tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
+void tl_restart(struct drbd_tconn *tconn, enum drbd_req_event what)
 {
-       spin_lock_irq(&mdev->tconn->req_lock);
-       _tl_restart(mdev, what);
-       spin_unlock_irq(&mdev->tconn->req_lock);
+       spin_lock_irq(&tconn->req_lock);
+       _tl_restart(tconn, what);
+       spin_unlock_irq(&tconn->req_lock);
 }
 
 static int drbd_thread_setup(void *arg)
@@ -480,6 +509,8 @@ restart:
        conn_info(tconn, "Terminating %s\n", current->comm);
 
        /* Release mod reference taken when thread was started */
+
+       kref_put(&tconn->kref, &conn_destroy);
        module_put(THIS_MODULE);
        return retval;
 }
@@ -517,6 +548,8 @@ int drbd_thread_start(struct drbd_thread *thi)
                        return false;
                }
 
+               kref_get(&thi->tconn->kref);
+
                init_completion(&thi->stop);
                thi->reset_cpu_mask = 1;
                thi->t_state = RUNNING;
@@ -529,6 +562,7 @@ int drbd_thread_start(struct drbd_thread *thi)
                if (IS_ERR(nt)) {
                        conn_err(tconn, "Couldn't start thread\n");
 
+                       kref_put(&tconn->kref, &conn_destroy);
                        module_put(THIS_MODULE);
                        return false;
                }
@@ -605,13 +639,20 @@ char *drbd_task_to_thread_name(struct drbd_tconn *tconn, struct task_struct *tas
        return thi ? thi->name : task->comm;
 }
 
-#ifdef CONFIG_SMP
-static int conn_lowest_minor(struct drbd_tconn *tconn)
+int conn_lowest_minor(struct drbd_tconn *tconn)
 {
-       int minor = 0;
-       idr_get_next(&tconn->volumes, &minor);
-       return minor;
+       struct drbd_conf *mdev;
+       int vnr = 0, m;
+
+       rcu_read_lock();
+       mdev = idr_get_next(&tconn->volumes, &vnr);
+       m = mdev ? mdev_to_minor(mdev) : -1;
+       rcu_read_unlock();
+
+       return m;
 }
+
+#ifdef CONFIG_SMP
 /**
  * drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs
  * @mdev:      DRBD device.
@@ -657,216 +698,275 @@ void drbd_thread_current_set_cpu(struct drbd_thread *thi)
 }
 #endif
 
-static void prepare_header80(struct p_header80 *h, enum drbd_packet cmd, int size)
+/**
+ * drbd_header_size  -  size of a packet header
+ *
+ * The header size is a multiple of 8, so any payload following the header is
+ * word aligned on 64-bit architectures.  (The bitmap send and receive code
+ * relies on this.)
+ */
+unsigned int drbd_header_size(struct drbd_tconn *tconn)
+{
+       if (tconn->agreed_pro_version >= 100) {
+               BUILD_BUG_ON(!IS_ALIGNED(sizeof(struct p_header100), 8));
+               return sizeof(struct p_header100);
+       } else {
+               BUILD_BUG_ON(sizeof(struct p_header80) !=
+                            sizeof(struct p_header95));
+               BUILD_BUG_ON(!IS_ALIGNED(sizeof(struct p_header80), 8));
+               return sizeof(struct p_header80);
+       }
+}
+
+static unsigned int prepare_header80(struct p_header80 *h, enum drbd_packet cmd, int size)
 {
        h->magic   = cpu_to_be32(DRBD_MAGIC);
        h->command = cpu_to_be16(cmd);
        h->length  = cpu_to_be16(size);
+       return sizeof(struct p_header80);
 }
 
-static void prepare_header95(struct p_header95 *h, enum drbd_packet cmd, int size)
+static unsigned int prepare_header95(struct p_header95 *h, enum drbd_packet cmd, int size)
 {
        h->magic   = cpu_to_be16(DRBD_MAGIC_BIG);
        h->command = cpu_to_be16(cmd);
-       h->length  = cpu_to_be32(size);
+       h->length = cpu_to_be32(size);
+       return sizeof(struct p_header95);
 }
 
-static void _prepare_header(struct drbd_tconn *tconn, int vnr, struct p_header *h,
-                           enum drbd_packet cmd, int size)
+static unsigned int prepare_header100(struct p_header100 *h, enum drbd_packet cmd,
+                                     int size, int vnr)
 {
-       if (tconn->agreed_pro_version >= 100 || size > DRBD_MAX_SIZE_H80_PACKET)
-               prepare_header95(&h->h95, cmd, size);
-       else
-               prepare_header80(&h->h80, cmd, size);
+       h->magic = cpu_to_be32(DRBD_MAGIC_100);
+       h->volume = cpu_to_be16(vnr);
+       h->command = cpu_to_be16(cmd);
+       h->length = cpu_to_be32(size);
+       h->pad = 0;
+       return sizeof(struct p_header100);
 }
 
-static void prepare_header(struct drbd_conf *mdev, struct p_header *h,
-                          enum drbd_packet cmd, int size)
+static unsigned int prepare_header(struct drbd_tconn *tconn, int vnr,
+                                  void *buffer, enum drbd_packet cmd, int size)
 {
-       _prepare_header(mdev->tconn, mdev->vnr, h, cmd, size);
+       if (tconn->agreed_pro_version >= 100)
+               return prepare_header100(buffer, cmd, size, vnr);
+       else if (tconn->agreed_pro_version >= 95 &&
+                size > DRBD_MAX_SIZE_H80_PACKET)
+               return prepare_header95(buffer, cmd, size);
+       else
+               return prepare_header80(buffer, cmd, size);
 }
 
-/* the appropriate socket mutex must be held already */
-int _conn_send_cmd(struct drbd_tconn *tconn, int vnr, struct socket *sock,
-                  enum drbd_packet cmd, struct p_header *h, size_t size,
-                  unsigned msg_flags)
+void *conn_prepare_command(struct drbd_tconn *tconn, struct drbd_socket *sock)
 {
-       int sent, ok;
-
-       _prepare_header(tconn, vnr, h, cmd, size - sizeof(struct p_header));
-
-       sent = drbd_send(tconn, sock, h, size, msg_flags);
+       mutex_lock(&sock->mutex);
+       if (!sock->socket) {
+               mutex_unlock(&sock->mutex);
+               return NULL;
+       }
+       return sock->sbuf + drbd_header_size(tconn);
+}
 
-       ok = (sent == size);
-       if (!ok && !signal_pending(current))
-               conn_warn(tconn, "short sent %s size=%d sent=%d\n",
-                         cmdname(cmd), (int)size, sent);
-       return ok;
+void *drbd_prepare_command(struct drbd_conf *mdev, struct drbd_socket *sock)
+{
+       return conn_prepare_command(mdev->tconn, sock);
 }
 
-/* don't pass the socket. we may only look at it
- * when we hold the appropriate socket mutex.
- */
-int conn_send_cmd(struct drbd_tconn *tconn, int vnr, int use_data_socket,
-                 enum drbd_packet cmd, struct p_header *h, size_t size)
+static int __send_command(struct drbd_tconn *tconn, int vnr,
+                         struct drbd_socket *sock, enum drbd_packet cmd,
+                         unsigned int header_size, void *data,
+                         unsigned int size)
 {
-       int ok = 0;
-       struct socket *sock;
+       int msg_flags;
+       int err;
 
-       if (use_data_socket) {
-               mutex_lock(&tconn->data.mutex);
-               sock = tconn->data.socket;
-       } else {
-               mutex_lock(&tconn->meta.mutex);
-               sock = tconn->meta.socket;
-       }
+       /*
+        * Called with @data == NULL and the size of the data blocks in @size
+        * for commands that send data blocks.  For those commands, omit the
+        * MSG_MORE flag: this will increase the likelihood that data blocks
+        * which are page aligned on the sender will end up page aligned on the
+        * receiver.
+        */
+       msg_flags = data ? MSG_MORE : 0;
+
+       header_size += prepare_header(tconn, vnr, sock->sbuf, cmd,
+                                     header_size + size);
+       err = drbd_send_all(tconn, sock->socket, sock->sbuf, header_size,
+                           msg_flags);
+       if (data && !err)
+               err = drbd_send_all(tconn, sock->socket, data, size, 0);
+       return err;
+}
 
-       /* drbd_disconnect() could have called drbd_free_sock()
-        * while we were waiting in down()... */
-       if (likely(sock != NULL))
-               ok = _conn_send_cmd(tconn, vnr, sock, cmd, h, size, 0);
+int conn_send_command(struct drbd_tconn *tconn, struct drbd_socket *sock,
+                     enum drbd_packet cmd, unsigned int header_size,
+                     void *data, unsigned int size)
+{
+       int err;
 
-       if (use_data_socket)
-               mutex_unlock(&tconn->data.mutex);
-       else
-               mutex_unlock(&tconn->meta.mutex);
-       return ok;
+       err = __send_command(tconn, 0, sock, cmd, header_size, data, size);
+       mutex_unlock(&sock->mutex);
+       return err;
 }
 
-int conn_send_cmd2(struct drbd_tconn *tconn, enum drbd_packet cmd, char *data,
-                  size_t size)
+int drbd_send_command(struct drbd_conf *mdev, struct drbd_socket *sock,
+                     enum drbd_packet cmd, unsigned int header_size,
+                     void *data, unsigned int size)
 {
-       struct p_header80 h;
-       int ok;
+       int err;
 
-       prepare_header80(&h, cmd, size);
+       err = __send_command(mdev->tconn, mdev->vnr, sock, cmd, header_size,
+                            data, size);
+       mutex_unlock(&sock->mutex);
+       return err;
+}
 
-       if (!drbd_get_data_sock(tconn))
-               return 0;
+int drbd_send_ping(struct drbd_tconn *tconn)
+{
+       struct drbd_socket *sock;
 
-       ok = (sizeof(h) ==
-               drbd_send(tconn, tconn->data.socket, &h, sizeof(h), 0));
-       ok = ok && (size ==
-               drbd_send(tconn, tconn->data.socket, data, size, 0));
+       sock = &tconn->meta;
+       if (!conn_prepare_command(tconn, sock))
+               return -EIO;
+       return conn_send_command(tconn, sock, P_PING, 0, NULL, 0);
+}
 
-       drbd_put_data_sock(tconn);
+int drbd_send_ping_ack(struct drbd_tconn *tconn)
+{
+       struct drbd_socket *sock;
 
-       return ok;
+       sock = &tconn->meta;
+       if (!conn_prepare_command(tconn, sock))
+               return -EIO;
+       return conn_send_command(tconn, sock, P_PING_ACK, 0, NULL, 0);
 }
 
-int drbd_send_sync_param(struct drbd_conf *mdev, struct syncer_conf *sc)
+int drbd_send_sync_param(struct drbd_conf *mdev)
 {
+       struct drbd_socket *sock;
        struct p_rs_param_95 *p;
-       struct socket *sock;
-       int size, rv;
+       int size;
        const int apv = mdev->tconn->agreed_pro_version;
+       enum drbd_packet cmd;
+       struct net_conf *nc;
+
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+
+       rcu_read_lock();
+       nc = rcu_dereference(mdev->tconn->net_conf);
 
        size = apv <= 87 ? sizeof(struct p_rs_param)
                : apv == 88 ? sizeof(struct p_rs_param)
-                       + strlen(mdev->sync_conf.verify_alg) + 1
+                       + strlen(nc->verify_alg) + 1
                : apv <= 94 ? sizeof(struct p_rs_param_89)
                : /* apv >= 95 */ sizeof(struct p_rs_param_95);
 
-       /* used from admin command context and receiver/worker context.
-        * to avoid kmalloc, grab the socket right here,
-        * then use the pre-allocated sbuf there */
-       mutex_lock(&mdev->tconn->data.mutex);
-       sock = mdev->tconn->data.socket;
-
-       if (likely(sock != NULL)) {
-               enum drbd_packet cmd =
-                       apv >= 89 ? P_SYNC_PARAM89 : P_SYNC_PARAM;
-
-               p = &mdev->tconn->data.sbuf.rs_param_95;
-
-               /* initialize verify_alg and csums_alg */
-               memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX);
-
-               p->rate = cpu_to_be32(sc->rate);
-               p->c_plan_ahead = cpu_to_be32(sc->c_plan_ahead);
-               p->c_delay_target = cpu_to_be32(sc->c_delay_target);
-               p->c_fill_target = cpu_to_be32(sc->c_fill_target);
-               p->c_max_rate = cpu_to_be32(sc->c_max_rate);
+       cmd = apv >= 89 ? P_SYNC_PARAM89 : P_SYNC_PARAM;
 
-               if (apv >= 88)
-                       strcpy(p->verify_alg, mdev->sync_conf.verify_alg);
-               if (apv >= 89)
-                       strcpy(p->csums_alg, mdev->sync_conf.csums_alg);
+       /* initialize verify_alg and csums_alg */
+       memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX);
 
-               rv = _drbd_send_cmd(mdev, sock, cmd, &p->head, size, 0);
-       } else
-               rv = 0; /* not ok */
+       if (get_ldev(mdev)) {
+               p->rate = cpu_to_be32(mdev->ldev->dc.resync_rate);
+               p->c_plan_ahead = cpu_to_be32(mdev->ldev->dc.c_plan_ahead);
+               p->c_delay_target = cpu_to_be32(mdev->ldev->dc.c_delay_target);
+               p->c_fill_target = cpu_to_be32(mdev->ldev->dc.c_fill_target);
+               p->c_max_rate = cpu_to_be32(mdev->ldev->dc.c_max_rate);
+               put_ldev(mdev);
+       } else {
+               p->rate = cpu_to_be32(DRBD_RATE_DEF);
+               p->c_plan_ahead = cpu_to_be32(DRBD_C_PLAN_AHEAD_DEF);
+               p->c_delay_target = cpu_to_be32(DRBD_C_DELAY_TARGET_DEF);
+               p->c_fill_target = cpu_to_be32(DRBD_C_FILL_TARGET_DEF);
+               p->c_max_rate = cpu_to_be32(DRBD_C_MAX_RATE_DEF);
+       }
 
-       mutex_unlock(&mdev->tconn->data.mutex);
+       if (apv >= 88)
+               strcpy(p->verify_alg, nc->verify_alg);
+       if (apv >= 89)
+               strcpy(p->csums_alg, nc->csums_alg);
+       rcu_read_unlock();
 
-       return rv;
+       return drbd_send_command(mdev, sock, cmd, size, NULL, 0);
 }
 
 int drbd_send_protocol(struct drbd_tconn *tconn)
 {
+       struct drbd_socket *sock;
        struct p_protocol *p;
-       int size, cf, rv;
+       struct net_conf *nc;
+       int size, cf;
 
-       size = sizeof(struct p_protocol);
+       sock = &tconn->data;
+       p = conn_prepare_command(tconn, sock);
+       if (!p)
+               return -EIO;
 
-       if (tconn->agreed_pro_version >= 87)
-               size += strlen(tconn->net_conf->integrity_alg) + 1;
+       rcu_read_lock();
+       nc = rcu_dereference(tconn->net_conf);
 
-       /* we must not recurse into our own queue,
-        * as that is blocked during handshake */
-       p = kmalloc(size, GFP_NOIO);
-       if (p == NULL)
-               return 0;
+       if (nc->dry_run && tconn->agreed_pro_version < 92) {
+               rcu_read_unlock();
+               mutex_unlock(&sock->mutex);
+               conn_err(tconn, "--dry-run is not supported by peer");
+               return -EOPNOTSUPP;
+       }
 
-       p->protocol      = cpu_to_be32(tconn->net_conf->wire_protocol);
-       p->after_sb_0p   = cpu_to_be32(tconn->net_conf->after_sb_0p);
-       p->after_sb_1p   = cpu_to_be32(tconn->net_conf->after_sb_1p);
-       p->after_sb_2p   = cpu_to_be32(tconn->net_conf->after_sb_2p);
-       p->two_primaries = cpu_to_be32(tconn->net_conf->two_primaries);
+       size = sizeof(*p);
+       if (tconn->agreed_pro_version >= 87)
+               size += strlen(nc->integrity_alg) + 1;
 
+       p->protocol      = cpu_to_be32(nc->wire_protocol);
+       p->after_sb_0p   = cpu_to_be32(nc->after_sb_0p);
+       p->after_sb_1p   = cpu_to_be32(nc->after_sb_1p);
+       p->after_sb_2p   = cpu_to_be32(nc->after_sb_2p);
+       p->two_primaries = cpu_to_be32(nc->two_primaries);
        cf = 0;
-       if (tconn->net_conf->want_lose)
+       if (nc->want_lose)
                cf |= CF_WANT_LOSE;
-       if (tconn->net_conf->dry_run) {
-               if (tconn->agreed_pro_version >= 92)
-                       cf |= CF_DRY_RUN;
-               else {
-                       conn_err(tconn, "--dry-run is not supported by peer");
-                       kfree(p);
-                       return -1;
-               }
-       }
+       if (nc->dry_run)
+               cf |= CF_DRY_RUN;
        p->conn_flags    = cpu_to_be32(cf);
 
        if (tconn->agreed_pro_version >= 87)
-               strcpy(p->integrity_alg, tconn->net_conf->integrity_alg);
+               strcpy(p->integrity_alg, nc->integrity_alg);
+       rcu_read_unlock();
 
-       rv = conn_send_cmd2(tconn, P_PROTOCOL, p->head.payload, size - sizeof(struct p_header));
-       kfree(p);
-       return rv;
+       return conn_send_command(tconn, sock, P_PROTOCOL, size, NULL, 0);
 }
 
 int _drbd_send_uuids(struct drbd_conf *mdev, u64 uuid_flags)
 {
-       struct p_uuids p;
+       struct drbd_socket *sock;
+       struct p_uuids *p;
        int i;
 
        if (!get_ldev_if_state(mdev, D_NEGOTIATING))
-               return 1;
+               return 0;
 
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p) {
+               put_ldev(mdev);
+               return -EIO;
+       }
        for (i = UI_CURRENT; i < UI_SIZE; i++)
-               p.uuid[i] = mdev->ldev ? cpu_to_be64(mdev->ldev->md.uuid[i]) : 0;
+               p->uuid[i] = mdev->ldev ? cpu_to_be64(mdev->ldev->md.uuid[i]) : 0;
 
        mdev->comm_bm_set = drbd_bm_total_weight(mdev);
-       p.uuid[UI_SIZE] = cpu_to_be64(mdev->comm_bm_set);
-       uuid_flags |= mdev->tconn->net_conf->want_lose ? 1 : 0;
+       p->uuid[UI_SIZE] = cpu_to_be64(mdev->comm_bm_set);
+       rcu_read_lock();
+       uuid_flags |= rcu_dereference(mdev->tconn->net_conf)->want_lose ? 1 : 0;
+       rcu_read_unlock();
        uuid_flags |= test_bit(CRASHED_PRIMARY, &mdev->flags) ? 2 : 0;
        uuid_flags |= mdev->new_state_tmp.disk == D_INCONSISTENT ? 4 : 0;
-       p.uuid[UI_FLAGS] = cpu_to_be64(uuid_flags);
+       p->uuid[UI_FLAGS] = cpu_to_be64(uuid_flags);
 
        put_ldev(mdev);
-
-       return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_UUIDS, &p.head, sizeof(p));
+       return drbd_send_command(mdev, sock, P_UUIDS, sizeof(*p), NULL, 0);
 }
 
 int drbd_send_uuids(struct drbd_conf *mdev)
@@ -897,9 +997,10 @@ void drbd_print_uuids(struct drbd_conf *mdev, const char *text)
        }
 }
 
-int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev)
+void drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev)
 {
-       struct p_rs_uuid p;
+       struct drbd_socket *sock;
+       struct p_rs_uuid *p;
        u64 uuid;
 
        D_ASSERT(mdev->state.disk == D_UP_TO_DATE);
@@ -908,17 +1009,21 @@ int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev)
        drbd_uuid_set(mdev, UI_BITMAP, uuid);
        drbd_print_uuids(mdev, "updated sync UUID");
        drbd_md_sync(mdev);
-       p.uuid = cpu_to_be64(uuid);
 
-       return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SYNC_UUID, &p.head, sizeof(p));
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (p) {
+               p->uuid = cpu_to_be64(uuid);
+               drbd_send_command(mdev, sock, P_SYNC_UUID, sizeof(*p), NULL, 0);
+       }
 }
 
 int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags flags)
 {
-       struct p_sizes p;
+       struct drbd_socket *sock;
+       struct p_sizes *p;
        sector_t d_size, u_size;
        int q_order_type, max_bio_size;
-       int ok;
 
        if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
                D_ASSERT(mdev->ldev->backing_bdev);
@@ -935,15 +1040,17 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
                max_bio_size = DRBD_MAX_BIO_SIZE; /* ... multiple BIOs per peer_request */
        }
 
-       p.d_size = cpu_to_be64(d_size);
-       p.u_size = cpu_to_be64(u_size);
-       p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
-       p.max_bio_size = cpu_to_be32(max_bio_size);
-       p.queue_order_type = cpu_to_be16(q_order_type);
-       p.dds_flags = cpu_to_be16(flags);
-
-       ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SIZES, &p.head, sizeof(p));
-       return ok;
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->d_size = cpu_to_be64(d_size);
+       p->u_size = cpu_to_be64(u_size);
+       p->c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
+       p->max_bio_size = cpu_to_be32(max_bio_size);
+       p->queue_order_type = cpu_to_be16(q_order_type);
+       p->dds_flags = cpu_to_be16(flags);
+       return drbd_send_command(mdev, sock, P_SIZES, sizeof(*p), NULL, 0);
 }
 
 /**
@@ -952,57 +1059,96 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
  */
 int drbd_send_state(struct drbd_conf *mdev)
 {
-       struct socket *sock;
-       struct p_state p;
-       int ok = 0;
-
-       mutex_lock(&mdev->tconn->data.mutex);
+       struct drbd_socket *sock;
+       struct p_state *p;
 
-       p.state = cpu_to_be32(mdev->state.i); /* Within the send mutex */
-       sock = mdev->tconn->data.socket;
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->state = cpu_to_be32(mdev->state.i); /* Within the send mutex */
+       return drbd_send_command(mdev, sock, P_STATE, sizeof(*p), NULL, 0);
+}
 
-       if (likely(sock != NULL)) {
-               ok = _drbd_send_cmd(mdev, sock, P_STATE, &p.head, sizeof(p), 0);
-       }
+int drbd_send_state_req(struct drbd_conf *mdev, union drbd_state mask, union drbd_state val)
+{
+       struct drbd_socket *sock;
+       struct p_req_state *p;
 
-       mutex_unlock(&mdev->tconn->data.mutex);
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->mask = cpu_to_be32(mask.i);
+       p->val = cpu_to_be32(val.i);
+       return drbd_send_command(mdev, sock, P_STATE_CHG_REQ, sizeof(*p), NULL, 0);
 
-       return ok;
 }
 
-int _conn_send_state_req(struct drbd_tconn *tconn, int vnr, enum drbd_packet cmd,
-                        union drbd_state mask, union drbd_state val)
+int conn_send_state_req(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state val)
 {
-       struct p_req_state p;
+       enum drbd_packet cmd;
+       struct drbd_socket *sock;
+       struct p_req_state *p;
 
-       p.mask    = cpu_to_be32(mask.i);
-       p.val     = cpu_to_be32(val.i);
-
-       return conn_send_cmd(tconn, vnr, USE_DATA_SOCKET, cmd, &p.head, sizeof(p));
+       cmd = tconn->agreed_pro_version < 100 ? P_STATE_CHG_REQ : P_CONN_ST_CHG_REQ;
+       sock = &tconn->data;
+       p = conn_prepare_command(tconn, sock);
+       if (!p)
+               return -EIO;
+       p->mask = cpu_to_be32(mask.i);
+       p->val = cpu_to_be32(val.i);
+       return conn_send_command(tconn, sock, cmd, sizeof(*p), NULL, 0);
 }
 
-int drbd_send_sr_reply(struct drbd_conf *mdev, enum drbd_state_rv retcode)
+void drbd_send_sr_reply(struct drbd_conf *mdev, enum drbd_state_rv retcode)
 {
-       struct p_req_state_reply p;
-
-       p.retcode    = cpu_to_be32(retcode);
+       struct drbd_socket *sock;
+       struct p_req_state_reply *p;
 
-       return drbd_send_cmd(mdev, USE_META_SOCKET, P_STATE_CHG_REPLY, &p.head, sizeof(p));
+       sock = &mdev->tconn->meta;
+       p = drbd_prepare_command(mdev, sock);
+       if (p) {
+               p->retcode = cpu_to_be32(retcode);
+               drbd_send_command(mdev, sock, P_STATE_CHG_REPLY, sizeof(*p), NULL, 0);
+       }
 }
 
-int conn_send_sr_reply(struct drbd_tconn *tconn, enum drbd_state_rv retcode)
+void conn_send_sr_reply(struct drbd_tconn *tconn, enum drbd_state_rv retcode)
 {
-       struct p_req_state_reply p;
+       struct drbd_socket *sock;
+       struct p_req_state_reply *p;
        enum drbd_packet cmd = tconn->agreed_pro_version < 100 ? P_STATE_CHG_REPLY : P_CONN_ST_CHG_REPLY;
 
-       p.retcode    = cpu_to_be32(retcode);
+       sock = &tconn->meta;
+       p = conn_prepare_command(tconn, sock);
+       if (p) {
+               p->retcode = cpu_to_be32(retcode);
+               conn_send_command(tconn, sock, cmd, sizeof(*p), NULL, 0);
+       }
+}
+
+static void dcbp_set_code(struct p_compressed_bm *p, enum drbd_bitmap_code code)
+{
+       BUG_ON(code & ~0xf);
+       p->encoding = (p->encoding & ~0xf) | code;
+}
+
+static void dcbp_set_start(struct p_compressed_bm *p, int set)
+{
+       p->encoding = (p->encoding & ~0x80) | (set ? 0x80 : 0);
+}
 
-       return conn_send_cmd(tconn, 0, USE_META_SOCKET, cmd, &p.head, sizeof(p));
+static void dcbp_set_pad_bits(struct p_compressed_bm *p, int n)
+{
+       BUG_ON(n & ~0x7);
+       p->encoding = (p->encoding & (~0x7 << 4)) | (n << 4);
 }
 
 int fill_bitmap_rle_bits(struct drbd_conf *mdev,
-       struct p_compressed_bm *p,
-       struct bm_xfer_ctx *c)
+                        struct p_compressed_bm *p,
+                        unsigned int size,
+                        struct bm_xfer_ctx *c)
 {
        struct bitstream bs;
        unsigned long plain_bits;
@@ -1010,19 +1156,21 @@ int fill_bitmap_rle_bits(struct drbd_conf *mdev,
        unsigned long rl;
        unsigned len;
        unsigned toggle;
-       int bits;
+       int bits, use_rle;
 
        /* may we use this feature? */
-       if ((mdev->sync_conf.use_rle == 0) ||
-               (mdev->tconn->agreed_pro_version < 90))
-                       return 0;
+       rcu_read_lock();
+       use_rle = rcu_dereference(mdev->tconn->net_conf)->use_rle;
+       rcu_read_unlock();
+       if (!use_rle || mdev->tconn->agreed_pro_version < 90)
+               return 0;
 
        if (c->bit_offset >= c->bm_bits)
                return 0; /* nothing to do. */
 
        /* use at most thus many bytes */
-       bitstream_init(&bs, p->code, BM_PACKET_VLI_BYTES_MAX, 0);
-       memset(p->code, 0, BM_PACKET_VLI_BYTES_MAX);
+       bitstream_init(&bs, p->code, size, 0);
+       memset(p->code, 0, size);
        /* plain bits covered in this code string */
        plain_bits = 0;
 
@@ -1044,12 +1192,12 @@ int fill_bitmap_rle_bits(struct drbd_conf *mdev,
                        if (rl == 0) {
                                /* the first checked bit was set,
                                 * store start value, */
-                               DCBP_set_start(p, 1);
+                               dcbp_set_start(p, 1);
                                /* but skip encoding of zero run length */
                                toggle = !toggle;
                                continue;
                        }
-                       DCBP_set_start(p, 0);
+                       dcbp_set_start(p, 0);
                }
 
                /* paranoia: catch zero runlength.
@@ -1089,7 +1237,7 @@ int fill_bitmap_rle_bits(struct drbd_conf *mdev,
        bm_xfer_ctx_bit_to_word_offset(c);
 
        /* store pad_bits */
-       DCBP_set_pad_bits(p, (8 - bs.cur.bit) & 0x7);
+       dcbp_set_pad_bits(p, (8 - bs.cur.bit) & 0x7);
 
        return len;
 }
@@ -1101,48 +1249,52 @@ int fill_bitmap_rle_bits(struct drbd_conf *mdev,
  * code upon failure.
  */
 static int
-send_bitmap_rle_or_plain(struct drbd_conf *mdev,
-                        struct p_header *h, struct bm_xfer_ctx *c)
+send_bitmap_rle_or_plain(struct drbd_conf *mdev, struct bm_xfer_ctx *c)
 {
-       struct p_compressed_bm *p = (void*)h;
-       unsigned long num_words;
-       int len;
-       int ok;
-
-       len = fill_bitmap_rle_bits(mdev, p, c);
+       struct drbd_socket *sock = &mdev->tconn->data;
+       unsigned int header_size = drbd_header_size(mdev->tconn);
+       struct p_compressed_bm *p = sock->sbuf + header_size;
+       int len, err;
 
+       len = fill_bitmap_rle_bits(mdev, p,
+                       DRBD_SOCKET_BUFFER_SIZE - header_size - sizeof(*p), c);
        if (len < 0)
                return -EIO;
 
        if (len) {
-               DCBP_set_code(p, RLE_VLI_Bits);
-               ok = _drbd_send_cmd(mdev, mdev->tconn->data.socket, P_COMPRESSED_BITMAP, h,
-                       sizeof(*p) + len, 0);
-
+               dcbp_set_code(p, RLE_VLI_Bits);
+               err = __send_command(mdev->tconn, mdev->vnr, sock,
+                                    P_COMPRESSED_BITMAP, sizeof(*p) + len,
+                                    NULL, 0);
                c->packets[0]++;
-               c->bytes[0] += sizeof(*p) + len;
+               c->bytes[0] += header_size + sizeof(*p) + len;
 
                if (c->bit_offset >= c->bm_bits)
                        len = 0; /* DONE */
        } else {
                /* was not compressible.
                 * send a buffer full of plain text bits instead. */
-               num_words = min_t(size_t, BM_PACKET_WORDS, c->bm_words - c->word_offset);
-               len = num_words * sizeof(long);
+               unsigned int data_size;
+               unsigned long num_words;
+               unsigned long *p = sock->sbuf + header_size;
+
+               data_size = DRBD_SOCKET_BUFFER_SIZE - header_size;
+               num_words = min_t(size_t, data_size / sizeof(*p),
+                                 c->bm_words - c->word_offset);
+               len = num_words * sizeof(*p);
                if (len)
-                       drbd_bm_get_lel(mdev, c->word_offset, num_words, (unsigned long*)h->payload);
-               ok = _drbd_send_cmd(mdev, mdev->tconn->data.socket, P_BITMAP,
-                                  h, sizeof(struct p_header80) + len, 0);
+                       drbd_bm_get_lel(mdev, c->word_offset, num_words, p);
+               err = __send_command(mdev->tconn, mdev->vnr, sock, P_BITMAP, len, NULL, 0);
                c->word_offset += num_words;
                c->bit_offset = c->word_offset * BITS_PER_LONG;
 
                c->packets[1]++;
-               c->bytes[1] += sizeof(struct p_header80) + len;
+               c->bytes[1] += header_size + len;
 
                if (c->bit_offset > c->bm_bits)
                        c->bit_offset = c->bm_bits;
        }
-       if (ok) {
+       if (!err) {
                if (len == 0) {
                        INFO_bm_xfer_stats(mdev, "send", c);
                        return 0;
@@ -1153,23 +1305,14 @@ send_bitmap_rle_or_plain(struct drbd_conf *mdev,
 }
 
 /* See the comment at receive_bitmap() */
-int _drbd_send_bitmap(struct drbd_conf *mdev)
+static int _drbd_send_bitmap(struct drbd_conf *mdev)
 {
        struct bm_xfer_ctx c;
-       struct p_header *p;
        int err;
 
        if (!expect(mdev->bitmap))
                return false;
 
-       /* maybe we should use some per thread scratch page,
-        * and allocate that during initial device creation? */
-       p = (struct p_header *) __get_free_page(GFP_NOIO);
-       if (!p) {
-               dev_err(DEV, "failed to allocate one page buffer in %s\n", __func__);
-               return false;
-       }
-
        if (get_ldev(mdev)) {
                if (drbd_md_test_flag(mdev->ldev, MDF_FULL_SYNC)) {
                        dev_info(DEV, "Writing the whole bitmap, MDF_FullSync was set.\n");
@@ -1193,36 +1336,39 @@ int _drbd_send_bitmap(struct drbd_conf *mdev)
        };
 
        do {
-               err = send_bitmap_rle_or_plain(mdev, p, &c);
+               err = send_bitmap_rle_or_plain(mdev, &c);
        } while (err > 0);
 
-       free_page((unsigned long) p);
        return err == 0;
 }
 
 int drbd_send_bitmap(struct drbd_conf *mdev)
 {
-       int err;
+       struct drbd_socket *sock = &mdev->tconn->data;
+       int err = -1;
 
-       if (!drbd_get_data_sock(mdev->tconn))
-               return -1;
-       err = !_drbd_send_bitmap(mdev);
-       drbd_put_data_sock(mdev->tconn);
+       mutex_lock(&sock->mutex);
+       if (sock->socket)
+               err = !_drbd_send_bitmap(mdev);
+       mutex_unlock(&sock->mutex);
        return err;
 }
 
-int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
+void drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
 {
-       int ok;
-       struct p_barrier_ack p;
-
-       p.barrier  = barrier_nr;
-       p.set_size = cpu_to_be32(set_size);
+       struct drbd_socket *sock;
+       struct p_barrier_ack *p;
 
        if (mdev->state.conn < C_CONNECTED)
-               return false;
-       ok = drbd_send_cmd(mdev, USE_META_SOCKET, P_BARRIER_ACK, &p.head, sizeof(p));
-       return ok;
+               return;
+
+       sock = &mdev->tconn->meta;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return;
+       p->barrier = barrier_nr;
+       p->set_size = cpu_to_be32(set_size);
+       drbd_send_command(mdev, sock, P_BARRIER_ACK, sizeof(*p), NULL, 0);
 }
 
 /**
@@ -1236,36 +1382,39 @@ int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
 static int _drbd_send_ack(struct drbd_conf *mdev, enum drbd_packet cmd,
                          u64 sector, u32 blksize, u64 block_id)
 {
-       int ok;
-       struct p_block_ack p;
+       struct drbd_socket *sock;
+       struct p_block_ack *p;
 
-       p.sector   = sector;
-       p.block_id = block_id;
-       p.blksize  = blksize;
-       p.seq_num  = cpu_to_be32(atomic_inc_return(&mdev->packet_seq));
+       if (mdev->state.conn < C_CONNECTED)
+               return -EIO;
 
-       if (!mdev->tconn->meta.socket || mdev->state.conn < C_CONNECTED)
-               return false;
-       ok = drbd_send_cmd(mdev, USE_META_SOCKET, cmd, &p.head, sizeof(p));
-       return ok;
+       sock = &mdev->tconn->meta;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = sector;
+       p->block_id = block_id;
+       p->blksize = blksize;
+       p->seq_num = cpu_to_be32(atomic_inc_return(&mdev->packet_seq));
+       return drbd_send_command(mdev, sock, cmd, sizeof(*p), NULL, 0);
 }
 
 /* dp->sector and dp->block_id already/still in network byte order,
  * data_size is payload size according to dp->head,
  * and may need to be corrected for digest size. */
-int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packet cmd,
-                    struct p_data *dp, int data_size)
+void drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packet cmd,
+                     struct p_data *dp, int data_size)
 {
        data_size -= (mdev->tconn->agreed_pro_version >= 87 && mdev->tconn->integrity_r_tfm) ?
                crypto_hash_digestsize(mdev->tconn->integrity_r_tfm) : 0;
-       return _drbd_send_ack(mdev, cmd, dp->sector, cpu_to_be32(data_size),
-                             dp->block_id);
+       _drbd_send_ack(mdev, cmd, dp->sector, cpu_to_be32(data_size),
+                      dp->block_id);
 }
 
-int drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packet cmd,
-                    struct p_block_req *rp)
+void drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packet cmd,
+                     struct p_block_req *rp)
 {
-       return _drbd_send_ack(mdev, cmd, rp->sector, rp->blksize, rp->block_id);
+       _drbd_send_ack(mdev, cmd, rp->sector, rp->blksize, rp->block_id);
 }
 
 /**
@@ -1297,49 +1446,51 @@ int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packet cmd,
 int drbd_send_drequest(struct drbd_conf *mdev, int cmd,
                       sector_t sector, int size, u64 block_id)
 {
-       int ok;
-       struct p_block_req p;
-
-       p.sector   = cpu_to_be64(sector);
-       p.block_id = block_id;
-       p.blksize  = cpu_to_be32(size);
+       struct drbd_socket *sock;
+       struct p_block_req *p;
 
-       ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, cmd, &p.head, sizeof(p));
-       return ok;
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(sector);
+       p->block_id = block_id;
+       p->blksize = cpu_to_be32(size);
+       return drbd_send_command(mdev, sock, cmd, sizeof(*p), NULL, 0);
 }
 
 int drbd_send_drequest_csum(struct drbd_conf *mdev, sector_t sector, int size,
                            void *digest, int digest_size, enum drbd_packet cmd)
 {
-       int ok;
-       struct p_block_req p;
-
-       prepare_header(mdev, &p.head, cmd, sizeof(p) - sizeof(struct p_header) + digest_size);
-       p.sector   = cpu_to_be64(sector);
-       p.block_id = ID_SYNCER /* unused */;
-       p.blksize  = cpu_to_be32(size);
-
-       mutex_lock(&mdev->tconn->data.mutex);
+       struct drbd_socket *sock;
+       struct p_block_req *p;
 
-       ok = (sizeof(p) == drbd_send(mdev->tconn, mdev->tconn->data.socket, &p, sizeof(p), 0));
-       ok = ok && (digest_size == drbd_send(mdev->tconn, mdev->tconn->data.socket, digest, digest_size, 0));
+       /* FIXME: Put the digest into the preallocated socket buffer.  */
 
-       mutex_unlock(&mdev->tconn->data.mutex);
-
-       return ok;
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(sector);
+       p->block_id = ID_SYNCER /* unused */;
+       p->blksize = cpu_to_be32(size);
+       return drbd_send_command(mdev, sock, cmd, sizeof(*p),
+                                digest, digest_size);
 }
 
 int drbd_send_ov_request(struct drbd_conf *mdev, sector_t sector, int size)
 {
-       int ok;
-       struct p_block_req p;
-
-       p.sector   = cpu_to_be64(sector);
-       p.block_id = ID_SYNCER /* unused */;
-       p.blksize  = cpu_to_be32(size);
+       struct drbd_socket *sock;
+       struct p_block_req *p;
 
-       ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_OV_REQUEST, &p.head, sizeof(p));
-       return ok;
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(sector);
+       p->block_id = ID_SYNCER /* unused */;
+       p->blksize = cpu_to_be32(size);
+       return drbd_send_command(mdev, sock, P_OV_REQUEST, sizeof(*p), NULL, 0);
 }
 
 /* called on sndtimeo
@@ -1398,21 +1549,28 @@ static void drbd_update_congested(struct drbd_tconn *tconn)
  * with page_count == 0 or PageSlab.
  */
 static int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page,
-                  int offset, size_t size, unsigned msg_flags)
+                             int offset, size_t size, unsigned msg_flags)
 {
-       int sent = drbd_send(mdev->tconn, mdev->tconn->data.socket, kmap(page) + offset, size, msg_flags);
+       struct socket *socket;
+       void *addr;
+       int err;
+
+       socket = mdev->tconn->data.socket;
+       addr = kmap(page) + offset;
+       err = drbd_send_all(mdev->tconn, socket, addr, size, msg_flags);
        kunmap(page);
-       if (sent == size)
-               mdev->send_cnt += size>>9;
-       return sent == size;
+       if (!err)
+               mdev->send_cnt += size >> 9;
+       return err;
 }
 
 static int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
                    int offset, size_t size, unsigned msg_flags)
 {
+       struct socket *socket = mdev->tconn->data.socket;
        mm_segment_t oldfs = get_fs();
-       int sent, ok;
        int len = size;
+       int err = -EIO;
 
        /* e.g. XFS meta- & log-data is in slab pages, which have a
         * page_count of 0 and/or have PageSlab() set.
@@ -1427,19 +1585,19 @@ static int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
        drbd_update_congested(mdev->tconn);
        set_fs(KERNEL_DS);
        do {
-               sent = mdev->tconn->data.socket->ops->sendpage(mdev->tconn->data.socket, page,
-                                                       offset, len,
-                                                       msg_flags);
-               if (sent == -EAGAIN) {
-                       if (we_should_drop_the_connection(mdev->tconn,
-                                                         mdev->tconn->data.socket))
-                               break;
-                       else
-                               continue;
-               }
+               int sent;
+
+               sent = socket->ops->sendpage(socket, page, offset, len, msg_flags);
                if (sent <= 0) {
+                       if (sent == -EAGAIN) {
+                               if (we_should_drop_the_connection(mdev->tconn, socket))
+                                       break;
+                               continue;
+                       }
                        dev_warn(DEV, "%s: size=%d len=%d sent=%d\n",
                             __func__, (int)size, len, sent);
+                       if (sent < 0)
+                               err = sent;
                        break;
                }
                len    -= sent;
@@ -1448,10 +1606,11 @@ static int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
        set_fs(oldfs);
        clear_bit(NET_CONGESTED, &mdev->tconn->flags);
 
-       ok = (len == 0);
-       if (likely(ok))
-               mdev->send_cnt += size>>9;
-       return ok;
+       if (len == 0) {
+               err = 0;
+               mdev->send_cnt += size >> 9;
+       }
+       return err;
 }
 
 static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio)
@@ -1460,12 +1619,15 @@ static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio)
        int i;
        /* hint all but last page with MSG_MORE */
        __bio_for_each_segment(bvec, bio, i, 0) {
-               if (!_drbd_no_send_page(mdev, bvec->bv_page,
-                                    bvec->bv_offset, bvec->bv_len,
-                                    i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
-                       return 0;
+               int err;
+
+               err = _drbd_no_send_page(mdev, bvec->bv_page,
+                                        bvec->bv_offset, bvec->bv_len,
+                                        i == bio->bi_vcnt - 1 ? 0 : MSG_MORE);
+               if (err)
+                       return err;
        }
-       return 1;
+       return 0;
 }
 
 static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio)
@@ -1474,12 +1636,15 @@ static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio)
        int i;
        /* hint all but last page with MSG_MORE */
        __bio_for_each_segment(bvec, bio, i, 0) {
-               if (!_drbd_send_page(mdev, bvec->bv_page,
-                                    bvec->bv_offset, bvec->bv_len,
-                                    i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
-                       return 0;
+               int err;
+
+               err = _drbd_send_page(mdev, bvec->bv_page,
+                                     bvec->bv_offset, bvec->bv_len,
+                                     i == bio->bi_vcnt - 1 ? 0 : MSG_MORE);
+               if (err)
+                       return err;
        }
-       return 1;
+       return 0;
 }
 
 static int _drbd_send_zc_ee(struct drbd_conf *mdev,
@@ -1487,16 +1652,19 @@ static int _drbd_send_zc_ee(struct drbd_conf *mdev,
 {
        struct page *page = peer_req->pages;
        unsigned len = peer_req->i.size;
+       int err;
 
        /* hint all but last page with MSG_MORE */
        page_chain_for_each(page) {
                unsigned l = min_t(unsigned, len, PAGE_SIZE);
-               if (!_drbd_send_page(mdev, page, 0, l,
-                               page_chain_next(page) ? MSG_MORE : 0))
-                       return 0;
+
+               err = _drbd_send_page(mdev, page, 0, l,
+                                     page_chain_next(page) ? MSG_MORE : 0);
+               if (err)
+                       return err;
                len -= l;
        }
-       return 1;
+       return 0;
 }
 
 static u32 bio_flags_to_wire(struct drbd_conf *mdev, unsigned long bi_rw)
@@ -1515,39 +1683,37 @@ static u32 bio_flags_to_wire(struct drbd_conf *mdev, unsigned long bi_rw)
  */
 int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
 {
-       int ok = 1;
-       struct p_data p;
+       struct drbd_socket *sock;
+       struct p_data *p;
        unsigned int dp_flags = 0;
-       void *dgb;
        int dgs;
-
-       if (!drbd_get_data_sock(mdev->tconn))
-               return 0;
+       int err;
 
        dgs = (mdev->tconn->agreed_pro_version >= 87 && mdev->tconn->integrity_w_tfm) ?
                crypto_hash_digestsize(mdev->tconn->integrity_w_tfm) : 0;
 
-       prepare_header(mdev, &p.head, P_DATA, sizeof(p) - sizeof(struct p_header) + dgs + req->i.size);
-       p.sector   = cpu_to_be64(req->i.sector);
-       p.block_id = (unsigned long)req;
-       p.seq_num  = cpu_to_be32(req->seq_num = atomic_inc_return(&mdev->packet_seq));
-
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(req->i.sector);
+       p->block_id = (unsigned long)req;
+       p->seq_num = cpu_to_be32(req->seq_num = atomic_inc_return(&mdev->packet_seq));
        dp_flags = bio_flags_to_wire(mdev, req->master_bio->bi_rw);
-
        if (mdev->state.conn >= C_SYNC_SOURCE &&
            mdev->state.conn <= C_PAUSED_SYNC_T)
                dp_flags |= DP_MAY_SET_IN_SYNC;
-
-       p.dp_flags = cpu_to_be32(dp_flags);
-       set_bit(UNPLUG_REMOTE, &mdev->flags);
-       ok = (sizeof(p) ==
-               drbd_send(mdev->tconn, mdev->tconn->data.socket, &p, sizeof(p), dgs ? MSG_MORE : 0));
-       if (ok && dgs) {
-               dgb = mdev->tconn->int_dig_out;
-               drbd_csum_bio(mdev, mdev->tconn->integrity_w_tfm, req->master_bio, dgb);
-               ok = dgs == drbd_send(mdev->tconn, mdev->tconn->data.socket, dgb, dgs, 0);
-       }
-       if (ok) {
+       if (mdev->tconn->agreed_pro_version >= 100) {
+               if (req->rq_state & RQ_EXP_RECEIVE_ACK)
+                       dp_flags |= DP_SEND_RECEIVE_ACK;
+               if (req->rq_state & RQ_EXP_WRITE_ACK)
+                       dp_flags |= DP_SEND_WRITE_ACK;
+       }
+       p->dp_flags = cpu_to_be32(dp_flags);
+       if (dgs)
+               drbd_csum_bio(mdev, mdev->tconn->integrity_w_tfm, req->master_bio, p + 1);
+       err = __send_command(mdev->tconn, mdev->vnr, sock, P_DATA, sizeof(*p) + dgs, NULL, req->i.size);
+       if (!err) {
                /* For protocol A, we have to memcpy the payload into
                 * socket buffers, as we may complete right away
                 * as soon as we handed it over to tcp, at which point the data
@@ -1559,10 +1725,10 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
                 * out ok after sending on this side, but does not fit on the
                 * receiving side, we sure have detected corruption elsewhere.
                 */
-               if (mdev->tconn->net_conf->wire_protocol == DRBD_PROT_A || dgs)
-                       ok = _drbd_send_bio(mdev, req->master_bio);
+               if (!(req->rq_state & (RQ_EXP_RECEIVE_ACK | RQ_EXP_WRITE_ACK)) || dgs)
+                       err = _drbd_send_bio(mdev, req->master_bio);
                else
-                       ok = _drbd_send_zc_bio(mdev, req->master_bio);
+                       err = _drbd_send_zc_bio(mdev, req->master_bio);
 
                /* double check digest, sometimes buffers have been modified in flight. */
                if (dgs > 0 && dgs <= 64) {
@@ -1570,7 +1736,7 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
                         * currently supported in kernel crypto. */
                        unsigned char digest[64];
                        drbd_csum_bio(mdev, mdev->tconn->integrity_w_tfm, req->master_bio, digest);
-                       if (memcmp(mdev->tconn->int_dig_out, digest, dgs)) {
+                       if (memcmp(p + 1, digest, dgs)) {
                                dev_warn(DEV,
                                        "Digest mismatch, buffer modified by upper layers during write: %llus +%u\n",
                                        (unsigned long long)req->i.sector, req->i.size);
@@ -1579,10 +1745,9 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
                     ... Be noisy about digest too large ...
                } */
        }
+       mutex_unlock(&sock->mutex);  /* locked by drbd_prepare_command() */
 
-       drbd_put_data_sock(mdev->tconn);
-
-       return ok;
+       return err;
 }
 
 /* answer packet, used to send data back for read requests:
@@ -1592,50 +1757,43 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
 int drbd_send_block(struct drbd_conf *mdev, enum drbd_packet cmd,
                    struct drbd_peer_request *peer_req)
 {
-       int ok;
-       struct p_data p;
-       void *dgb;
+       struct drbd_socket *sock;
+       struct p_data *p;
+       int err;
        int dgs;
 
        dgs = (mdev->tconn->agreed_pro_version >= 87 && mdev->tconn->integrity_w_tfm) ?
                crypto_hash_digestsize(mdev->tconn->integrity_w_tfm) : 0;
 
-       prepare_header(mdev, &p.head, cmd, sizeof(p) -
-                                          sizeof(struct p_header80) +
-                                          dgs + peer_req->i.size);
-       p.sector   = cpu_to_be64(peer_req->i.sector);
-       p.block_id = peer_req->block_id;
-       p.seq_num = 0;  /* unused */
-
-       /* Only called by our kernel thread.
-        * This one may be interrupted by DRBD_SIG and/or DRBD_SIGKILL
-        * in response to admin command or module unload.
-        */
-       if (!drbd_get_data_sock(mdev->tconn))
-               return 0;
-
-       ok = sizeof(p) == drbd_send(mdev->tconn, mdev->tconn->data.socket, &p, sizeof(p), dgs ? MSG_MORE : 0);
-       if (ok && dgs) {
-               dgb = mdev->tconn->int_dig_out;
-               drbd_csum_ee(mdev, mdev->tconn->integrity_w_tfm, peer_req, dgb);
-               ok = dgs == drbd_send(mdev->tconn, mdev->tconn->data.socket, dgb, dgs, 0);
-       }
-       if (ok)
-               ok = _drbd_send_zc_ee(mdev, peer_req);
-
-       drbd_put_data_sock(mdev->tconn);
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(peer_req->i.sector);
+       p->block_id = peer_req->block_id;
+       p->seq_num = 0;  /* unused */
+       if (dgs)
+               drbd_csum_ee(mdev, mdev->tconn->integrity_w_tfm, peer_req, p + 1);
+       err = __send_command(mdev->tconn, mdev->vnr, sock, cmd, sizeof(*p) + dgs, NULL, peer_req->i.size);
+       if (!err)
+               err = _drbd_send_zc_ee(mdev, peer_req);
+       mutex_unlock(&sock->mutex);  /* locked by drbd_prepare_command() */
 
-       return ok;
+       return err;
 }
 
-int drbd_send_oos(struct drbd_conf *mdev, struct drbd_request *req)
+int drbd_send_out_of_sync(struct drbd_conf *mdev, struct drbd_request *req)
 {
-       struct p_block_desc p;
-
-       p.sector  = cpu_to_be64(req->i.sector);
-       p.blksize = cpu_to_be32(req->i.size);
+       struct drbd_socket *sock;
+       struct p_block_desc *p;
 
-       return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_OUT_OF_SYNC, &p.head, sizeof(p));
+       sock = &mdev->tconn->data;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
+               return -EIO;
+       p->sector = cpu_to_be64(req->i.sector);
+       p->blksize = cpu_to_be32(req->i.size);
+       return drbd_send_command(mdev, sock, P_OUT_OF_SYNC, sizeof(*p), NULL, 0);
 }
 
 /*
@@ -1662,7 +1820,7 @@ int drbd_send(struct drbd_tconn *tconn, struct socket *sock,
        int rv, sent = 0;
 
        if (!sock)
-               return -1000;
+               return -EBADR;
 
        /* THINK  if (signal_pending) return ... ? */
 
@@ -1676,7 +1834,9 @@ int drbd_send(struct drbd_tconn *tconn, struct socket *sock,
        msg.msg_flags      = msg_flags | MSG_NOSIGNAL;
 
        if (sock == tconn->data.socket) {
-               tconn->ko_count = tconn->net_conf->ko_count;
+               rcu_read_lock();
+               tconn->ko_count = rcu_dereference(tconn->net_conf)->ko_count;
+               rcu_read_unlock();
                drbd_update_congested(tconn);
        }
        do {
@@ -1723,6 +1883,24 @@ int drbd_send(struct drbd_tconn *tconn, struct socket *sock,
        return sent;
 }
 
+/**
+ * drbd_send_all  -  Send an entire buffer
+ *
+ * Returns 0 upon success and a negative error value otherwise.
+ */
+int drbd_send_all(struct drbd_tconn *tconn, struct socket *sock, void *buffer,
+                 size_t size, unsigned msg_flags)
+{
+       int err;
+
+       err = drbd_send(tconn, sock, buffer, size, msg_flags);
+       if (err < 0)
+               return err;
+       if (err != size)
+               return -EIO;
+       return 0;
+}
+
 static int drbd_open(struct block_device *bdev, fmode_t mode)
 {
        struct drbd_conf *mdev = bdev->bd_disk->private_data;
@@ -1760,35 +1938,14 @@ static int drbd_release(struct gendisk *gd, fmode_t mode)
 
 static void drbd_set_defaults(struct drbd_conf *mdev)
 {
-       /* This way we get a compile error when sync_conf grows,
-          and we forgot to initialize it here */
-       mdev->sync_conf = (struct syncer_conf) {
-               /* .rate = */           DRBD_RATE_DEF,
-               /* .after = */          DRBD_AFTER_DEF,
-               /* .al_extents = */     DRBD_AL_EXTENTS_DEF,
-               /* .verify_alg = */     {}, 0,
-               /* .cpu_mask = */       {}, 0,
-               /* .csums_alg = */      {}, 0,
-               /* .use_rle = */        0,
-               /* .on_no_data = */     DRBD_ON_NO_DATA_DEF,
-               /* .c_plan_ahead = */   DRBD_C_PLAN_AHEAD_DEF,
-               /* .c_delay_target = */ DRBD_C_DELAY_TARGET_DEF,
-               /* .c_fill_target = */  DRBD_C_FILL_TARGET_DEF,
-               /* .c_max_rate = */     DRBD_C_MAX_RATE_DEF,
-               /* .c_min_rate = */     DRBD_C_MIN_RATE_DEF
-       };
-
-       /* Have to use that way, because the layout differs between
-          big endian and little endian */
-       mdev->state = (union drbd_state) {
+       /* Beware! The actual layout differs
+        * between big endian and little endian */
+       mdev->state = (union drbd_dev_state) {
                { .role = R_SECONDARY,
                  .peer = R_UNKNOWN,
                  .conn = C_STANDALONE,
                  .disk = D_DISKLESS,
                  .pdsk = D_UNKNOWN,
-                 .susp = 0,
-                 .susp_nod = 0,
-                 .susp_fen = 0
                } };
 }
 
@@ -1804,7 +1961,6 @@ void drbd_init_set_defaults(struct drbd_conf *mdev)
        atomic_set(&mdev->rs_pending_cnt, 0);
        atomic_set(&mdev->unacked_cnt, 0);
        atomic_set(&mdev->local_cnt, 0);
-       atomic_set(&mdev->pp_in_use, 0);
        atomic_set(&mdev->pp_in_use_by_net, 0);
        atomic_set(&mdev->rs_sect_in, 0);
        atomic_set(&mdev->rs_sect_ev, 0);
@@ -1906,13 +2062,11 @@ void drbd_mdev_cleanup(struct drbd_conf *mdev)
                drbd_bm_cleanup(mdev);
        }
 
-       drbd_free_resources(mdev);
+       drbd_free_bc(mdev->ldev);
+       mdev->ldev = NULL;
+
        clear_bit(AL_SUSPENDED, &mdev->flags);
 
-       /*
-        * currently we drbd_init_ee only on module load, so
-        * we may do drbd_release_ee only on module unload!
-        */
        D_ASSERT(list_empty(&mdev->active_ee));
        D_ASSERT(list_empty(&mdev->sync_ee));
        D_ASSERT(list_empty(&mdev->done_ee));
@@ -1942,6 +2096,10 @@ static void drbd_destroy_mempools(void)
 
        /* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */
 
+       if (drbd_md_io_bio_set)
+               bioset_free(drbd_md_io_bio_set);
+       if (drbd_md_io_page_pool)
+               mempool_destroy(drbd_md_io_page_pool);
        if (drbd_ee_mempool)
                mempool_destroy(drbd_ee_mempool);
        if (drbd_request_mempool)
@@ -1955,6 +2113,8 @@ static void drbd_destroy_mempools(void)
        if (drbd_al_ext_cache)
                kmem_cache_destroy(drbd_al_ext_cache);
 
+       drbd_md_io_bio_set   = NULL;
+       drbd_md_io_page_pool = NULL;
        drbd_ee_mempool      = NULL;
        drbd_request_mempool = NULL;
        drbd_ee_cache        = NULL;
@@ -1978,6 +2138,8 @@ static int drbd_create_mempools(void)
        drbd_bm_ext_cache    = NULL;
        drbd_al_ext_cache    = NULL;
        drbd_pp_pool         = NULL;
+       drbd_md_io_page_pool = NULL;
+       drbd_md_io_bio_set   = NULL;
 
        /* caches */
        drbd_request_cache = kmem_cache_create(
@@ -2001,6 +2163,14 @@ static int drbd_create_mempools(void)
                goto Enomem;
 
        /* mempools */
+       drbd_md_io_bio_set = bioset_create(DRBD_MIN_POOL_PAGES, 0);
+       if (drbd_md_io_bio_set == NULL)
+               goto Enomem;
+
+       drbd_md_io_page_pool = mempool_create_page_pool(DRBD_MIN_POOL_PAGES, 0);
+       if (drbd_md_io_page_pool == NULL)
+               goto Enomem;
+
        drbd_request_mempool = mempool_create(number,
                mempool_alloc_slab, mempool_free_slab, drbd_request_cache);
        if (drbd_request_mempool == NULL)
@@ -2044,39 +2214,39 @@ static struct notifier_block drbd_notifier = {
        .notifier_call = drbd_notify_sys,
 };
 
-static void drbd_release_ee_lists(struct drbd_conf *mdev)
+static void drbd_release_all_peer_reqs(struct drbd_conf *mdev)
 {
        int rr;
 
-       rr = drbd_release_ee(mdev, &mdev->active_ee);
+       rr = drbd_free_peer_reqs(mdev, &mdev->active_ee);
        if (rr)
                dev_err(DEV, "%d EEs in active list found!\n", rr);
 
-       rr = drbd_release_ee(mdev, &mdev->sync_ee);
+       rr = drbd_free_peer_reqs(mdev, &mdev->sync_ee);
        if (rr)
                dev_err(DEV, "%d EEs in sync list found!\n", rr);
 
-       rr = drbd_release_ee(mdev, &mdev->read_ee);
+       rr = drbd_free_peer_reqs(mdev, &mdev->read_ee);
        if (rr)
                dev_err(DEV, "%d EEs in read list found!\n", rr);
 
-       rr = drbd_release_ee(mdev, &mdev->done_ee);
+       rr = drbd_free_peer_reqs(mdev, &mdev->done_ee);
        if (rr)
                dev_err(DEV, "%d EEs in done list found!\n", rr);
 
-       rr = drbd_release_ee(mdev, &mdev->net_ee);
+       rr = drbd_free_peer_reqs(mdev, &mdev->net_ee);
        if (rr)
                dev_err(DEV, "%d EEs in net list found!\n", rr);
 }
 
-/* caution. no locking.
- * currently only used from module cleanup code. */
-static void drbd_delete_device(unsigned int minor)
+/* caution. no locking. */
+void drbd_delete_device(struct drbd_conf *mdev)
 {
-       struct drbd_conf *mdev = minor_to_mdev(minor);
+       struct drbd_tconn *tconn = mdev->tconn;
 
-       if (!mdev)
-               return;
+       idr_remove(&mdev->tconn->volumes, mdev->vnr);
+       idr_remove(&minors, mdev_to_minor(mdev));
+       synchronize_rcu();
 
        /* paranoia asserts */
        D_ASSERT(mdev->open_cnt == 0);
@@ -2091,10 +2261,10 @@ static void drbd_delete_device(unsigned int minor)
        if (mdev->this_bdev)
                bdput(mdev->this_bdev);
 
-       drbd_free_resources(mdev);
-       drbd_free_tconn(mdev->tconn);
+       drbd_free_bc(mdev->ldev);
+       mdev->ldev = NULL;
 
-       drbd_release_ee_lists(mdev);
+       drbd_release_all_peer_reqs(mdev);
 
        lc_destroy(mdev->act_log);
        lc_destroy(mdev->resync);
@@ -2102,15 +2272,21 @@ static void drbd_delete_device(unsigned int minor)
        kfree(mdev->p_uuid);
        /* mdev->p_uuid = NULL; */
 
-       /* cleanup the rest that has been
-        * allocated from drbd_new_device
-        * and actually free the mdev itself */
-       drbd_free_mdev(mdev);
+       kfree(mdev->current_epoch);
+       if (mdev->bitmap) /* should no longer be there. */
+               drbd_bm_cleanup(mdev);
+       __free_page(mdev->md_io_page);
+       put_disk(mdev->vdisk);
+       blk_cleanup_queue(mdev->rq_queue);
+       kfree(mdev);
+
+       kref_put(&tconn->kref, &conn_destroy);
 }
 
 static void drbd_cleanup(void)
 {
        unsigned int i;
+       struct drbd_conf *mdev;
 
        unregister_reboot_notifier(&drbd_notifier);
 
@@ -2125,19 +2301,18 @@ static void drbd_cleanup(void)
        if (drbd_proc)
                remove_proc_entry("drbd", NULL);
 
-       drbd_nl_cleanup();
+       drbd_genl_unregister();
 
-       if (minor_table) {
-               i = minor_count;
-               while (i--)
-                       drbd_delete_device(i);
-               drbd_destroy_mempools();
-       }
-
-       kfree(minor_table);
+       down_write(&drbd_cfg_rwsem);
+       idr_for_each_entry(&minors, mdev, i)
+               drbd_delete_device(mdev);
+       up_write(&drbd_cfg_rwsem);
 
+       drbd_destroy_mempools();
        unregister_blkdev(DRBD_MAJOR, "drbd");
 
+       idr_destroy(&minors);
+
        printk(KERN_INFO "drbd: module cleanup done.\n");
 }
 
@@ -2187,7 +2362,63 @@ static void drbd_init_workqueue(struct drbd_work_queue* wq)
        INIT_LIST_HEAD(&wq->q);
 }
 
-struct drbd_tconn *drbd_new_tconn(char *name)
+struct drbd_tconn *conn_by_name(const char *name)
+{
+       struct drbd_tconn *tconn;
+
+       if (!name || !name[0])
+               return NULL;
+
+       down_read(&drbd_cfg_rwsem);
+       list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
+               if (!strcmp(tconn->name, name))
+                       goto found;
+       }
+       tconn = NULL;
+found:
+       up_read(&drbd_cfg_rwsem);
+       return tconn;
+}
+
+static int drbd_alloc_socket(struct drbd_socket *socket)
+{
+       socket->rbuf = (void *) __get_free_page(GFP_KERNEL);
+       if (!socket->rbuf)
+               return -ENOMEM;
+       socket->sbuf = (void *) __get_free_page(GFP_KERNEL);
+       if (!socket->sbuf)
+               return -ENOMEM;
+       return 0;
+}
+
+static void drbd_free_socket(struct drbd_socket *socket)
+{
+       free_page((unsigned long) socket->sbuf);
+       free_page((unsigned long) socket->rbuf);
+}
+
+void conn_free_crypto(struct drbd_tconn *tconn)
+{
+       drbd_free_sock(tconn);
+
+       crypto_free_hash(tconn->csums_tfm);
+       crypto_free_hash(tconn->verify_tfm);
+       crypto_free_hash(tconn->cram_hmac_tfm);
+       crypto_free_hash(tconn->integrity_w_tfm);
+       crypto_free_hash(tconn->integrity_r_tfm);
+       kfree(tconn->int_dig_in);
+       kfree(tconn->int_dig_vv);
+
+       tconn->csums_tfm = NULL;
+       tconn->verify_tfm = NULL;
+       tconn->cram_hmac_tfm = NULL;
+       tconn->integrity_w_tfm = NULL;
+       tconn->integrity_r_tfm = NULL;
+       tconn->int_dig_in = NULL;
+       tconn->int_dig_vv = NULL;
+}
+
+struct drbd_tconn *conn_create(const char *name)
 {
        struct drbd_tconn *tconn;
 
@@ -2199,11 +2430,21 @@ struct drbd_tconn *drbd_new_tconn(char *name)
        if (!tconn->name)
                goto fail;
 
+       if (drbd_alloc_socket(&tconn->data))
+               goto fail;
+       if (drbd_alloc_socket(&tconn->meta))
+               goto fail;
+
+       if (!zalloc_cpumask_var(&tconn->cpu_mask, GFP_KERNEL))
+               goto fail;
+
+       if (!tl_init(tconn))
+               goto fail;
+
        tconn->cstate = C_STANDALONE;
        mutex_init(&tconn->cstate_mutex);
        spin_lock_init(&tconn->req_lock);
-       atomic_set(&tconn->net_cnt, 0);
-       init_waitqueue_head(&tconn->net_cnt_wait);
+       mutex_init(&tconn->net_conf_update);
        init_waitqueue_head(&tconn->ping_wait);
        idr_init(&tconn->volumes);
 
@@ -2217,61 +2458,67 @@ struct drbd_tconn *drbd_new_tconn(char *name)
        drbd_thread_init(tconn, &tconn->worker, drbd_worker, "worker");
        drbd_thread_init(tconn, &tconn->asender, drbd_asender, "asender");
 
-       write_lock_irq(&global_state_lock);
-       list_add(&tconn->all_tconn, &drbd_tconns);
-       write_unlock_irq(&global_state_lock);
+       tconn->res_opts = (struct res_opts) {
+               {}, 0, /* cpu_mask */
+               DRBD_ON_NO_DATA_DEF, /* on_no_data */
+       };
+
+       down_write(&drbd_cfg_rwsem);
+       kref_init(&tconn->kref);
+       list_add_tail(&tconn->all_tconn, &drbd_tconns);
+       up_write(&drbd_cfg_rwsem);
 
        return tconn;
 
 fail:
+       tl_cleanup(tconn);
+       free_cpumask_var(tconn->cpu_mask);
+       drbd_free_socket(&tconn->meta);
+       drbd_free_socket(&tconn->data);
        kfree(tconn->name);
        kfree(tconn);
 
        return NULL;
 }
 
-void drbd_free_tconn(struct drbd_tconn *tconn)
+void conn_destroy(struct kref *kref)
 {
-       write_lock_irq(&global_state_lock);
-       list_del(&tconn->all_tconn);
-       write_unlock_irq(&global_state_lock);
+       struct drbd_tconn *tconn = container_of(kref, struct drbd_tconn, kref);
+
        idr_destroy(&tconn->volumes);
 
+       free_cpumask_var(tconn->cpu_mask);
+       drbd_free_socket(&tconn->meta);
+       drbd_free_socket(&tconn->data);
        kfree(tconn->name);
-       kfree(tconn->int_dig_out);
        kfree(tconn->int_dig_in);
        kfree(tconn->int_dig_vv);
        kfree(tconn);
 }
 
-struct drbd_conf *drbd_new_device(unsigned int minor)
+enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr)
 {
        struct drbd_conf *mdev;
        struct gendisk *disk;
        struct request_queue *q;
-       char conn_name[9]; /* drbd1234N */
-       int vnr;
+       int vnr_got = vnr;
+       int minor_got = minor;
+       enum drbd_ret_code err = ERR_NOMEM;
+
+       mdev = minor_to_mdev(minor);
+       if (mdev)
+               return ERR_MINOR_EXISTS;
 
        /* GFP_KERNEL, we are outside of all write-out paths */
        mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL);
        if (!mdev)
-               return NULL;
-       sprintf(conn_name, "drbd%d", minor);
-       mdev->tconn = drbd_new_tconn(conn_name);
-       if (!mdev->tconn)
-               goto out_no_tconn;
-       if (!idr_pre_get(&mdev->tconn->volumes, GFP_KERNEL))
-               goto out_no_cpumask;
-       if (idr_get_new(&mdev->tconn->volumes, mdev, &vnr))
-               goto out_no_cpumask;
-       if (vnr != 0) {
-               dev_err(DEV, "vnr = %d\n", vnr);
-               goto out_no_cpumask;
-       }
-       if (!zalloc_cpumask_var(&mdev->tconn->cpu_mask, GFP_KERNEL))
-               goto out_no_cpumask;
+               return ERR_NOMEM;
+
+       kref_get(&tconn->kref);
+       mdev->tconn = tconn;
 
        mdev->minor = minor;
+       mdev->vnr = vnr;
 
        drbd_init_set_defaults(mdev);
 
@@ -2316,9 +2563,6 @@ 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 this minor device. */
-       if (!tl_init(mdev))
-               goto out_no_tl;
        mdev->read_requests = RB_ROOT;
        mdev->write_requests = RB_ROOT;
 
@@ -2329,13 +2573,42 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
        INIT_LIST_HEAD(&mdev->current_epoch->list);
        mdev->epochs = 1;
 
-       return mdev;
-
-/* out_whatever_else:
-       kfree(mdev->current_epoch); */
+       if (!idr_pre_get(&minors, GFP_KERNEL))
+               goto out_no_minor_idr;
+       if (idr_get_new_above(&minors, mdev, minor, &minor_got))
+               goto out_no_minor_idr;
+       if (minor_got != minor) {
+               err = ERR_MINOR_EXISTS;
+               drbd_msg_put_info("requested minor exists already");
+               goto out_idr_remove_minor;
+       }
+
+       if (!idr_pre_get(&tconn->volumes, GFP_KERNEL))
+               goto out_idr_remove_minor;
+       if (idr_get_new_above(&tconn->volumes, mdev, vnr, &vnr_got))
+               goto out_idr_remove_minor;
+       if (vnr_got != vnr) {
+               err = ERR_INVALID_REQUEST;
+               drbd_msg_put_info("requested volume exists already");
+               goto out_idr_remove_vol;
+       }
+       add_disk(disk);
+
+       /* inherit the connection state */
+       mdev->state.conn = tconn->cstate;
+       if (mdev->state.conn == C_WF_REPORT_PARAMS)
+               drbd_connected(vnr, mdev, tconn);
+
+       return NO_ERROR;
+
+out_idr_remove_vol:
+       idr_remove(&tconn->volumes, vnr_got);
+out_idr_remove_minor:
+       idr_remove(&minors, minor_got);
+       synchronize_rcu();
+out_no_minor_idr:
+       kfree(mdev->current_epoch);
 out_no_epoch:
-       tl_cleanup(mdev);
-out_no_tl:
        drbd_bm_cleanup(mdev);
 out_no_bitmap:
        __free_page(mdev->md_io_page);
@@ -2344,39 +2617,18 @@ out_no_io_page:
 out_no_disk:
        blk_cleanup_queue(q);
 out_no_q:
-       free_cpumask_var(mdev->tconn->cpu_mask);
-out_no_cpumask:
-       drbd_free_tconn(mdev->tconn);
-out_no_tconn:
-       kfree(mdev);
-       return NULL;
-}
-
-/* counterpart of drbd_new_device.
- * last part of drbd_delete_device. */
-void drbd_free_mdev(struct drbd_conf *mdev)
-{
-       kfree(mdev->current_epoch);
-       tl_cleanup(mdev);
-       if (mdev->bitmap) /* should no longer be there. */
-               drbd_bm_cleanup(mdev);
-       __free_page(mdev->md_io_page);
-       put_disk(mdev->vdisk);
-       blk_cleanup_queue(mdev->rq_queue);
        kfree(mdev);
+       kref_put(&tconn->kref, &conn_destroy);
+       return err;
 }
 
-
 int __init drbd_init(void)
 {
        int err;
 
-       BUILD_BUG_ON(sizeof(struct p_header80) != sizeof(struct p_header95));
-       BUILD_BUG_ON(sizeof(struct p_handshake) != 80);
-
        if (minor_count < DRBD_MINOR_COUNT_MIN || minor_count > DRBD_MINOR_COUNT_MAX) {
                printk(KERN_ERR
-                       "drbd: invalid minor_count (%d)\n", minor_count);
+                      "drbd: invalid minor_count (%d)\n", minor_count);
 #ifdef MODULE
                return -EINVAL;
 #else
@@ -2384,10 +2636,6 @@ int __init drbd_init(void)
 #endif
        }
 
-       err = drbd_nl_init();
-       if (err)
-               return err;
-
        err = register_blkdev(DRBD_MAJOR, "drbd");
        if (err) {
                printk(KERN_ERR
@@ -2396,6 +2644,13 @@ int __init drbd_init(void)
                return err;
        }
 
+       err = drbd_genl_register();
+       if (err) {
+               printk(KERN_ERR "drbd: unable to register generic netlink family\n");
+               goto fail;
+       }
+
+
        register_reboot_notifier(&drbd_notifier);
 
        /*
@@ -2406,19 +2661,16 @@ int __init drbd_init(void)
        init_waitqueue_head(&drbd_pp_wait);
 
        drbd_proc = NULL; /* play safe for drbd_cleanup */
-       minor_table = kzalloc(sizeof(struct drbd_conf *)*minor_count,
-                               GFP_KERNEL);
-       if (!minor_table)
-               goto Enomem;
+       idr_init(&minors);
 
        err = drbd_create_mempools();
        if (err)
-               goto Enomem;
+               goto fail;
 
        drbd_proc = proc_create_data("drbd", S_IFREG | S_IRUGO , NULL, &drbd_proc_fops, NULL);
        if (!drbd_proc) {
                printk(KERN_ERR "drbd: unable to register proc file\n");
-               goto Enomem;
+               goto fail;
        }
 
        rwlock_init(&global_state_lock);
@@ -2430,11 +2682,10 @@ int __init drbd_init(void)
        printk(KERN_INFO "drbd: %s\n", drbd_buildtag());
        printk(KERN_INFO "drbd: registered as block device major %d\n",
                DRBD_MAJOR);
-       printk(KERN_INFO "drbd: minor_table @ 0x%p\n", minor_table);
 
        return 0; /* Success! */
 
-Enomem:
+fail:
        drbd_cleanup();
        if (err == -ENOMEM)
                /* currently always the case */
@@ -2473,27 +2724,6 @@ void drbd_free_sock(struct drbd_tconn *tconn)
        }
 }
 
-
-void drbd_free_resources(struct drbd_conf *mdev)
-{
-       crypto_free_hash(mdev->csums_tfm);
-       mdev->csums_tfm = NULL;
-       crypto_free_hash(mdev->verify_tfm);
-       mdev->verify_tfm = NULL;
-       crypto_free_hash(mdev->tconn->cram_hmac_tfm);
-       mdev->tconn->cram_hmac_tfm = NULL;
-       crypto_free_hash(mdev->tconn->integrity_w_tfm);
-       mdev->tconn->integrity_w_tfm = NULL;
-       crypto_free_hash(mdev->tconn->integrity_r_tfm);
-       mdev->tconn->integrity_r_tfm = NULL;
-
-       drbd_free_sock(mdev->tconn);
-
-       __no_warn(local,
-                 drbd_free_bc(mdev->ldev);
-                 mdev->ldev = NULL;);
-}
-
 /* meta data management */
 
 struct meta_data_on_disk {
@@ -2506,7 +2736,7 @@ struct meta_data_on_disk {
        u32 md_size_sect;
        u32 al_offset;         /* offset to this block */
        u32 al_nr_extents;     /* important for restoring the AL */
-             /* `-- act_log->nr_elements <-- sync_conf.al_extents */
+             /* `-- act_log->nr_elements <-- ldev->dc.al_extents */
        u32 bm_offset;         /* offset to the bitmap, from here */
        u32 bm_bytes_per_bit;  /* BM_BLOCK_SIZE */
        u32 la_peer_max_bio_size;   /* last peer max_bio_size */
@@ -2556,7 +2786,7 @@ void drbd_md_sync(struct drbd_conf *mdev)
        D_ASSERT(drbd_md_ss__(mdev, mdev->ldev) == mdev->ldev->md.md_offset);
        sector = mdev->ldev->md.md_offset;
 
-       if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
+       if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
                /* this was a try anyways ... */
                dev_err(DEV, "meta data update failed!\n");
                drbd_chk_io_error(mdev, 1, true);
@@ -2589,7 +2819,7 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
        mutex_lock(&mdev->md_io_mutex);
        buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
 
-       if (!drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
+       if (drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
                /* NOTE: can't do normal error processing here as this is
                   called BEFORE disk is attached */
                dev_err(DEV, "Error while reading metadata.\n");
@@ -2632,7 +2862,7 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
        for (i = UI_CURRENT; i < UI_SIZE; i++)
                bdev->md.uuid[i] = be64_to_cpu(buffer->uuid[i]);
        bdev->md.flags = be32_to_cpu(buffer->flags);
-       mdev->sync_conf.al_extents = be32_to_cpu(buffer->al_nr_extents);
+       bdev->dc.al_extents = be32_to_cpu(buffer->al_nr_extents);
        bdev->md.device_uuid = be64_to_cpu(buffer->device_uuid);
 
        spin_lock_irq(&mdev->tconn->req_lock);
@@ -2644,8 +2874,8 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
        }
        spin_unlock_irq(&mdev->tconn->req_lock);
 
-       if (mdev->sync_conf.al_extents < 7)
-               mdev->sync_conf.al_extents = 127;
+       if (bdev->dc.al_extents < 7)
+               bdev->dc.al_extents = 127;
 
  err:
        mutex_unlock(&mdev->md_io_mutex);
@@ -2828,7 +3058,7 @@ static int w_bitmap_io(struct drbd_work *w, int unused)
        work->why = NULL;
        work->flags = 0;
 
-       return 1;
+       return 0;
 }
 
 void drbd_ldev_destroy(struct drbd_conf *mdev)
@@ -2854,7 +3084,7 @@ static int w_go_diskless(struct drbd_work *w, int unused)
         * the protected members anymore, though, so once put_ldev reaches zero
         * again, it will be safe to free them. */
        drbd_force_state(mdev, NS(disk, D_DISKLESS));
-       return 1;
+       return 0;
 }
 
 void drbd_go_diskless(struct drbd_conf *mdev)
@@ -2970,7 +3200,7 @@ static int w_md_sync(struct drbd_work *w, int unused)
                mdev->last_md_mark_dirty.func, mdev->last_md_mark_dirty.line);
 #endif
        drbd_md_sync(mdev);
-       return 1;
+       return 0;
 }
 
 const char *cmdname(enum drbd_packet cmd)
@@ -3003,7 +3233,7 @@ const char *cmdname(enum drbd_packet cmd)
                [P_RECV_ACK]            = "RecvAck",
                [P_WRITE_ACK]           = "WriteAck",
                [P_RS_WRITE_ACK]        = "RSWriteAck",
-               [P_DISCARD_ACK]         = "DiscardAck",
+               [P_DISCARD_WRITE]        = "DiscardWrite",
                [P_NEG_ACK]             = "NegAck",
                [P_NEG_DREPLY]          = "NegDReply",
                [P_NEG_RS_DREPLY]       = "NegRSDReply",
@@ -3018,20 +3248,64 @@ const char *cmdname(enum drbd_packet cmd)
                [P_COMPRESSED_BITMAP]   = "CBitmap",
                [P_DELAY_PROBE]         = "DelayProbe",
                [P_OUT_OF_SYNC]         = "OutOfSync",
-               [P_MAX_CMD]             = NULL,
+               [P_RETRY_WRITE]         = "RetryWrite",
+               [P_RS_CANCEL]           = "RSCancel",
+               [P_CONN_ST_CHG_REQ]     = "conn_st_chg_req",
+               [P_CONN_ST_CHG_REPLY]   = "conn_st_chg_reply",
+
+               /* enum drbd_packet, but not commands - obsoleted flags:
+                *      P_MAY_IGNORE
+                *      P_MAX_OPT_CMD
+                */
        };
 
-       if (cmd == P_HAND_SHAKE_M)
-               return "HandShakeM";
-       if (cmd == P_HAND_SHAKE_S)
-               return "HandShakeS";
-       if (cmd == P_HAND_SHAKE)
-               return "HandShake";
-       if (cmd >= P_MAX_CMD)
+       /* too big for the array: 0xfffX */
+       if (cmd == P_INITIAL_META)
+               return "InitialMeta";
+       if (cmd == P_INITIAL_DATA)
+               return "InitialData";
+       if (cmd == P_CONNECTION_FEATURES)
+               return "ConnectionFeatures";
+       if (cmd >= ARRAY_SIZE(cmdnames))
                return "Unknown";
        return cmdnames[cmd];
 }
 
+/**
+ * drbd_wait_misc  -  wait for a request to make progress
+ * @mdev:      device associated with the request
+ * @i:         the struct drbd_interval embedded in struct drbd_request or
+ *             struct drbd_peer_request
+ */
+int drbd_wait_misc(struct drbd_conf *mdev, struct drbd_interval *i)
+{
+       struct net_conf *nc;
+       DEFINE_WAIT(wait);
+       long timeout;
+
+       rcu_read_lock();
+       nc = rcu_dereference(mdev->tconn->net_conf);
+       if (!nc) {
+               rcu_read_unlock();
+               return -ETIMEDOUT;
+       }
+       timeout = nc->ko_count ? nc->timeout * HZ / 10 * nc->ko_count : MAX_SCHEDULE_TIMEOUT;
+       rcu_read_unlock();
+
+       /* Indicate to wake up mdev->misc_wait on progress.  */
+       i->waiting = true;
+       prepare_to_wait(&mdev->misc_wait, &wait, TASK_INTERRUPTIBLE);
+       spin_unlock_irq(&mdev->tconn->req_lock);
+       timeout = schedule_timeout(timeout);
+       finish_wait(&mdev->misc_wait, &wait);
+       spin_lock_irq(&mdev->tconn->req_lock);
+       if (!timeout || mdev->state.conn < C_CONNECTED)
+               return -ETIMEDOUT;
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       return 0;
+}
+
 #ifdef CONFIG_DRBD_FAULT_INJECTION
 /* Fault insertion support including random number generator shamelessly
  * stolen from kernel/rcutorture.c */