dm raid1: add framework to hold bios during suspend
authorMikulas Patocka <mpatocka@redhat.com>
Thu, 10 Dec 2009 23:52:03 +0000 (23:52 +0000)
committerAlasdair G Kergon <agk@redhat.com>
Thu, 10 Dec 2009 23:52:03 +0000 (23:52 +0000)
Add framework to delay bios until a suspend and then resubmit them with
either DM_ENDIO_REQUEUE (if the suspend was noflush) or complete them
with -EIO.  I/O barrier support will use this.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Takahiro Yasui <tyasui@redhat.com>
Tested-by: Takahiro Yasui <tyasui@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
drivers/md/dm-raid1.c

index 85c8704c67bb1412a91cc1dd9ed3167991392b49..895cce75eee918579f0f489b510ced26e819d3e1 100644 (file)
@@ -58,6 +58,7 @@ struct mirror_set {
        struct bio_list reads;
        struct bio_list writes;
        struct bio_list failures;
+       struct bio_list holds;  /* bios are waiting until suspend */
 
        struct dm_region_hash *rh;
        struct dm_kcopyd_client *kcopyd_client;
@@ -450,6 +451,27 @@ static void map_region(struct dm_io_region *io, struct mirror *m,
        io->count = bio->bi_size >> 9;
 }
 
+static void hold_bio(struct mirror_set *ms, struct bio *bio)
+{
+       /*
+        * If device is suspended, complete the bio.
+        */
+       if (atomic_read(&ms->suspend)) {
+               if (dm_noflush_suspending(ms->ti))
+                       bio_endio(bio, DM_ENDIO_REQUEUE);
+               else
+                       bio_endio(bio, -EIO);
+               return;
+       }
+
+       /*
+        * Hold bio until the suspend is complete.
+        */
+       spin_lock_irq(&ms->lock);
+       bio_list_add(&ms->holds, bio);
+       spin_unlock_irq(&ms->lock);
+}
+
 /*-----------------------------------------------------------------
  * Reads
  *---------------------------------------------------------------*/
@@ -1225,6 +1247,9 @@ static void mirror_presuspend(struct dm_target *ti)
        struct mirror_set *ms = (struct mirror_set *) ti->private;
        struct dm_dirty_log *log = dm_rh_dirty_log(ms->rh);
 
+       struct bio_list holds;
+       struct bio *bio;
+
        atomic_set(&ms->suspend, 1);
 
        /*
@@ -1247,6 +1272,22 @@ static void mirror_presuspend(struct dm_target *ti)
         * we know that all of our I/O has been pushed.
         */
        flush_workqueue(ms->kmirrord_wq);
+
+       /*
+        * Now set ms->suspend is set and the workqueue flushed, no more
+        * entries can be added to ms->hold list, so process it.
+        *
+        * Bios can still arrive concurrently with or after this
+        * presuspend function, but they cannot join the hold list
+        * because ms->suspend is set.
+        */
+       spin_lock_irq(&ms->lock);
+       holds = ms->holds;
+       bio_list_init(&ms->holds);
+       spin_unlock_irq(&ms->lock);
+
+       while ((bio = bio_list_pop(&holds)))
+               hold_bio(ms, bio);
 }
 
 static void mirror_postsuspend(struct dm_target *ti)