dm raid1: remove bio_endio from dm_rh_mark_nosync
[safe/jmp/linux-2.6] / drivers / md / dm-log.c
index 6b23631..7035582 100644 (file)
@@ -145,8 +145,9 @@ int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type)
 EXPORT_SYMBOL(dm_dirty_log_type_unregister);
 
 struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
-                                        struct dm_target *ti,
-                                        unsigned int argc, char **argv)
+                       struct dm_target *ti,
+                       int (*flush_callback_fn)(struct dm_target *ti),
+                       unsigned int argc, char **argv)
 {
        struct dm_dirty_log_type *type;
        struct dm_dirty_log *log;
@@ -161,6 +162,7 @@ struct dm_dirty_log *dm_dirty_log_create(const char *type_name,
                return NULL;
        }
 
+       log->flush_callback_fn = flush_callback_fn;
        log->type = type;
        if (type->ctr(log, ti, argc, argv)) {
                kfree(log);
@@ -210,6 +212,7 @@ struct log_c {
        struct dm_target *ti;
        int touched_dirtied;
        int touched_cleaned;
+       int flush_failed;
        uint32_t region_size;
        unsigned int region_count;
        region_t sync_count;
@@ -234,6 +237,7 @@ struct log_c {
         * Disk log fields
         */
        int log_dev_failed;
+       int log_dev_flush_failed;
        struct dm_dev *log_dev;
        struct log_header header;
 
@@ -394,6 +398,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
        lc->ti = ti;
        lc->touched_dirtied = 0;
        lc->touched_cleaned = 0;
+       lc->flush_failed = 0;
        lc->region_size = region_size;
        lc->region_count = region_count;
        lc->sync = sync;
@@ -421,6 +426,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
        } else {
                lc->log_dev = dev;
                lc->log_dev_failed = 0;
+               lc->log_dev_flush_failed = 0;
                lc->header_location.bdev = lc->log_dev->bdev;
                lc->header_location.sector = 0;
 
@@ -629,8 +635,11 @@ static int disk_resume(struct dm_dirty_log *log)
 
        /* write the new header */
        r = rw_header(lc, WRITE);
-       if (!r)
+       if (!r) {
                r = flush_header(lc);
+               if (r)
+                       lc->log_dev_flush_failed = 1;
+       }
        if (r) {
                DMWARN("%s: Failed to write header on dirty region log device",
                       lc->log_dev->name);
@@ -673,22 +682,36 @@ static int core_flush(struct dm_dirty_log *log)
 
 static int disk_flush(struct dm_dirty_log *log)
 {
-       int r;
-       struct log_c *lc = (struct log_c *) log->context;
+       int r, i;
+       struct log_c *lc = log->context;
 
        /* only write if the log has changed */
        if (!lc->touched_cleaned && !lc->touched_dirtied)
                return 0;
 
+       if (lc->touched_cleaned && log->flush_callback_fn &&
+           log->flush_callback_fn(lc->ti)) {
+               /*
+                * At this point it is impossible to determine which
+                * regions are clean and which are dirty (without
+                * re-reading the log off disk). So mark all of them
+                * dirty.
+                */
+               lc->flush_failed = 1;
+               for (i = 0; i < lc->region_count; i++)
+                       log_clear_bit(lc, lc->clean_bits, i);
+       }
+
        r = rw_header(lc, WRITE);
        if (r)
                fail_log_device(lc);
        else {
                if (lc->touched_dirtied) {
                        r = flush_header(lc);
-                       if (r)
+                       if (r) {
+                               lc->log_dev_flush_failed = 1;
                                fail_log_device(lc);
-                       else
+                       else
                                lc->touched_dirtied = 0;
                }
                lc->touched_cleaned = 0;
@@ -706,7 +729,8 @@ static void core_mark_region(struct dm_dirty_log *log, region_t region)
 static void core_clear_region(struct dm_dirty_log *log, region_t region)
 {
        struct log_c *lc = (struct log_c *) log->context;
-       log_set_bit(lc, lc->clean_bits, region);
+       if (likely(!lc->flush_failed))
+               log_set_bit(lc, lc->clean_bits, region);
 }
 
 static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
@@ -787,7 +811,9 @@ static int disk_status(struct dm_dirty_log *log, status_type_t status,
        switch(status) {
        case STATUSTYPE_INFO:
                DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name,
-                      lc->log_dev_failed ? 'D' : 'A');
+                      lc->log_dev_flush_failed ? 'F' :
+                      lc->log_dev_failed ? 'D' :
+                      'A');
                break;
 
        case STATUSTYPE_TABLE: