dm raid1: add framework to hold bios during suspend
[safe/jmp/linux-2.6] / drivers / md / dm-region-hash.c
index 59f8d9d..00806b7 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/vmalloc.h>
 
 #include "dm.h"
-#include "dm-bio-list.h"
 
 #define        DM_MSG_PREFIX   "region hash"
 
@@ -80,6 +79,11 @@ struct dm_region_hash {
        struct list_head recovered_regions;
        struct list_head failed_recovered_regions;
 
+       /*
+        * If there was a barrier failure no regions can be marked clean.
+        */
+       int barrier_failure;
+
        void *context;
        sector_t target_begin;
 
@@ -212,6 +216,7 @@ struct dm_region_hash *dm_region_hash_create(
        INIT_LIST_HEAD(&rh->quiesced_regions);
        INIT_LIST_HEAD(&rh->recovered_regions);
        INIT_LIST_HEAD(&rh->failed_recovered_regions);
+       rh->barrier_failure = 0;
 
        rh->region_pool = mempool_create_kmalloc_pool(MIN_REGIONS,
                                                      sizeof(struct dm_region));
@@ -284,7 +289,7 @@ static struct dm_region *__rh_alloc(struct dm_region_hash *rh, region_t region)
 
        nreg = mempool_alloc(rh->region_pool, GFP_ATOMIC);
        if (unlikely(!nreg))
-               nreg = kmalloc(sizeof(*nreg), GFP_NOIO);
+               nreg = kmalloc(sizeof(*nreg), GFP_NOIO | __GFP_NOFAIL);
 
        nreg->state = rh->log->type->in_sync(rh->log, region, 1) ?
                      DM_RH_CLEAN : DM_RH_NOSYNC;
@@ -396,6 +401,11 @@ void dm_rh_mark_nosync(struct dm_region_hash *rh,
        region_t region = dm_rh_bio_to_region(rh, bio);
        int recovering = 0;
 
+       if (bio_empty_barrier(bio)) {
+               rh->barrier_failure = 1;
+               return;
+       }
+
        /* We must inform the log that the sync count has changed. */
        log->type->set_region_sync(log, region, 0);
 
@@ -516,8 +526,11 @@ void dm_rh_inc_pending(struct dm_region_hash *rh, struct bio_list *bios)
 {
        struct bio *bio;
 
-       for (bio = bios->head; bio; bio = bio->bi_next)
+       for (bio = bios->head; bio; bio = bio->bi_next) {
+               if (bio_empty_barrier(bio))
+                       continue;
                rh_inc(rh, dm_rh_bio_to_region(rh, bio));
+       }
 }
 EXPORT_SYMBOL_GPL(dm_rh_inc_pending);
 
@@ -545,7 +558,14 @@ void dm_rh_dec(struct dm_region_hash *rh, region_t region)
                 */
 
                /* do nothing for DM_RH_NOSYNC */
-               if (reg->state == DM_RH_RECOVERING) {
+               if (unlikely(rh->barrier_failure)) {
+                       /*
+                        * If a write barrier failed some time ago, we
+                        * don't know whether or not this write made it
+                        * to the disk, so we must resync the device.
+                        */
+                       reg->state = DM_RH_NOSYNC;
+               } else if (reg->state == DM_RH_RECOVERING) {
                        list_add_tail(&reg->list, &rh->quiesced_regions);
                } else if (reg->state == DM_RH_DIRTY) {
                        reg->state = DM_RH_CLEAN;