[PATCH] dm: raid1: fix waiting for io on suspend
authorJonathan E Brassow <jbrassow@redhat.com>
Thu, 9 Nov 2006 01:44:44 +0000 (17:44 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 9 Nov 2006 02:29:23 +0000 (18:29 -0800)
All device-mapper targets must complete outstanding I/O before suspending.
The mirror target generates I/O in its recovery phase and fails to wait for
it.  It needs to be tracked so we can ensure that it has completed before we
suspend.

[akpm@osdl.org: cleanup]
Signed-off-by: Jonathan E Brassow <jbrassow@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Cc: <dm-devel@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/md/dm-raid1.c

index 659224cb7c533d6a7dba75e84cf03ab2ced13a0c..48a653b3f518d1a066b598291c50e2ac43e516fa 100644 (file)
@@ -24,6 +24,7 @@
 
 static struct workqueue_struct *_kmirrord_wq;
 static struct work_struct _kmirrord_work;
+static DECLARE_WAIT_QUEUE_HEAD(_kmirrord_recovery_stopped);
 
 static inline void wake(void)
 {
@@ -83,6 +84,7 @@ struct region_hash {
        struct list_head *buckets;
 
        spinlock_t region_lock;
+       atomic_t recovery_in_flight;
        struct semaphore recovery_count;
        struct list_head clean_regions;
        struct list_head quiesced_regions;
@@ -191,6 +193,7 @@ static int rh_init(struct region_hash *rh, struct mirror_set *ms,
 
        spin_lock_init(&rh->region_lock);
        sema_init(&rh->recovery_count, 0);
+       atomic_set(&rh->recovery_in_flight, 0);
        INIT_LIST_HEAD(&rh->clean_regions);
        INIT_LIST_HEAD(&rh->quiesced_regions);
        INIT_LIST_HEAD(&rh->recovered_regions);
@@ -382,6 +385,8 @@ static void rh_update_states(struct region_hash *rh)
                rh->log->type->clear_region(rh->log, reg->key);
                rh->log->type->complete_resync_work(rh->log, reg->key, 1);
                dispatch_bios(rh->ms, &reg->delayed_bios);
+               if (atomic_dec_and_test(&rh->recovery_in_flight))
+                       wake_up_all(&_kmirrord_recovery_stopped);
                up(&rh->recovery_count);
                mempool_free(reg, rh->region_pool);
        }
@@ -502,11 +507,21 @@ static int __rh_recovery_prepare(struct region_hash *rh)
 
 static void rh_recovery_prepare(struct region_hash *rh)
 {
-       while (!down_trylock(&rh->recovery_count))
+       /* Extra reference to avoid race with rh_stop_recovery */
+       atomic_inc(&rh->recovery_in_flight);
+
+       while (!down_trylock(&rh->recovery_count)) {
+               atomic_inc(&rh->recovery_in_flight);
                if (__rh_recovery_prepare(rh) <= 0) {
+                       atomic_dec(&rh->recovery_in_flight);
                        up(&rh->recovery_count);
                        break;
                }
+       }
+
+       /* Drop the extra reference */
+       if (atomic_dec_and_test(&rh->recovery_in_flight))
+               wake_up_all(&_kmirrord_recovery_stopped);
 }
 
 /*
@@ -1177,6 +1192,11 @@ static void mirror_postsuspend(struct dm_target *ti)
        struct dirty_log *log = ms->rh.log;
 
        rh_stop_recovery(&ms->rh);
+
+       /* Wait for all I/O we generated to complete */
+       wait_event(_kmirrord_recovery_stopped,
+                  !atomic_read(&ms->rh.recovery_in_flight));
+
        if (log->type->suspend && log->type->suspend(log))
                /* FIXME: need better error handling */
                DMWARN("log suspend failed");