USB: put claimed interfaces in the "suspended" state
[safe/jmp/linux-2.6] / drivers / md / dm-raid1.c
index e363335..ddda531 100644 (file)
@@ -69,6 +69,7 @@ struct mirror_set {
        region_t nr_regions;
        int in_sync;
        int log_failure;
+       int leg_failure;
        atomic_t suspend;
 
        atomic_t default_mirror;        /* Default mirror */
@@ -211,6 +212,8 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type)
        struct mirror_set *ms = m->ms;
        struct mirror *new;
 
+       ms->leg_failure = 1;
+
        /*
         * error_count is used for nothing more than a
         * simple way to tell if a device has encountered
@@ -462,9 +465,17 @@ static void map_region(struct dm_io_region *io, struct mirror *m,
 static void hold_bio(struct mirror_set *ms, struct bio *bio)
 {
        /*
-        * If device is suspended, complete the bio.
+        * Lock is required to avoid race condition during suspend
+        * process.
         */
+       spin_lock_irq(&ms->lock);
+
        if (atomic_read(&ms->suspend)) {
+               spin_unlock_irq(&ms->lock);
+
+               /*
+                * If device is suspended, complete the bio.
+                */
                if (dm_noflush_suspending(ms->ti))
                        bio_endio(bio, DM_ENDIO_REQUEUE);
                else
@@ -475,7 +486,6 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio)
        /*
         * Hold bio until the suspend is complete.
         */
-       spin_lock_irq(&ms->lock);
        bio_list_add(&ms->holds, bio);
        spin_unlock_irq(&ms->lock);
 }
@@ -721,7 +731,7 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
        /*
         * Dispatch io.
         */
-       if (unlikely(ms->log_failure)) {
+       if (unlikely(ms->log_failure) && errors_handled(ms)) {
                spin_lock_irq(&ms->lock);
                bio_list_merge(&ms->failures, &sync);
                spin_unlock_irq(&ms->lock);
@@ -734,8 +744,15 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
                dm_rh_delay(ms->rh, bio);
 
        while ((bio = bio_list_pop(&nosync))) {
-               map_bio(get_default_mirror(ms), bio);
-               generic_make_request(bio);
+               if (unlikely(ms->leg_failure) && errors_handled(ms)) {
+                       spin_lock_irq(&ms->lock);
+                       bio_list_add(&ms->failures, bio);
+                       spin_unlock_irq(&ms->lock);
+                       wakeup_mirrord(ms);
+               } else {
+                       map_bio(get_default_mirror(ms), bio);
+                       generic_make_request(bio);
+               }
        }
 }
 
@@ -842,12 +859,17 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
        }
 
        spin_lock_init(&ms->lock);
+       bio_list_init(&ms->reads);
+       bio_list_init(&ms->writes);
+       bio_list_init(&ms->failures);
+       bio_list_init(&ms->holds);
 
        ms->ti = ti;
        ms->nr_mirrors = nr_mirrors;
        ms->nr_regions = dm_sector_div_up(ti->len, region_size);
        ms->in_sync = 0;
        ms->log_failure = 0;
+       ms->leg_failure = 0;
        atomic_set(&ms->suspend, 0);
        atomic_set(&ms->default_mirror, DEFAULT_MIRROR);
 
@@ -905,8 +927,7 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
                return -EINVAL;
        }
 
-       if (dm_get_device(ti, argv[0], offset, ti->len,
-                         dm_table_get_mode(ti->table),
+       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
                          &ms->mirror[mirror].dev)) {
                ti->error = "Device lookup failure";
                return -ENXIO;
@@ -1247,6 +1268,20 @@ static void mirror_presuspend(struct dm_target *ti)
        atomic_set(&ms->suspend, 1);
 
        /*
+        * Process bios in the hold list to start recovery waiting
+        * for bios in the hold list. After the process, no bio has
+        * a chance to be added in 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);
+
+       /*
         * We must finish up all the work that we've
         * generated (i.e. recovery work).
         */
@@ -1266,22 +1301,6 @@ 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)