]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/block/drbd/drbd_worker.c
drbd: Introduced drbd_read_state()
[karo-tx-linux.git] / drivers / block / drbd / drbd_worker.c
index ec26df37884590cf4ba131ab30ee28017d3da696..7350466ff30c29100c7beb63bbdba26da208dd81 100644 (file)
 #include "drbd_int.h"
 #include "drbd_req.h"
 
-static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel);
-static int w_make_resync_request(struct drbd_conf *mdev,
-                                struct drbd_work *w, int cancel);
-
+static int w_make_ov_request(struct drbd_work *w, int cancel);
 
 
 /* endio handlers:
  *   drbd_md_io_complete (defined here)
- *   drbd_endio_pri (defined here)
- *   drbd_endio_sec (defined here)
+ *   drbd_request_endio (defined here)
+ *   drbd_peer_request_endio (defined here)
  *   bm_async_io_complete (defined in drbd_bitmap.c)
  *
  * For all these callbacks, note the following:
@@ -80,50 +77,50 @@ void drbd_md_io_complete(struct bio *bio, int error)
 /* reads on behalf of the partner,
  * "submitted" by the receiver
  */
-void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local)
+void drbd_endio_read_sec_final(struct drbd_peer_request *peer_req) __releases(local)
 {
        unsigned long flags = 0;
-       struct drbd_conf *mdev = e->mdev;
+       struct drbd_conf *mdev = peer_req->w.mdev;
 
-       spin_lock_irqsave(&mdev->req_lock, flags);
-       mdev->read_cnt += e->i.size >> 9;
-       list_del(&e->w.list);
+       spin_lock_irqsave(&mdev->tconn->req_lock, flags);
+       mdev->read_cnt += peer_req->i.size >> 9;
+       list_del(&peer_req->w.list);
        if (list_empty(&mdev->read_ee))
                wake_up(&mdev->ee_wait);
-       if (test_bit(__EE_WAS_ERROR, &e->flags))
+       if (test_bit(__EE_WAS_ERROR, &peer_req->flags))
                __drbd_chk_io_error(mdev, false);
-       spin_unlock_irqrestore(&mdev->req_lock, flags);
+       spin_unlock_irqrestore(&mdev->tconn->req_lock, flags);
 
-       drbd_queue_work(&mdev->tconn->data.work, &e->w);
+       drbd_queue_work(&mdev->tconn->data.work, &peer_req->w);
        put_ldev(mdev);
 }
 
 /* writes on behalf of the partner, or resync writes,
  * "submitted" by the receiver, final stage.  */
-static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(local)
+static void drbd_endio_write_sec_final(struct drbd_peer_request *peer_req) __releases(local)
 {
        unsigned long flags = 0;
-       struct drbd_conf *mdev = e->mdev;
-       sector_t e_sector;
+       struct drbd_conf *mdev = peer_req->w.mdev;
+       struct drbd_interval i;
        int do_wake;
        u64 block_id;
        int do_al_complete_io;
 
-       /* after we moved e to done_ee,
+       /* after we moved peer_req to done_ee,
         * we may no longer access it,
         * it may be freed/reused already!
         * (as soon as we release the req_lock) */
-       e_sector = e->i.sector;
-       do_al_complete_io = e->flags & EE_CALL_AL_COMPLETE_IO;
-       block_id = e->block_id;
+       i = peer_req->i;
+       do_al_complete_io = peer_req->flags & EE_CALL_AL_COMPLETE_IO;
+       block_id = peer_req->block_id;
 
-       spin_lock_irqsave(&mdev->req_lock, flags);
-       mdev->writ_cnt += e->i.size >> 9;
-       list_del(&e->w.list); /* has been on active_ee or sync_ee */
-       list_add_tail(&e->w.list, &mdev->done_ee);
+       spin_lock_irqsave(&mdev->tconn->req_lock, flags);
+       mdev->writ_cnt += peer_req->i.size >> 9;
+       list_del(&peer_req->w.list); /* has been on active_ee or sync_ee */
+       list_add_tail(&peer_req->w.list, &mdev->done_ee);
 
        /*
-        * Do not remove from the epoch_entries tree here: we did not send the
+        * Do not remove from the write_requests tree here: we did not send the
         * Ack yet and did not wake possibly waiting conflicting requests.
         * Removed from the tree from "drbd_process_done_ee" within the
         * appropriate w.cb (e_end_block/e_end_resync_block) or from
@@ -132,42 +129,42 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo
 
        do_wake = list_empty(block_id == ID_SYNCER ? &mdev->sync_ee : &mdev->active_ee);
 
-       if (test_bit(__EE_WAS_ERROR, &e->flags))
+       if (test_bit(__EE_WAS_ERROR, &peer_req->flags))
                __drbd_chk_io_error(mdev, false);
-       spin_unlock_irqrestore(&mdev->req_lock, flags);
+       spin_unlock_irqrestore(&mdev->tconn->req_lock, flags);
 
        if (block_id == ID_SYNCER)
-               drbd_rs_complete_io(mdev, e_sector);
+               drbd_rs_complete_io(mdev, i.sector);
 
        if (do_wake)
                wake_up(&mdev->ee_wait);
 
        if (do_al_complete_io)
-               drbd_al_complete_io(mdev, e_sector);
+               drbd_al_complete_io(mdev, &i);
 
-       wake_asender(mdev);
+       wake_asender(mdev->tconn);
        put_ldev(mdev);
 }
 
 /* writes on behalf of the partner, or resync writes,
  * "submitted" by the receiver.
  */
-void drbd_endio_sec(struct bio *bio, int error)
+void drbd_peer_request_endio(struct bio *bio, int error)
 {
-       struct drbd_epoch_entry *e = bio->bi_private;
-       struct drbd_conf *mdev = e->mdev;
+       struct drbd_peer_request *peer_req = bio->bi_private;
+       struct drbd_conf *mdev = peer_req->w.mdev;
        int uptodate = bio_flagged(bio, BIO_UPTODATE);
        int is_write = bio_data_dir(bio) == WRITE;
 
        if (error && __ratelimit(&drbd_ratelimit_state))
                dev_warn(DEV, "%s: error=%d s=%llus\n",
                                is_write ? "write" : "read", error,
-                               (unsigned long long)e->i.sector);
+                               (unsigned long long)peer_req->i.sector);
        if (!error && !uptodate) {
                if (__ratelimit(&drbd_ratelimit_state))
                        dev_warn(DEV, "%s: setting error to -EIO s=%llus\n",
                                        is_write ? "write" : "read",
-                                       (unsigned long long)e->i.sector);
+                                       (unsigned long long)peer_req->i.sector);
                /* strange behavior of some lower level drivers...
                 * fail the request by clearing the uptodate flag,
                 * but do not return any error?! */
@@ -175,24 +172,24 @@ void drbd_endio_sec(struct bio *bio, int error)
        }
 
        if (error)
-               set_bit(__EE_WAS_ERROR, &e->flags);
+               set_bit(__EE_WAS_ERROR, &peer_req->flags);
 
        bio_put(bio); /* no need for the bio anymore */
-       if (atomic_dec_and_test(&e->pending_bios)) {
+       if (atomic_dec_and_test(&peer_req->pending_bios)) {
                if (is_write)
-                       drbd_endio_write_sec_final(e);
+                       drbd_endio_write_sec_final(peer_req);
                else
-                       drbd_endio_read_sec_final(e);
+                       drbd_endio_read_sec_final(peer_req);
        }
 }
 
 /* read, readA or write requests on R_PRIMARY coming from drbd_make_request
  */
-void drbd_endio_pri(struct bio *bio, int error)
+void drbd_request_endio(struct bio *bio, int error)
 {
        unsigned long flags;
        struct drbd_request *req = bio->bi_private;
-       struct drbd_conf *mdev = req->mdev;
+       struct drbd_conf *mdev = req->w.mdev;
        struct bio_and_error m;
        enum drbd_req_event what;
        int uptodate = bio_flagged(bio, BIO_UPTODATE);
@@ -220,38 +217,40 @@ void drbd_endio_pri(struct bio *bio, int error)
        req->private_bio = ERR_PTR(error);
 
        /* not req_mod(), we need irqsave here! */
-       spin_lock_irqsave(&mdev->req_lock, flags);
+       spin_lock_irqsave(&mdev->tconn->req_lock, flags);
        __req_mod(req, what, &m);
-       spin_unlock_irqrestore(&mdev->req_lock, flags);
+       spin_unlock_irqrestore(&mdev->tconn->req_lock, flags);
 
        if (m.bio)
                complete_master_bio(mdev, &m);
 }
 
-int w_read_retry_remote(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_read_retry_remote(struct drbd_work *w, int cancel)
 {
        struct drbd_request *req = container_of(w, struct drbd_request, w);
+       struct drbd_conf *mdev = w->mdev;
 
        /* We should not detach for read io-error,
         * but try to WRITE the P_DATA_REPLY to the failed location,
         * to give the disk the chance to relocate that block */
 
-       spin_lock_irq(&mdev->req_lock);
+       spin_lock_irq(&mdev->tconn->req_lock);
        if (cancel || mdev->state.pdsk != D_UP_TO_DATE) {
                _req_mod(req, READ_RETRY_REMOTE_CANCELED);
-               spin_unlock_irq(&mdev->req_lock);
-               return 1;
+               spin_unlock_irq(&mdev->tconn->req_lock);
+               return 0;
        }
-       spin_unlock_irq(&mdev->req_lock);
+       spin_unlock_irq(&mdev->tconn->req_lock);
 
-       return w_send_read_req(mdev, w, 0);
+       return w_send_read_req(w, 0);
 }
 
-void drbd_csum_ee(struct drbd_conf *mdev, struct crypto_hash *tfm, struct drbd_epoch_entry *e, void *digest)
+void drbd_csum_ee(struct drbd_conf *mdev, struct crypto_hash *tfm,
+                 struct drbd_peer_request *peer_req, void *digest)
 {
        struct hash_desc desc;
        struct scatterlist sg;
-       struct page *page = e->pages;
+       struct page *page = peer_req->pages;
        struct page *tmp;
        unsigned len;
 
@@ -268,7 +267,7 @@ void drbd_csum_ee(struct drbd_conf *mdev, struct crypto_hash *tfm, struct drbd_e
                page = tmp;
        }
        /* and now the last, possibly only partially used page */
-       len = e->i.size & (PAGE_SIZE - 1);
+       len = peer_req->i.size & (PAGE_SIZE - 1);
        sg_set_page(&sg, page, len ?: PAGE_SIZE, 0);
        crypto_hash_update(&desc, &sg, sg.length);
        crypto_hash_final(&desc, digest);
@@ -294,57 +293,58 @@ void drbd_csum_bio(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *
        crypto_hash_final(&desc, digest);
 }
 
-/* TODO merge common code with w_e_end_ov_req */
-int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+/* MAYBE merge common code with w_e_end_ov_req */
+static int w_e_send_csum(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
        int digest_size;
        void *digest;
-       int ok = 1;
+       int err = 0;
 
        if (unlikely(cancel))
                goto out;
 
-       if (likely((e->flags & EE_WAS_ERROR) != 0))
+       if (unlikely((peer_req->flags & EE_WAS_ERROR) != 0))
                goto out;
 
-       digest_size = crypto_hash_digestsize(mdev->csums_tfm);
+       digest_size = crypto_hash_digestsize(mdev->tconn->csums_tfm);
        digest = kmalloc(digest_size, GFP_NOIO);
        if (digest) {
-               sector_t sector = e->i.sector;
-               unsigned int size = e->i.size;
-               drbd_csum_ee(mdev, mdev->csums_tfm, e, digest);
-               /* Free e and pages before send.
+               sector_t sector = peer_req->i.sector;
+               unsigned int size = peer_req->i.size;
+               drbd_csum_ee(mdev, mdev->tconn->csums_tfm, peer_req, digest);
+               /* Free peer_req and pages before send.
                 * In case we block on congestion, we could otherwise run into
                 * some distributed deadlock, if the other side blocks on
                 * congestion as well, because our receiver blocks in
                 * drbd_pp_alloc due to pp_in_use > max_buffers. */
-               drbd_free_ee(mdev, e);
-               e = NULL;
+               drbd_free_ee(mdev, peer_req);
+               peer_req = NULL;
                inc_rs_pending(mdev);
-               ok = drbd_send_drequest_csum(mdev, sector, size,
-                                            digest, digest_size,
-                                            P_CSUM_RS_REQUEST);
+               err = drbd_send_drequest_csum(mdev, sector, size,
+                                             digest, digest_size,
+                                             P_CSUM_RS_REQUEST);
                kfree(digest);
        } else {
                dev_err(DEV, "kmalloc() of digest failed.\n");
-               ok = 0;
+               err = -ENOMEM;
        }
 
 out:
-       if (e)
-               drbd_free_ee(mdev, e);
+       if (peer_req)
+               drbd_free_ee(mdev, peer_req);
 
-       if (unlikely(!ok))
+       if (unlikely(err))
                dev_err(DEV, "drbd_send_drequest(..., csum) failed\n");
-       return ok;
+       return err;
 }
 
 #define GFP_TRY        (__GFP_HIGHMEM | __GFP_NOWARN)
 
 static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
 {
-       struct drbd_epoch_entry *e;
+       struct drbd_peer_request *peer_req;
 
        if (!get_ldev(mdev))
                return -EIO;
@@ -354,45 +354,46 @@ static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
 
        /* GFP_TRY, because if there is no memory available right now, this may
         * be rescheduled for later. It is "only" background resync, after all. */
-       e = drbd_alloc_ee(mdev, ID_SYNCER /* unused */, sector, size, GFP_TRY);
-       if (!e)
+       peer_req = drbd_alloc_ee(mdev, ID_SYNCER /* unused */, sector, size, GFP_TRY);
+       if (!peer_req)
                goto defer;
 
-       e->w.cb = w_e_send_csum;
-       spin_lock_irq(&mdev->req_lock);
-       list_add(&e->w.list, &mdev->read_ee);
-       spin_unlock_irq(&mdev->req_lock);
+       peer_req->w.cb = w_e_send_csum;
+       spin_lock_irq(&mdev->tconn->req_lock);
+       list_add(&peer_req->w.list, &mdev->read_ee);
+       spin_unlock_irq(&mdev->tconn->req_lock);
 
        atomic_add(size >> 9, &mdev->rs_sect_ev);
-       if (drbd_submit_ee(mdev, e, READ, DRBD_FAULT_RS_RD) == 0)
+       if (drbd_submit_peer_request(mdev, peer_req, READ, DRBD_FAULT_RS_RD) == 0)
                return 0;
 
        /* If it failed because of ENOMEM, retry should help.  If it failed
         * because bio_add_page failed (probably broken lower level driver),
         * retry may or may not help.
         * If it does not, you may need to force disconnect. */
-       spin_lock_irq(&mdev->req_lock);
-       list_del(&e->w.list);
-       spin_unlock_irq(&mdev->req_lock);
+       spin_lock_irq(&mdev->tconn->req_lock);
+       list_del(&peer_req->w.list);
+       spin_unlock_irq(&mdev->tconn->req_lock);
 
-       drbd_free_ee(mdev, e);
+       drbd_free_ee(mdev, peer_req);
 defer:
        put_ldev(mdev);
        return -EAGAIN;
 }
 
-int w_resync_timer(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_resync_timer(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        switch (mdev->state.conn) {
        case C_VERIFY_S:
-               w_make_ov_request(mdev, w, cancel);
+               w_make_ov_request(w, cancel);
                break;
        case C_SYNC_TARGET:
-               w_make_resync_request(mdev, w, cancel);
+               w_make_resync_request(w, cancel);
                break;
        }
 
-       return 1;
+       return 0;
 }
 
 void resync_timer_fn(unsigned long data)
@@ -448,13 +449,13 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
 
        spin_lock(&mdev->peer_seq_lock); /* get an atomic view on mdev->rs_plan_s */
 
-       steps = mdev->rs_plan_s.size; /* (mdev->sync_conf.c_plan_ahead * 10 * SLEEP_TIME) / HZ; */
+       steps = mdev->rs_plan_s.size; /* (mdev->ldev->dc.c_plan_ahead * 10 * SLEEP_TIME) / HZ; */
 
        if (mdev->rs_in_flight + sect_in == 0) { /* At start of resync */
-               want = ((mdev->sync_conf.rate * 2 * SLEEP_TIME) / HZ) * steps;
+               want = ((mdev->ldev->dc.resync_rate * 2 * SLEEP_TIME) / HZ) * steps;
        } else { /* normal path */
-               want = mdev->sync_conf.c_fill_target ? mdev->sync_conf.c_fill_target :
-                       sect_in * mdev->sync_conf.c_delay_target * HZ / (SLEEP_TIME * 10);
+               want = mdev->ldev->dc.c_fill_target ? mdev->ldev->dc.c_fill_target :
+                       sect_in * mdev->ldev->dc.c_delay_target * HZ / (SLEEP_TIME * 10);
        }
 
        correction = want - mdev->rs_in_flight - mdev->rs_planed;
@@ -473,7 +474,7 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
        if (req_sect < 0)
                req_sect = 0;
 
-       max_sect = (mdev->sync_conf.c_max_rate * 2 * SLEEP_TIME) / HZ;
+       max_sect = (mdev->ldev->dc.c_max_rate * 2 * SLEEP_TIME) / HZ;
        if (req_sect > max_sect)
                req_sect = max_sect;
 
@@ -489,11 +490,11 @@ static int drbd_rs_controller(struct drbd_conf *mdev)
 static int drbd_rs_number_requests(struct drbd_conf *mdev)
 {
        int number;
-       if (mdev->rs_plan_s.size) { /* mdev->sync_conf.c_plan_ahead */
+       if (mdev->rs_plan_s.size) { /* mdev->ldev->dc.c_plan_ahead */
                number = drbd_rs_controller(mdev) >> (BM_BLOCK_SHIFT - 9);
                mdev->c_sync_rate = number * HZ * (BM_BLOCK_SIZE / 1024) / SLEEP_TIME;
        } else {
-               mdev->c_sync_rate = mdev->sync_conf.rate;
+               mdev->c_sync_rate = mdev->ldev->dc.resync_rate;
                number = SLEEP_TIME * mdev->c_sync_rate  / ((BM_BLOCK_SIZE / 1024) * HZ);
        }
 
@@ -502,9 +503,9 @@ static int drbd_rs_number_requests(struct drbd_conf *mdev)
        return number;
 }
 
-static int w_make_resync_request(struct drbd_conf *mdev,
-                                struct drbd_work *w, int cancel)
+int w_make_resync_request(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        unsigned long bit;
        sector_t sector;
        const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
@@ -514,12 +515,12 @@ static int w_make_resync_request(struct drbd_conf *mdev,
        int i = 0;
 
        if (unlikely(cancel))
-               return 1;
+               return 0;
 
        if (mdev->rs_total == 0) {
                /* empty resync? */
                drbd_resync_finished(mdev);
-               return 1;
+               return 0;
        }
 
        if (!get_ldev(mdev)) {
@@ -528,7 +529,7 @@ static int w_make_resync_request(struct drbd_conf *mdev,
                   to continue resync with a broken disk makes no sense at
                   all */
                dev_err(DEV, "Disk broke down during resync!\n");
-               return 1;
+               return 0;
        }
 
        max_bio_size = queue_max_hw_sectors(mdev->rq_queue) << 9;
@@ -557,7 +558,7 @@ next_sector:
                if (bit == DRBD_END_OF_BITMAP) {
                        mdev->bm_resync_fo = drbd_bm_bits(mdev);
                        put_ldev(mdev);
-                       return 1;
+                       return 0;
                }
 
                sector = BM_BIT_TO_SECT(bit);
@@ -616,11 +617,11 @@ next_sector:
                /* adjust very last sectors, in case we are oddly sized */
                if (sector + (size>>9) > capacity)
                        size = (capacity-sector)<<9;
-               if (mdev->tconn->agreed_pro_version >= 89 && mdev->csums_tfm) {
+               if (mdev->tconn->agreed_pro_version >= 89 && mdev->tconn->csums_tfm) {
                        switch (read_for_csum(mdev, sector, size)) {
                        case -EIO: /* Disk failure */
                                put_ldev(mdev);
-                               return 0;
+                               return -EIO;
                        case -EAGAIN: /* allocation failed, or ldev busy */
                                drbd_rs_complete_io(mdev, sector);
                                mdev->bm_resync_fo = BM_SECT_TO_BIT(sector);
@@ -633,13 +634,16 @@ next_sector:
                                BUG();
                        }
                } else {
+                       int err;
+
                        inc_rs_pending(mdev);
-                       if (!drbd_send_drequest(mdev, P_RS_DATA_REQUEST,
-                                              sector, size, ID_SYNCER)) {
+                       err = drbd_send_drequest(mdev, P_RS_DATA_REQUEST,
+                                                sector, size, ID_SYNCER);
+                       if (err) {
                                dev_err(DEV, "drbd_send_drequest() failed, aborting...\n");
                                dec_rs_pending(mdev);
                                put_ldev(mdev);
-                               return 0;
+                               return err;
                        }
                }
        }
@@ -652,18 +656,19 @@ next_sector:
                 * until then resync "work" is "inactive" ...
                 */
                put_ldev(mdev);
-               return 1;
+               return 0;
        }
 
  requeue:
        mdev->rs_in_flight += (i << (BM_BLOCK_SHIFT - 9));
        mod_timer(&mdev->resync_timer, jiffies + SLEEP_TIME);
        put_ldev(mdev);
-       return 1;
+       return 0;
 }
 
-static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+static int w_make_ov_request(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        int number, i, size;
        sector_t sector;
        const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
@@ -691,7 +696,7 @@ static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int ca
                        size = (capacity-sector)<<9;
 
                inc_rs_pending(mdev);
-               if (!drbd_send_ov_request(mdev, sector, size)) {
+               if (drbd_send_ov_request(mdev, sector, size)) {
                        dec_rs_pending(mdev);
                        return 0;
                }
@@ -705,52 +710,34 @@ static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int ca
        return 1;
 }
 
-
-void start_resync_timer_fn(unsigned long data)
-{
-       struct drbd_conf *mdev = (struct drbd_conf *) data;
-
-       drbd_queue_work(&mdev->tconn->data.work, &mdev->start_resync_work);
-}
-
-int w_start_resync(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
-{
-       if (atomic_read(&mdev->unacked_cnt) || atomic_read(&mdev->rs_pending_cnt)) {
-               dev_warn(DEV, "w_start_resync later...\n");
-               mdev->start_resync_timer.expires = jiffies + HZ/10;
-               add_timer(&mdev->start_resync_timer);
-               return 1;
-       }
-
-       drbd_start_resync(mdev, C_SYNC_SOURCE);
-       clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags);
-       return 1;
-}
-
-int w_ov_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_ov_finished(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        kfree(w);
-       ov_oos_print(mdev);
+       ov_out_of_sync_print(mdev);
        drbd_resync_finished(mdev);
 
-       return 1;
+       return 0;
 }
 
-static int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+static int w_resync_finished(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        kfree(w);
 
        drbd_resync_finished(mdev);
 
-       return 1;
+       return 0;
 }
 
 static void ping_peer(struct drbd_conf *mdev)
 {
-       clear_bit(GOT_PING_ACK, &mdev->flags);
-       request_ping(mdev);
-       wait_event(mdev->misc_wait,
-                  test_bit(GOT_PING_ACK, &mdev->flags) || mdev->state.conn < C_CONNECTED);
+       struct drbd_tconn *tconn = mdev->tconn;
+
+       clear_bit(GOT_PING_ACK, &tconn->flags);
+       request_ping(tconn);
+       wait_event(tconn->ping_wait,
+                  test_bit(GOT_PING_ACK, &tconn->flags) || mdev->state.conn < C_CONNECTED);
 }
 
 int drbd_resync_finished(struct drbd_conf *mdev)
@@ -793,8 +780,8 @@ int drbd_resync_finished(struct drbd_conf *mdev)
 
        ping_peer(mdev);
 
-       spin_lock_irq(&mdev->req_lock);
-       os = mdev->state;
+       spin_lock_irq(&mdev->tconn->req_lock);
+       os = drbd_read_state(mdev);
 
        verify_done = (os.conn == C_VERIFY_S || os.conn == C_VERIFY_T);
 
@@ -824,7 +811,7 @@ int drbd_resync_finished(struct drbd_conf *mdev)
                if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T)
                        khelper_cmd = "after-resync-target";
 
-               if (mdev->csums_tfm && mdev->rs_total) {
+               if (mdev->tconn->csums_tfm && mdev->rs_total) {
                        const unsigned long s = mdev->rs_same_csum;
                        const unsigned long t = mdev->rs_total;
                        const int ratio =
@@ -882,7 +869,7 @@ int drbd_resync_finished(struct drbd_conf *mdev)
 
        _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
 out_unlock:
-       spin_unlock_irq(&mdev->req_lock);
+       spin_unlock_irq(&mdev->tconn->req_lock);
        put_ldev(mdev);
 out:
        mdev->rs_total  = 0;
@@ -900,19 +887,19 @@ out:
 }
 
 /* helper */
-static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_epoch_entry *e)
+static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_peer_request *peer_req)
 {
-       if (drbd_ee_has_active_page(e)) {
+       if (drbd_ee_has_active_page(peer_req)) {
                /* This might happen if sendpage() has not finished */
-               int i = (e->i.size + PAGE_SIZE -1) >> PAGE_SHIFT;
+               int i = (peer_req->i.size + PAGE_SIZE -1) >> PAGE_SHIFT;
                atomic_add(i, &mdev->pp_in_use_by_net);
                atomic_sub(i, &mdev->pp_in_use);
-               spin_lock_irq(&mdev->req_lock);
-               list_add_tail(&e->w.list, &mdev->net_ee);
-               spin_unlock_irq(&mdev->req_lock);
+               spin_lock_irq(&mdev->tconn->req_lock);
+               list_add_tail(&peer_req->w.list, &mdev->net_ee);
+               spin_unlock_irq(&mdev->tconn->req_lock);
                wake_up(&drbd_pp_wait);
        } else
-               drbd_free_ee(mdev, e);
+               drbd_free_ee(mdev, peer_req);
 }
 
 /**
@@ -921,34 +908,35 @@ static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_epoch_ent
  * @w:         work object.
  * @cancel:    The connection will be closed anyways
  */
-int w_e_end_data_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_e_end_data_req(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
-       int ok;
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
+       int err;
 
        if (unlikely(cancel)) {
-               drbd_free_ee(mdev, e);
+               drbd_free_ee(mdev, peer_req);
                dec_unacked(mdev);
-               return 1;
+               return 0;
        }
 
-       if (likely((e->flags & EE_WAS_ERROR) == 0)) {
-               ok = drbd_send_block(mdev, P_DATA_REPLY, e);
+       if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
+               err = drbd_send_block(mdev, P_DATA_REPLY, peer_req);
        } else {
                if (__ratelimit(&drbd_ratelimit_state))
                        dev_err(DEV, "Sending NegDReply. sector=%llus.\n",
-                           (unsigned long long)e->i.sector);
+                           (unsigned long long)peer_req->i.sector);
 
-               ok = drbd_send_ack(mdev, P_NEG_DREPLY, e);
+               err = drbd_send_ack(mdev, P_NEG_DREPLY, peer_req);
        }
 
        dec_unacked(mdev);
 
-       move_to_net_ee_or_free(mdev, e);
+       move_to_net_ee_or_free(mdev, peer_req);
 
-       if (unlikely(!ok))
+       if (unlikely(err))
                dev_err(DEV, "drbd_send_block() failed\n");
-       return ok;
+       return err;
 }
 
 /**
@@ -957,138 +945,140 @@ int w_e_end_data_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
  * @w:         work object.
  * @cancel:    The connection will be closed anyways
  */
-int w_e_end_rsdata_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_e_end_rsdata_req(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
-       int ok;
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
+       int err;
 
        if (unlikely(cancel)) {
-               drbd_free_ee(mdev, e);
+               drbd_free_ee(mdev, peer_req);
                dec_unacked(mdev);
-               return 1;
+               return 0;
        }
 
        if (get_ldev_if_state(mdev, D_FAILED)) {
-               drbd_rs_complete_io(mdev, e->i.sector);
+               drbd_rs_complete_io(mdev, peer_req->i.sector);
                put_ldev(mdev);
        }
 
        if (mdev->state.conn == C_AHEAD) {
-               ok = drbd_send_ack(mdev, P_RS_CANCEL, e);
-       } else if (likely((e->flags & EE_WAS_ERROR) == 0)) {
+               err = drbd_send_ack(mdev, P_RS_CANCEL, peer_req);
+       } else if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
                if (likely(mdev->state.pdsk >= D_INCONSISTENT)) {
                        inc_rs_pending(mdev);
-                       ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e);
+                       err = drbd_send_block(mdev, P_RS_DATA_REPLY, peer_req);
                } else {
                        if (__ratelimit(&drbd_ratelimit_state))
                                dev_err(DEV, "Not sending RSDataReply, "
                                    "partner DISKLESS!\n");
-                       ok = 1;
+                       err = 0;
                }
        } else {
                if (__ratelimit(&drbd_ratelimit_state))
                        dev_err(DEV, "Sending NegRSDReply. sector %llus.\n",
-                           (unsigned long long)e->i.sector);
+                           (unsigned long long)peer_req->i.sector);
 
-               ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e);
+               err = drbd_send_ack(mdev, P_NEG_RS_DREPLY, peer_req);
 
                /* update resync data with failure */
-               drbd_rs_failed_io(mdev, e->i.sector, e->i.size);
+               drbd_rs_failed_io(mdev, peer_req->i.sector, peer_req->i.size);
        }
 
        dec_unacked(mdev);
 
-       move_to_net_ee_or_free(mdev, e);
+       move_to_net_ee_or_free(mdev, peer_req);
 
-       if (unlikely(!ok))
+       if (unlikely(err))
                dev_err(DEV, "drbd_send_block() failed\n");
-       return ok;
+       return err;
 }
 
-int w_e_end_csum_rs_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_e_end_csum_rs_req(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
        struct digest_info *di;
        int digest_size;
        void *digest = NULL;
-       int ok, eq = 0;
+       int err, eq = 0;
 
        if (unlikely(cancel)) {
-               drbd_free_ee(mdev, e);
+               drbd_free_ee(mdev, peer_req);
                dec_unacked(mdev);
-               return 1;
+               return 0;
        }
 
        if (get_ldev(mdev)) {
-               drbd_rs_complete_io(mdev, e->i.sector);
+               drbd_rs_complete_io(mdev, peer_req->i.sector);
                put_ldev(mdev);
        }
 
-       di = e->digest;
+       di = peer_req->digest;
 
-       if (likely((e->flags & EE_WAS_ERROR) == 0)) {
+       if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
                /* quick hack to try to avoid a race against reconfiguration.
                 * a real fix would be much more involved,
                 * introducing more locking mechanisms */
-               if (mdev->csums_tfm) {
-                       digest_size = crypto_hash_digestsize(mdev->csums_tfm);
+               if (mdev->tconn->csums_tfm) {
+                       digest_size = crypto_hash_digestsize(mdev->tconn->csums_tfm);
                        D_ASSERT(digest_size == di->digest_size);
                        digest = kmalloc(digest_size, GFP_NOIO);
                }
                if (digest) {
-                       drbd_csum_ee(mdev, mdev->csums_tfm, e, digest);
+                       drbd_csum_ee(mdev, mdev->tconn->csums_tfm, peer_req, digest);
                        eq = !memcmp(digest, di->digest, digest_size);
                        kfree(digest);
                }
 
                if (eq) {
-                       drbd_set_in_sync(mdev, e->i.sector, e->i.size);
+                       drbd_set_in_sync(mdev, peer_req->i.sector, peer_req->i.size);
                        /* rs_same_csums unit is BM_BLOCK_SIZE */
-                       mdev->rs_same_csum += e->i.size >> BM_BLOCK_SHIFT;
-                       ok = drbd_send_ack(mdev, P_RS_IS_IN_SYNC, e);
+                       mdev->rs_same_csum += peer_req->i.size >> BM_BLOCK_SHIFT;
+                       err = drbd_send_ack(mdev, P_RS_IS_IN_SYNC, peer_req);
                } else {
                        inc_rs_pending(mdev);
-                       e->block_id = ID_SYNCER; /* By setting block_id, digest pointer becomes invalid! */
-                       e->flags &= ~EE_HAS_DIGEST; /* This e no longer has a digest pointer */
+                       peer_req->block_id = ID_SYNCER; /* By setting block_id, digest pointer becomes invalid! */
+                       peer_req->flags &= ~EE_HAS_DIGEST; /* This peer request no longer has a digest pointer */
                        kfree(di);
-                       ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e);
+                       err = drbd_send_block(mdev, P_RS_DATA_REPLY, peer_req);
                }
        } else {
-               ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e);
+               err = drbd_send_ack(mdev, P_NEG_RS_DREPLY, peer_req);
                if (__ratelimit(&drbd_ratelimit_state))
                        dev_err(DEV, "Sending NegDReply. I guess it gets messy.\n");
        }
 
        dec_unacked(mdev);
-       move_to_net_ee_or_free(mdev, e);
+       move_to_net_ee_or_free(mdev, peer_req);
 
-       if (unlikely(!ok))
+       if (unlikely(err))
                dev_err(DEV, "drbd_send_block/ack() failed\n");
-       return ok;
+       return err;
 }
 
-/* TODO merge common code with w_e_send_csum */
-int w_e_end_ov_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_e_end_ov_req(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
-       sector_t sector = e->i.sector;
-       unsigned int size = e->i.size;
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
+       sector_t sector = peer_req->i.sector;
+       unsigned int size = peer_req->i.size;
        int digest_size;
        void *digest;
-       int ok = 1;
+       int err = 0;
 
        if (unlikely(cancel))
                goto out;
 
-       digest_size = crypto_hash_digestsize(mdev->verify_tfm);
+       digest_size = crypto_hash_digestsize(mdev->tconn->verify_tfm);
        digest = kmalloc(digest_size, GFP_NOIO);
        if (!digest) {
-               ok = 0; /* terminate the connection in case the allocation failed */
+               err = 1;        /* terminate the connection in case the allocation failed */
                goto out;
        }
 
-       if (likely(!(e->flags & EE_WAS_ERROR)))
-               drbd_csum_ee(mdev, mdev->verify_tfm, e, digest);
+       if (likely(!(peer_req->flags & EE_WAS_ERROR)))
+               drbd_csum_ee(mdev, mdev->tconn->verify_tfm, peer_req, digest);
        else
                memset(digest, 0, digest_size);
 
@@ -1097,24 +1087,22 @@ int w_e_end_ov_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
         * some distributed deadlock, if the other side blocks on
         * congestion as well, because our receiver blocks in
         * drbd_pp_alloc due to pp_in_use > max_buffers. */
-       drbd_free_ee(mdev, e);
-       e = NULL;
+       drbd_free_ee(mdev, peer_req);
+       peer_req = NULL;
        inc_rs_pending(mdev);
-       ok = drbd_send_drequest_csum(mdev, sector, size,
-                                    digest, digest_size,
-                                    P_OV_REPLY);
-       if (!ok)
+       err = drbd_send_drequest_csum(mdev, sector, size, digest, digest_size, P_OV_REPLY);
+       if (err)
                dec_rs_pending(mdev);
        kfree(digest);
 
 out:
-       if (e)
-               drbd_free_ee(mdev, e);
+       if (peer_req)
+               drbd_free_ee(mdev, peer_req);
        dec_unacked(mdev);
-       return ok;
+       return err;
 }
 
-void drbd_ov_oos_found(struct drbd_conf *mdev, sector_t sector, int size)
+void drbd_ov_out_of_sync_found(struct drbd_conf *mdev, sector_t sector, int size)
 {
        if (mdev->ov_last_oos_start + mdev->ov_last_oos_size == sector) {
                mdev->ov_last_oos_size += size>>9;
@@ -1125,36 +1113,37 @@ void drbd_ov_oos_found(struct drbd_conf *mdev, sector_t sector, int size)
        drbd_set_out_of_sync(mdev, sector, size);
 }
 
-int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_e_end_ov_reply(struct drbd_work *w, int cancel)
 {
-       struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+       struct drbd_peer_request *peer_req = container_of(w, struct drbd_peer_request, w);
+       struct drbd_conf *mdev = w->mdev;
        struct digest_info *di;
        void *digest;
-       sector_t sector = e->i.sector;
-       unsigned int size = e->i.size;
+       sector_t sector = peer_req->i.sector;
+       unsigned int size = peer_req->i.size;
        int digest_size;
-       int ok, eq = 0;
+       int err, eq = 0;
 
        if (unlikely(cancel)) {
-               drbd_free_ee(mdev, e);
+               drbd_free_ee(mdev, peer_req);
                dec_unacked(mdev);
-               return 1;
+               return 0;
        }
 
        /* after "cancel", because after drbd_disconnect/drbd_rs_cancel_all
         * the resync lru has been cleaned up already */
        if (get_ldev(mdev)) {
-               drbd_rs_complete_io(mdev, e->i.sector);
+               drbd_rs_complete_io(mdev, peer_req->i.sector);
                put_ldev(mdev);
        }
 
-       di = e->digest;
+       di = peer_req->digest;
 
-       if (likely((e->flags & EE_WAS_ERROR) == 0)) {
-               digest_size = crypto_hash_digestsize(mdev->verify_tfm);
+       if (likely((peer_req->flags & EE_WAS_ERROR) == 0)) {
+               digest_size = crypto_hash_digestsize(mdev->tconn->verify_tfm);
                digest = kmalloc(digest_size, GFP_NOIO);
                if (digest) {
-                       drbd_csum_ee(mdev, mdev->verify_tfm, e, digest);
+                       drbd_csum_ee(mdev, mdev->tconn->verify_tfm, peer_req, digest);
 
                        D_ASSERT(digest_size == di->digest_size);
                        eq = !memcmp(digest, di->digest, digest_size);
@@ -1162,19 +1151,19 @@ int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
                }
        }
 
-               /* Free e and pages before send.
-                * In case we block on congestion, we could otherwise run into
-                * some distributed deadlock, if the other side blocks on
-                * congestion as well, because our receiver blocks in
-                * drbd_pp_alloc due to pp_in_use > max_buffers. */
-       drbd_free_ee(mdev, e);
+       /* Free peer_req and pages before send.
+        * In case we block on congestion, we could otherwise run into
+        * some distributed deadlock, if the other side blocks on
+        * congestion as well, because our receiver blocks in
+        * drbd_pp_alloc due to pp_in_use > max_buffers. */
+       drbd_free_ee(mdev, peer_req);
        if (!eq)
-               drbd_ov_oos_found(mdev, sector, size);
+               drbd_ov_out_of_sync_found(mdev, sector, size);
        else
-               ov_oos_print(mdev);
+               ov_out_of_sync_print(mdev);
 
-       ok = drbd_send_ack_ex(mdev, P_OV_RESULT, sector, size,
-                             eq ? ID_IN_SYNC : ID_OUT_OF_SYNC);
+       err = drbd_send_ack_ex(mdev, P_OV_RESULT, sector, size,
+                              eq ? ID_IN_SYNC : ID_OUT_OF_SYNC);
 
        dec_unacked(mdev);
 
@@ -1185,72 +1174,77 @@ int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
                drbd_advance_rs_marks(mdev, mdev->ov_left);
 
        if (mdev->ov_left == 0) {
-               ov_oos_print(mdev);
+               ov_out_of_sync_print(mdev);
                drbd_resync_finished(mdev);
        }
 
-       return ok;
+       return err;
 }
 
-int w_prev_work_done(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_prev_work_done(struct drbd_work *w, int cancel)
 {
        struct drbd_wq_barrier *b = container_of(w, struct drbd_wq_barrier, w);
+
        complete(&b->done);
-       return 1;
+       return 0;
 }
 
-int w_send_barrier(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_send_barrier(struct drbd_work *w, int cancel)
 {
        struct drbd_tl_epoch *b = container_of(w, struct drbd_tl_epoch, w);
-       struct p_barrier *p = &mdev->tconn->data.sbuf.barrier;
-       int ok = 1;
+       struct drbd_conf *mdev = w->mdev;
+       struct p_barrier *p = mdev->tconn->data.sbuf;
+       int err = 0;
 
        /* really avoid racing with tl_clear.  w.cb may have been referenced
         * just before it was reassigned and re-queued, so double check that.
         * actually, this race was harmless, since we only try to send the
         * barrier packet here, and otherwise do nothing with the object.
         * but compare with the head of w_clear_epoch */
-       spin_lock_irq(&mdev->req_lock);
+       spin_lock_irq(&mdev->tconn->req_lock);
        if (w->cb != w_send_barrier || mdev->state.conn < C_CONNECTED)
                cancel = 1;
-       spin_unlock_irq(&mdev->req_lock);
+       spin_unlock_irq(&mdev->tconn->req_lock);
        if (cancel)
-               return 1;
-
-       if (!drbd_get_data_sock(mdev))
                return 0;
+
+       err = drbd_get_data_sock(mdev->tconn);
+       if (err)
+               return err;
        p->barrier = b->br_number;
        /* inc_ap_pending was done where this was queued.
         * dec_ap_pending will be done in got_BarrierAck
         * or (on connection loss) in w_clear_epoch.  */
-       ok = _drbd_send_cmd(mdev, mdev->tconn->data.socket, P_BARRIER,
-                               (struct p_header80 *)p, sizeof(*p), 0);
-       drbd_put_data_sock(mdev);
+       err = _drbd_send_cmd(mdev, &mdev->tconn->data, P_BARRIER,
+                            &p->head, sizeof(*p), 0);
+       drbd_put_data_sock(mdev->tconn);
 
-       return ok;
+       return err;
 }
 
-int w_send_write_hint(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_send_write_hint(struct drbd_work *w, int cancel)
 {
+       struct drbd_conf *mdev = w->mdev;
        if (cancel)
-               return 1;
+               return 0;
        return drbd_send_short_cmd(mdev, P_UNPLUG_REMOTE);
 }
 
-int w_send_oos(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_send_out_of_sync(struct drbd_work *w, int cancel)
 {
        struct drbd_request *req = container_of(w, struct drbd_request, w);
-       int ok;
+       struct drbd_conf *mdev = w->mdev;
+       int err;
 
        if (unlikely(cancel)) {
                req_mod(req, SEND_CANCELED);
-               return 1;
+               return 0;
        }
 
-       ok = drbd_send_oos(mdev, req);
+       err = drbd_send_out_of_sync(mdev, req);
        req_mod(req, OOS_HANDED_TO_NETWORK);
 
-       return ok;
+       return err;
 }
 
 /**
@@ -1259,20 +1253,21 @@ int w_send_oos(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
  * @w:         work object.
  * @cancel:    The connection will be closed anyways
  */
-int w_send_dblock(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_send_dblock(struct drbd_work *w, int cancel)
 {
        struct drbd_request *req = container_of(w, struct drbd_request, w);
-       int ok;
+       struct drbd_conf *mdev = w->mdev;
+       int err;
 
        if (unlikely(cancel)) {
                req_mod(req, SEND_CANCELED);
-               return 1;
+               return 0;
        }
 
-       ok = drbd_send_dblock(mdev, req);
-       req_mod(req, ok ? HANDED_OVER_TO_NETWORK : SEND_FAILED);
+       err = drbd_send_dblock(mdev, req);
+       req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK);
 
-       return ok;
+       return err;
 }
 
 /**
@@ -1281,36 +1276,32 @@ int w_send_dblock(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
  * @w:         work object.
  * @cancel:    The connection will be closed anyways
  */
-int w_send_read_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_send_read_req(struct drbd_work *w, int cancel)
 {
        struct drbd_request *req = container_of(w, struct drbd_request, w);
-       int ok;
+       struct drbd_conf *mdev = w->mdev;
+       int err;
 
        if (unlikely(cancel)) {
                req_mod(req, SEND_CANCELED);
-               return 1;
+               return 0;
        }
 
-       ok = drbd_send_drequest(mdev, P_DATA_REQUEST, req->i.sector, req->i.size,
-                               (unsigned long)req);
+       err = drbd_send_drequest(mdev, P_DATA_REQUEST, req->i.sector, req->i.size,
+                                (unsigned long)req);
 
-       if (!ok) {
-               /* ?? we set C_TIMEOUT or C_BROKEN_PIPE in drbd_send();
-                * so this is probably redundant */
-               if (mdev->state.conn >= C_CONNECTED)
-                       drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
-       }
-       req_mod(req, ok ? HANDED_OVER_TO_NETWORK : SEND_FAILED);
+       req_mod(req, err ? SEND_FAILED : HANDED_OVER_TO_NETWORK);
 
-       return ok;
+       return err;
 }
 
-int w_restart_disk_io(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+int w_restart_disk_io(struct drbd_work *w, int cancel)
 {
        struct drbd_request *req = container_of(w, struct drbd_request, w);
+       struct drbd_conf *mdev = w->mdev;
 
        if (bio_data_dir(req->master_bio) == WRITE && req->rq_state & RQ_IN_ACT_LOG)
-               drbd_al_begin_io(mdev, req->i.sector);
+               drbd_al_begin_io(mdev, &req->i);
        /* Calling drbd_al_begin_io() out of the worker might deadlocks
           theoretically. Practically it can not deadlock, since this is
           only used when unfreezing IOs. All the extents of the requests
@@ -1320,7 +1311,7 @@ int w_restart_disk_io(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
        req->private_bio->bi_bdev = mdev->ldev->backing_bdev;
        generic_make_request(req->private_bio);
 
-       return 1;
+       return 0;
 }
 
 static int _drbd_may_sync_now(struct drbd_conf *mdev)
@@ -1328,9 +1319,11 @@ static int _drbd_may_sync_now(struct drbd_conf *mdev)
        struct drbd_conf *odev = mdev;
 
        while (1) {
-               if (odev->sync_conf.after == -1)
+               if (!odev->ldev)
                        return 1;
-               odev = minor_to_mdev(odev->sync_conf.after);
+               if (odev->ldev->dc.resync_after == -1)
+                       return 1;
+               odev = minor_to_mdev(odev->ldev->dc.resync_after);
                if (!expect(odev))
                        return 1;
                if ((odev->state.conn >= C_SYNC_SOURCE &&
@@ -1352,10 +1345,7 @@ static int _drbd_pause_after(struct drbd_conf *mdev)
        struct drbd_conf *odev;
        int i, rv = 0;
 
-       for (i = 0; i < minor_count; i++) {
-               odev = minor_to_mdev(i);
-               if (!odev)
-                       continue;
+       idr_for_each_entry(&minors, odev, i) {
                if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS)
                        continue;
                if (!_drbd_may_sync_now(odev))
@@ -1377,10 +1367,7 @@ static int _drbd_resume_next(struct drbd_conf *mdev)
        struct drbd_conf *odev;
        int i, rv = 0;
 
-       for (i = 0; i < minor_count; i++) {
-               odev = minor_to_mdev(i);
-               if (!odev)
-                       continue;
+       idr_for_each_entry(&minors, odev, i) {
                if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS)
                        continue;
                if (odev->state.aftr_isp) {
@@ -1423,11 +1410,11 @@ static int sync_after_error(struct drbd_conf *mdev, int o_minor)
                        return ERR_SYNC_AFTER_CYCLE;
 
                /* dependency chain ends here, no cycles. */
-               if (odev->sync_conf.after == -1)
+               if (odev->ldev->dc.resync_after == -1)
                        return NO_ERROR;
 
                /* follow the dependency chain */
-               odev = minor_to_mdev(odev->sync_conf.after);
+               odev = minor_to_mdev(odev->ldev->dc.resync_after);
        }
 }
 
@@ -1439,7 +1426,7 @@ int drbd_alter_sa(struct drbd_conf *mdev, int na)
        write_lock_irq(&global_state_lock);
        retcode = sync_after_error(mdev, na);
        if (retcode == NO_ERROR) {
-               mdev->sync_conf.after = na;
+               mdev->ldev->dc.resync_after = na;
                do {
                        changes  = _drbd_pause_after(mdev);
                        changes |= _drbd_resume_next(mdev);
@@ -1460,6 +1447,29 @@ void drbd_rs_controller_reset(struct drbd_conf *mdev)
        spin_unlock(&mdev->peer_seq_lock);
 }
 
+void start_resync_timer_fn(unsigned long data)
+{
+       struct drbd_conf *mdev = (struct drbd_conf *) data;
+
+       drbd_queue_work(&mdev->tconn->data.work, &mdev->start_resync_work);
+}
+
+int w_start_resync(struct drbd_work *w, int cancel)
+{
+       struct drbd_conf *mdev = w->mdev;
+
+       if (atomic_read(&mdev->unacked_cnt) || atomic_read(&mdev->rs_pending_cnt)) {
+               dev_warn(DEV, "w_start_resync later...\n");
+               mdev->start_resync_timer.expires = jiffies + HZ/10;
+               add_timer(&mdev->start_resync_timer);
+               return 0;
+       }
+
+       drbd_start_resync(mdev, C_SYNC_SOURCE);
+       clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags);
+       return 0;
+}
+
 /**
  * drbd_start_resync() - Start the resync process
  * @mdev:      DRBD device.
@@ -1486,43 +1496,57 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
                   Ahead/Behind and SyncSource/SyncTarget */
        }
 
-       if (side == C_SYNC_TARGET) {
-               /* Since application IO was locked out during C_WF_BITMAP_T and
-                  C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
-                  we check that we might make the data inconsistent. */
-               r = drbd_khelper(mdev, "before-resync-target");
-               r = (r >> 8) & 0xff;
-               if (r > 0) {
-                       dev_info(DEV, "before-resync-target handler returned %d, "
-                            "dropping connection.\n", r);
-                       drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
-                       return;
-               }
-       } else /* C_SYNC_SOURCE */ {
-               r = drbd_khelper(mdev, "before-resync-source");
-               r = (r >> 8) & 0xff;
-               if (r > 0) {
-                       if (r == 3) {
-                               dev_info(DEV, "before-resync-source handler returned %d, "
-                                        "ignoring. Old userland tools?", r);
-                       } else {
-                               dev_info(DEV, "before-resync-source handler returned %d, "
+       if (!test_bit(B_RS_H_DONE, &mdev->flags)) {
+               if (side == C_SYNC_TARGET) {
+                       /* Since application IO was locked out during C_WF_BITMAP_T and
+                          C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
+                          we check that we might make the data inconsistent. */
+                       r = drbd_khelper(mdev, "before-resync-target");
+                       r = (r >> 8) & 0xff;
+                       if (r > 0) {
+                               dev_info(DEV, "before-resync-target handler returned %d, "
                                         "dropping connection.\n", r);
-                               drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+                               conn_request_state(mdev->tconn, NS(conn, C_DISCONNECTING), CS_HARD);
                                return;
                        }
+               } else /* C_SYNC_SOURCE */ {
+                       r = drbd_khelper(mdev, "before-resync-source");
+                       r = (r >> 8) & 0xff;
+                       if (r > 0) {
+                               if (r == 3) {
+                                       dev_info(DEV, "before-resync-source handler returned %d, "
+                                                "ignoring. Old userland tools?", r);
+                               } else {
+                                       dev_info(DEV, "before-resync-source handler returned %d, "
+                                                "dropping connection.\n", r);
+                                       conn_request_state(mdev->tconn, NS(conn, C_DISCONNECTING), CS_HARD);
+                                       return;
+                               }
+                       }
                }
        }
 
-       drbd_state_lock(mdev);
+       if (current == mdev->tconn->worker.task) {
+               /* The worker should not sleep waiting for state_mutex,
+                  that can take long */
+               if (!mutex_trylock(mdev->state_mutex)) {
+                       set_bit(B_RS_H_DONE, &mdev->flags);
+                       mdev->start_resync_timer.expires = jiffies + HZ/5;
+                       add_timer(&mdev->start_resync_timer);
+                       return;
+               }
+       } else {
+               mutex_lock(mdev->state_mutex);
+       }
+       clear_bit(B_RS_H_DONE, &mdev->flags);
 
        if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
-               drbd_state_unlock(mdev);
+               mutex_unlock(mdev->state_mutex);
                return;
        }
 
        write_lock_irq(&global_state_lock);
-       ns = mdev->state;
+       ns = drbd_read_state(mdev);
 
        ns.aftr_isp = !_drbd_may_sync_now(mdev);
 
@@ -1534,7 +1558,7 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
                ns.pdsk = D_INCONSISTENT;
 
        r = __drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
-       ns = mdev->state;
+       ns = drbd_read_state(mdev);
 
        if (ns.conn < C_CONNECTED)
                r = SS_UNKNOWN_ERROR;
@@ -1606,40 +1630,40 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
                drbd_md_sync(mdev);
        }
        put_ldev(mdev);
-       drbd_state_unlock(mdev);
+       mutex_unlock(mdev->state_mutex);
 }
 
 int drbd_worker(struct drbd_thread *thi)
 {
-       struct drbd_conf *mdev = thi->mdev;
+       struct drbd_tconn *tconn = thi->tconn;
        struct drbd_work *w = NULL;
+       struct drbd_conf *mdev;
        LIST_HEAD(work_list);
-       int intr = 0, i;
-
-       sprintf(current->comm, "drbd%d_worker", mdev_to_minor(mdev));
+       int vnr, intr = 0;
 
        while (get_t_state(thi) == RUNNING) {
-               drbd_thread_current_set_cpu(mdev);
+               drbd_thread_current_set_cpu(thi);
 
-               if (down_trylock(&mdev->tconn->data.work.s)) {
-                       mutex_lock(&mdev->tconn->data.mutex);
-                       if (mdev->tconn->data.socket && !mdev->tconn->net_conf->no_cork)
-                               drbd_tcp_uncork(mdev->tconn->data.socket);
-                       mutex_unlock(&mdev->tconn->data.mutex);
+               if (down_trylock(&tconn->data.work.s)) {
+                       mutex_lock(&tconn->data.mutex);
+                       if (tconn->data.socket && !tconn->net_conf->no_cork)
+                               drbd_tcp_uncork(tconn->data.socket);
+                       mutex_unlock(&tconn->data.mutex);
 
-                       intr = down_interruptible(&mdev->tconn->data.work.s);
+                       intr = down_interruptible(&tconn->data.work.s);
 
-                       mutex_lock(&mdev->tconn->data.mutex);
-                       if (mdev->tconn->data.socket  && !mdev->tconn->net_conf->no_cork)
-                               drbd_tcp_cork(mdev->tconn->data.socket);
-                       mutex_unlock(&mdev->tconn->data.mutex);
+                       mutex_lock(&tconn->data.mutex);
+                       if (tconn->data.socket  && !tconn->net_conf->no_cork)
+                               drbd_tcp_cork(tconn->data.socket);
+                       mutex_unlock(&tconn->data.mutex);
                }
 
                if (intr) {
-                       D_ASSERT(intr == -EINTR);
                        flush_signals(current);
-                       if (!expect(get_t_state(thi) != RUNNING))
+                       if (get_t_state(thi) == RUNNING) {
+                               conn_warn(tconn, "Worker got an unexpected signal\n");
                                continue;
+                       }
                        break;
                }
 
@@ -1650,8 +1674,8 @@ int drbd_worker(struct drbd_thread *thi)
                   this...   */
 
                w = NULL;
-               spin_lock_irq(&mdev->tconn->data.work.q_lock);
-               if (!expect(!list_empty(&mdev->tconn->data.work.q))) {
+               spin_lock_irq(&tconn->data.work.q_lock);
+               if (list_empty(&tconn->data.work.q)) {
                        /* something terribly wrong in our logic.
                         * we were able to down() the semaphore,
                         * but the list is empty... doh.
@@ -1663,57 +1687,52 @@ int drbd_worker(struct drbd_thread *thi)
                         *
                         * I'll try to get away just starting over this loop.
                         */
-                       spin_unlock_irq(&mdev->tconn->data.work.q_lock);
+                       conn_warn(tconn, "Work list unexpectedly empty\n");
+                       spin_unlock_irq(&tconn->data.work.q_lock);
                        continue;
                }
-               w = list_entry(mdev->tconn->data.work.q.next, struct drbd_work, list);
+               w = list_entry(tconn->data.work.q.next, struct drbd_work, list);
                list_del_init(&w->list);
-               spin_unlock_irq(&mdev->tconn->data.work.q_lock);
+               spin_unlock_irq(&tconn->data.work.q_lock);
 
-               if (!w->cb(mdev, w, mdev->state.conn < C_CONNECTED)) {
+               if (w->cb(w, tconn->cstate < C_WF_REPORT_PARAMS)) {
                        /* dev_warn(DEV, "worker: a callback failed! \n"); */
-                       if (mdev->state.conn >= C_CONNECTED)
-                               drbd_force_state(mdev,
-                                               NS(conn, C_NETWORK_FAILURE));
+                       if (tconn->cstate >= C_WF_REPORT_PARAMS)
+                               conn_request_state(tconn, NS(conn, C_NETWORK_FAILURE), CS_HARD);
                }
        }
-       D_ASSERT(test_bit(DEVICE_DYING, &mdev->flags));
-       D_ASSERT(test_bit(CONFIG_PENDING, &mdev->flags));
 
-       spin_lock_irq(&mdev->tconn->data.work.q_lock);
-       i = 0;
-       while (!list_empty(&mdev->tconn->data.work.q)) {
-               list_splice_init(&mdev->tconn->data.work.q, &work_list);
-               spin_unlock_irq(&mdev->tconn->data.work.q_lock);
+       spin_lock_irq(&tconn->data.work.q_lock);
+       while (!list_empty(&tconn->data.work.q)) {
+               list_splice_init(&tconn->data.work.q, &work_list);
+               spin_unlock_irq(&tconn->data.work.q_lock);
 
                while (!list_empty(&work_list)) {
                        w = list_entry(work_list.next, struct drbd_work, list);
                        list_del_init(&w->list);
-                       w->cb(mdev, w, 1);
-                       i++; /* dead debugging code */
+                       w->cb(w, 1);
                }
 
-               spin_lock_irq(&mdev->tconn->data.work.q_lock);
+               spin_lock_irq(&tconn->data.work.q_lock);
        }
-       sema_init(&mdev->tconn->data.work.s, 0);
+       sema_init(&tconn->data.work.s, 0);
        /* DANGEROUS race: if someone did queue his work within the spinlock,
         * but up() ed outside the spinlock, we could get an up() on the
         * semaphore without corresponding list entry.
         * So don't do that.
         */
-       spin_unlock_irq(&mdev->tconn->data.work.q_lock);
-
-       D_ASSERT(mdev->state.disk == D_DISKLESS && mdev->state.conn == C_STANDALONE);
-       /* _drbd_set_state only uses stop_nowait.
-        * wait here for the exiting receiver. */
-       drbd_thread_stop(&mdev->tconn->receiver);
-       drbd_mdev_cleanup(mdev);
-
-       dev_info(DEV, "worker terminated\n");
-
-       clear_bit(DEVICE_DYING, &mdev->flags);
-       clear_bit(CONFIG_PENDING, &mdev->flags);
-       wake_up(&mdev->state_wait);
+       spin_unlock_irq(&tconn->data.work.q_lock);
+
+       drbd_thread_stop(&tconn->receiver);
+       idr_for_each_entry(&tconn->volumes, mdev, vnr) {
+               D_ASSERT(mdev->state.disk == D_DISKLESS && mdev->state.conn == C_STANDALONE);
+               /* _drbd_set_state only uses stop_nowait.
+                * wait here for the exiting receiver. */
+               drbd_mdev_cleanup(mdev);
+       }
+       clear_bit(OBJECT_DYING, &tconn->flags);
+       clear_bit(CONFIG_PENDING, &tconn->flags);
+       wake_up(&tconn->ping_wait);
 
        return 0;
 }