Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / drivers / block / drbd / drbd_worker.c
index 0e5bf8c..108d580 100644 (file)
@@ -387,6 +387,13 @@ static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
        if (drbd_submit_ee(mdev, e, READ, DRBD_FAULT_RS_RD) == 0)
                return 0;
 
+       /* drbd_submit_ee currently fails for one reason only:
+        * not being able to allocate enough bios.
+        * Is dropping the connection going to help? */
+       spin_lock_irq(&mdev->req_lock);
+       list_del(&e->w.list);
+       spin_unlock_irq(&mdev->req_lock);
+
        drbd_free_ee(mdev, e);
 defer:
        put_ldev(mdev);
@@ -421,7 +428,7 @@ static void fifo_set(struct fifo_buffer *fb, int value)
        int i;
 
        for (i = 0; i < fb->size; i++)
-               fb->values[i] += value;
+               fb->values[i] = value;
 }
 
 static int fifo_push(struct fifo_buffer *fb, int value)
@@ -522,6 +529,12 @@ int w_make_resync_request(struct drbd_conf *mdev,
                dev_err(DEV, "%s in w_make_resync_request\n",
                        drbd_conn_str(mdev->state.conn));
 
+       if (mdev->rs_total == 0) {
+               /* empty resync? */
+               drbd_resync_finished(mdev);
+               return 1;
+       }
+
        if (!get_ldev(mdev)) {
                /* Since we only need to access mdev->rsync a
                   get_ldev_if_state(mdev,D_FAILED) would be sufficient, but
@@ -534,8 +547,9 @@ int w_make_resync_request(struct drbd_conf *mdev,
 
        /* starting with drbd 8.3.8, we can handle multi-bio EEs,
         * if it should be necessary */
-       max_segment_size = mdev->agreed_pro_version < 94 ?
-               queue_max_segment_size(mdev->rq_queue) : DRBD_MAX_SEGMENT_SIZE;
+       max_segment_size =
+               mdev->agreed_pro_version < 94 ? queue_max_segment_size(mdev->rq_queue) :
+               mdev->agreed_pro_version < 95 ? DRBD_MAX_SIZE_H80_PACKET : DRBD_MAX_SEGMENT_SIZE;
 
        if (mdev->rs_plan_s.size) { /* mdev->sync_conf.c_plan_ahead */
                number = drbd_rs_controller(mdev) >> (BM_BLOCK_SHIFT - 9);
@@ -767,6 +781,14 @@ static int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int ca
        return 1;
 }
 
+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);
+}
+
 int drbd_resync_finished(struct drbd_conf *mdev)
 {
        unsigned long db, dt, dbdt;
@@ -806,6 +828,8 @@ int drbd_resync_finished(struct drbd_conf *mdev)
        if (!get_ldev(mdev))
                goto out;
 
+       ping_peer(mdev);
+
        spin_lock_irq(&mdev->req_lock);
        os = mdev->state;
 
@@ -898,6 +922,8 @@ out:
        mdev->rs_paused = 0;
        mdev->ov_start_sector = 0;
 
+       drbd_md_sync(mdev);
+
        if (test_and_clear_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags)) {
                dev_warn(DEV, "Writing the whole bitmap, due to failed kmalloc\n");
                drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL, "write from resync_finished");
@@ -914,9 +940,13 @@ static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_epoch_ent
 {
        if (drbd_ee_has_active_page(e)) {
                /* This might happen if sendpage() has not finished */
+               int i = (e->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);
+               wake_up(&drbd_pp_wait);
        } else
                drbd_free_ee(mdev, e);
 }
@@ -1023,7 +1053,10 @@ int w_e_end_csum_rs_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
                return 1;
        }
 
-       drbd_rs_complete_io(mdev, e->sector);
+       if (get_ldev(mdev)) {
+               drbd_rs_complete_io(mdev, e->sector);
+               put_ldev(mdev);
+       }
 
        di = e->digest;
 
@@ -1130,7 +1163,10 @@ int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
 
        /* after "cancel", because after drbd_disconnect/drbd_rs_cancel_all
         * the resync lru has been cleaned up already */
-       drbd_rs_complete_io(mdev, e->sector);
+       if (get_ldev(mdev)) {
+               drbd_rs_complete_io(mdev, e->sector);
+               put_ldev(mdev);
+       }
 
        di = e->digest;
 
@@ -1409,14 +1445,6 @@ int drbd_alter_sa(struct drbd_conf *mdev, int na)
        return retcode;
 }
 
-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);
-}
-
 /**
  * drbd_start_resync() - Start the resync process
  * @mdev:      DRBD device.
@@ -1516,9 +1544,21 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
                     (unsigned long) mdev->rs_total << (BM_BLOCK_SHIFT-10),
                     (unsigned long) mdev->rs_total);
 
-               if (mdev->rs_total == 0) {
-                       /* Peer still reachable? Beware of failing before-resync-target handlers! */
-                       ping_peer(mdev);
+               if (mdev->agreed_pro_version < 95 && mdev->rs_total == 0) {
+                       /* This still has a race (about when exactly the peers
+                        * detect connection loss) that can lead to a full sync
+                        * on next handshake. In 8.3.9 we fixed this with explicit
+                        * resync-finished notifications, but the fix
+                        * introduces a protocol change.  Sleeping for some
+                        * time longer than the ping interval + timeout on the
+                        * SyncSource, to give the SyncTarget the chance to
+                        * detect connection loss, then waiting for a ping
+                        * response (implicit in drbd_resync_finished) reduces
+                        * the race considerably, but does not solve it. */
+                       if (side == C_SYNC_SOURCE)
+                               schedule_timeout_interruptible(
+                                       mdev->net_conf->ping_int * HZ +
+                                       mdev->net_conf->ping_timeo*HZ/9);
                        drbd_resync_finished(mdev);
                }