drbd: Basic refcounting for drbd_tconn
[firefly-linux-kernel-4.4.55.git] / drivers / block / drbd / drbd_main.c
index 200791e44aa257c6c065c5dbd65b8ba75ec63ae5..11427f59c5af538f7155e124bbe4d8b28510aab1 100644 (file)
@@ -120,7 +120,7 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0
  */
 struct idr minors;
 struct list_head drbd_tconns;  /* list of struct drbd_tconn */
-DEFINE_MUTEX(drbd_cfg_mutex);
+DECLARE_RWSEM(drbd_cfg_rwsem);
 
 struct kmem_cache *drbd_request_cache;
 struct kmem_cache *drbd_ee_cache;      /* peer requests */
@@ -453,8 +453,10 @@ void tl_clear(struct drbd_tconn *tconn)
        }
 
        /* ensure bit indicating barrier is required is clear */
+       rcu_read_lock();
        idr_for_each_entry(&tconn->volumes, mdev, vnr)
                clear_bit(CREATE_BARRIER, &mdev->flags);
+       rcu_read_unlock();
 
        spin_unlock_irq(&tconn->req_lock);
 }
@@ -507,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;
 }
@@ -544,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;
@@ -556,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;
                }
@@ -634,13 +641,15 @@ char *drbd_task_to_thread_name(struct drbd_tconn *tconn, struct task_struct *tas
 
 int conn_lowest_minor(struct drbd_tconn *tconn)
 {
-       int vnr = 0;
        struct drbd_conf *mdev;
+       int vnr = 0, m;
 
+       rcu_read_lock();
        mdev = idr_get_next(&tconn->volumes, &vnr);
-       if (!mdev)
-               return -1;
-       return mdev_to_minor(mdev);
+       m = mdev ? mdev_to_minor(mdev) : -1;
+       rcu_read_unlock();
+
+       return m;
 }
 
 #ifdef CONFIG_SMP
@@ -689,204 +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);
+       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 unsigned int prepare_header(struct drbd_tconn *tconn, int vnr,
+                                  void *buffer, enum drbd_packet cmd, int 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
-               prepare_header80(&h->h80, cmd, size);
+               return prepare_header80(buffer, cmd, size);
+}
+
+void *conn_prepare_command(struct drbd_tconn *tconn, struct drbd_socket *sock)
+{
+       mutex_lock(&sock->mutex);
+       if (!sock->socket) {
+               mutex_unlock(&sock->mutex);
+               return NULL;
+       }
+       return sock->sbuf + drbd_header_size(tconn);
 }
 
-static void prepare_header(struct drbd_conf *mdev, struct p_header *h,
-                          enum drbd_packet cmd, int size)
+void *drbd_prepare_command(struct drbd_conf *mdev, struct drbd_socket *sock)
 {
-       _prepare_header(mdev->tconn, mdev->vnr, h, cmd, size);
+       return conn_prepare_command(mdev->tconn, sock);
 }
 
-/* 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)
+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 msg_flags;
        int err;
 
-       _prepare_header(tconn, vnr, h, cmd, size - sizeof(struct p_header));
-       err = drbd_send_all(tconn, sock, h, size, msg_flags);
-       if (err && !signal_pending(current))
-               conn_warn(tconn, "short send %s size=%d\n",
-                         cmdname(cmd), (int)size);
+       /*
+        * 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;
 }
 
-/* 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, struct drbd_socket *sock,
-                 enum drbd_packet cmd, struct p_header *h, size_t size)
+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 = -EIO;
+       int err;
 
-       mutex_lock(&sock->mutex);
-       if (sock->socket)
-               err = _conn_send_cmd(tconn, vnr, sock->socket, cmd, h, size, 0);
+       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 err;
 
-       prepare_header80(&h, cmd, size);
-       err = drbd_get_data_sock(tconn);
-       if (!err) {
-               err = drbd_send_all(tconn, tconn->data.socket, &h, sizeof(h), 0);
-               if (!err)
-                       err = drbd_send_all(tconn, tconn->data.socket, data, size, 0);
-               drbd_put_data_sock(tconn);
-       }
+       err = __send_command(mdev->tconn, mdev->vnr, sock, cmd, header_size,
+                            data, size);
+       mutex_unlock(&sock->mutex);
        return err;
 }
 
+int drbd_send_ping(struct drbd_tconn *tconn)
+{
+       struct drbd_socket *sock;
+
+       sock = &tconn->meta;
+       if (!conn_prepare_command(tconn, sock))
+               return -EIO;
+       return conn_send_command(tconn, sock, P_PING, 0, NULL, 0);
+}
+
+int drbd_send_ping_ack(struct drbd_tconn *tconn)
+{
+       struct drbd_socket *sock;
+
+       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 drbd_socket *sock;
        struct p_rs_param_95 *p;
-       struct socket *sock;
-       int size, err;
+       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->tconn->net_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);
-
-               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);
-               }
+       cmd = apv >= 89 ? P_SYNC_PARAM89 : P_SYNC_PARAM;
 
-               if (apv >= 88)
-                       strcpy(p->verify_alg, mdev->tconn->net_conf->verify_alg);
-               if (apv >= 89)
-                       strcpy(p->csums_alg, mdev->tconn->net_conf->csums_alg);
+       /* initialize verify_alg and csums_alg */
+       memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX);
 
-               err = _drbd_send_cmd(mdev, sock, cmd, &p->head, size, 0);
-       } else
-               err = -EIO;
+       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 err;
+       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, err;
+       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 -ENOMEM;
+       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 -EOPNOTSUPP;
-               }
-       }
+       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();
 
-       err = conn_send_cmd2(tconn, P_PROTOCOL, p->head.payload, size - sizeof(struct p_header));
-       kfree(p);
-       return err;
+       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 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, &mdev->tconn->data, 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)
@@ -919,7 +999,8 @@ void drbd_print_uuids(struct drbd_conf *mdev, const char *text)
 
 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);
@@ -928,14 +1009,19 @@ void 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);
 
-       drbd_send_cmd(mdev, &mdev->tconn->data, 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;
 
@@ -954,14 +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);
-
-       return drbd_send_cmd(mdev, &mdev->tconn->data, P_SIZES, &p.head, sizeof(p));
+       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);
 }
 
 /**
@@ -970,56 +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 err = -EIO;
+       struct drbd_socket *sock;
+       struct p_state *p;
 
-       mutex_lock(&mdev->tconn->data.mutex);
-
-       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))
-               err = _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 err;
 }
 
-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;
-
-       p.mask    = cpu_to_be32(mask.i);
-       p.val     = cpu_to_be32(val.i);
+       enum drbd_packet cmd;
+       struct drbd_socket *sock;
+       struct p_req_state *p;
 
-       return conn_send_cmd(tconn, vnr, &tconn->data, 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);
 }
 
 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;
 
-       drbd_send_cmd(mdev, &mdev->tconn->meta, 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);
+       }
+}
 
-       return !conn_send_cmd(tconn, 0, &tconn->meta, cmd, &p.head, sizeof(p));
+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);
+}
+
+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;
@@ -1027,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->tconn->net_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;
 
@@ -1061,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.
@@ -1106,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;
 }
@@ -1118,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;
@@ -1170,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");
@@ -1210,32 +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;
 }
+
 void drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
 {
-       struct p_barrier_ack p;
+       struct drbd_socket *sock;
+       struct p_barrier_ack *p;
 
-       p.barrier  = barrier_nr;
-       p.set_size = cpu_to_be32(set_size);
+       if (mdev->state.conn < C_CONNECTED)
+               return;
 
-       if (mdev->state.conn >= C_CONNECTED)
-               drbd_send_cmd(mdev, &mdev->tconn->meta, P_BARRIER_ACK, &p.head, sizeof(p));
+       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);
 }
 
 /**
@@ -1249,16 +1382,21 @@ void 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)
 {
-       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)
+       sock = &mdev->tconn->meta;
+       p = drbd_prepare_command(mdev, sock);
+       if (!p)
                return -EIO;
-       return drbd_send_cmd(mdev, &mdev->tconn->meta, cmd, &p.head, sizeof(p));
+       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,
@@ -1308,47 +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, &mdev->tconn->data, 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);
+       struct drbd_socket *sock;
+       struct p_block_req *p;
 
-       mutex_lock(&mdev->tconn->data.mutex);
+       /* FIXME: Put the digest into the preallocated socket buffer.  */
 
-       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));
-
-       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)
 {
-       struct p_block_req p;
+       struct drbd_socket *sock;
+       struct p_block_req *p;
 
-       p.sector   = cpu_to_be64(sector);
-       p.block_id = ID_SYNCER /* unused */;
-       p.blksize  = cpu_to_be32(size);
-
-       return drbd_send_cmd(mdev, &mdev->tconn->data, P_OV_REQUEST, &p.head, sizeof(p));
+       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
@@ -1407,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.
@@ -1436,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;
@@ -1457,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)
@@ -1469,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)
@@ -1483,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,
@@ -1496,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)
@@ -1524,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
@@ -1568,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) {
@@ -1579,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);
@@ -1588,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:
@@ -1601,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, &mdev->tconn->data, 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);
 }
 
 /*
@@ -1685,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 {
@@ -1789,15 +1940,12 @@ static void drbd_set_defaults(struct drbd_conf *mdev)
 {
        /* Beware! The actual layout differs
         * between big endian and little endian */
-       mdev->state = (union drbd_state) {
+       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
                } };
 }
 
@@ -1813,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);
@@ -1915,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));
@@ -2069,41 +2214,38 @@ 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. */
-void drbd_delete_device(unsigned int minor)
+void drbd_delete_device(struct drbd_conf *mdev)
 {
-       struct drbd_conf *mdev = minor_to_mdev(minor);
-
-       if (!mdev)
-               return;
+       struct drbd_tconn *tconn = mdev->tconn;
 
        idr_remove(&mdev->tconn->volumes, mdev->vnr);
-       idr_remove(&minors, minor);
+       idr_remove(&minors, mdev_to_minor(mdev));
        synchronize_rcu();
 
        /* paranoia asserts */
@@ -2119,9 +2261,10 @@ void drbd_delete_device(unsigned int minor)
        if (mdev->this_bdev)
                bdput(mdev->this_bdev);
 
-       drbd_free_resources(mdev);
+       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);
@@ -2129,10 +2272,15 @@ 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)
@@ -2155,8 +2303,11 @@ static void drbd_cleanup(void)
 
        drbd_genl_unregister();
 
+       down_write(&drbd_cfg_rwsem);
        idr_for_each_entry(&minors, mdev, i)
-               drbd_delete_device(i);
+               drbd_delete_device(mdev);
+       up_write(&drbd_cfg_rwsem);
+
        drbd_destroy_mempools();
        unregister_blkdev(DRBD_MAJOR, "drbd");
 
@@ -2218,18 +2369,56 @@ struct drbd_tconn *conn_by_name(const char *name)
        if (!name || !name[0])
                return NULL;
 
-       mutex_lock(&drbd_cfg_mutex);
+       down_read(&drbd_cfg_rwsem);
        list_for_each_entry(tconn, &drbd_tconns, all_tconn) {
                if (!strcmp(tconn->name, name))
                        goto found;
        }
        tconn = NULL;
 found:
-       mutex_unlock(&drbd_cfg_mutex);
+       up_read(&drbd_cfg_rwsem);
        return tconn;
 }
 
-struct drbd_tconn *drbd_new_tconn(const char *name)
+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;
 
@@ -2241,6 +2430,11 @@ struct drbd_tconn *drbd_new_tconn(const 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;
 
@@ -2250,8 +2444,7 @@ struct drbd_tconn *drbd_new_tconn(const char *name)
        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);
 
@@ -2270,29 +2463,34 @@ struct drbd_tconn *drbd_new_tconn(const char *name)
                DRBD_ON_NO_DATA_DEF, /* on_no_data */
        };
 
-       mutex_lock(&drbd_cfg_mutex);
+       down_write(&drbd_cfg_rwsem);
+       kref_init(&tconn->kref);
        list_add_tail(&tconn->all_tconn, &drbd_tconns);
-       mutex_unlock(&drbd_cfg_mutex);
+       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)
 {
-       list_del(&tconn->all_tconn);
+       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);
@@ -2316,7 +2514,9 @@ enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor,
        if (!mdev)
                return ERR_NOMEM;
 
+       kref_get(&tconn->kref);
        mdev->tconn = tconn;
+
        mdev->minor = minor;
        mdev->vnr = vnr;
 
@@ -2418,30 +2618,14 @@ out_no_disk:
        blk_cleanup_queue(q);
 out_no_q:
        kfree(mdev);
+       kref_put(&tconn->kref, &conn_destroy);
        return err;
 }
 
-/* counterpart of drbd_new_device.
- * last part of drbd_delete_device. */
-void drbd_free_mdev(struct drbd_conf *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);
-}
-
-
 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);
@@ -2540,27 +2724,6 @@ void drbd_free_sock(struct drbd_tconn *tconn)
        }
 }
 
-
-void drbd_free_resources(struct drbd_conf *mdev)
-{
-       crypto_free_hash(mdev->tconn->csums_tfm);
-       mdev->tconn->csums_tfm = NULL;
-       crypto_free_hash(mdev->tconn->verify_tfm);
-       mdev->tconn->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 {
@@ -2623,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);
@@ -2656,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");
@@ -2895,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)
@@ -2921,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)
@@ -3037,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)
@@ -3086,14 +3249,23 @@ const char *cmdname(enum drbd_packet cmd)
                [P_DELAY_PROBE]         = "DelayProbe",
                [P_OUT_OF_SYNC]         = "OutOfSync",
                [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";
+       /* 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];
@@ -3107,15 +3279,18 @@ const char *cmdname(enum drbd_packet cmd)
  */
 int drbd_wait_misc(struct drbd_conf *mdev, struct drbd_interval *i)
 {
-       struct net_conf *net_conf = mdev->tconn->net_conf;
+       struct net_conf *nc;
        DEFINE_WAIT(wait);
        long timeout;
 
-       if (!net_conf)
+       rcu_read_lock();
+       nc = rcu_dereference(mdev->tconn->net_conf);
+       if (!nc) {
+               rcu_read_unlock();
                return -ETIMEDOUT;
-       timeout = MAX_SCHEDULE_TIMEOUT;
-       if (net_conf->ko_count)
-               timeout = net_conf->timeout * HZ / 10 * net_conf->ko_count;
+       }
+       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;