diff options
Diffstat (limited to 'drivers/md/dm-log.c')
-rw-r--r-- | drivers/md/dm-log.c | 77 |
1 files changed, 64 insertions, 13 deletions
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 9443896ede07..7035582786fb 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c | |||
@@ -145,8 +145,9 @@ int dm_dirty_log_type_unregister(struct dm_dirty_log_type *type) | |||
145 | EXPORT_SYMBOL(dm_dirty_log_type_unregister); | 145 | EXPORT_SYMBOL(dm_dirty_log_type_unregister); |
146 | 146 | ||
147 | struct dm_dirty_log *dm_dirty_log_create(const char *type_name, | 147 | struct dm_dirty_log *dm_dirty_log_create(const char *type_name, |
148 | struct dm_target *ti, | 148 | struct dm_target *ti, |
149 | unsigned int argc, char **argv) | 149 | int (*flush_callback_fn)(struct dm_target *ti), |
150 | unsigned int argc, char **argv) | ||
150 | { | 151 | { |
151 | struct dm_dirty_log_type *type; | 152 | struct dm_dirty_log_type *type; |
152 | struct dm_dirty_log *log; | 153 | struct dm_dirty_log *log; |
@@ -161,6 +162,7 @@ struct dm_dirty_log *dm_dirty_log_create(const char *type_name, | |||
161 | return NULL; | 162 | return NULL; |
162 | } | 163 | } |
163 | 164 | ||
165 | log->flush_callback_fn = flush_callback_fn; | ||
164 | log->type = type; | 166 | log->type = type; |
165 | if (type->ctr(log, ti, argc, argv)) { | 167 | if (type->ctr(log, ti, argc, argv)) { |
166 | kfree(log); | 168 | kfree(log); |
@@ -208,7 +210,9 @@ struct log_header { | |||
208 | 210 | ||
209 | struct log_c { | 211 | struct log_c { |
210 | struct dm_target *ti; | 212 | struct dm_target *ti; |
211 | int touched; | 213 | int touched_dirtied; |
214 | int touched_cleaned; | ||
215 | int flush_failed; | ||
212 | uint32_t region_size; | 216 | uint32_t region_size; |
213 | unsigned int region_count; | 217 | unsigned int region_count; |
214 | region_t sync_count; | 218 | region_t sync_count; |
@@ -233,6 +237,7 @@ struct log_c { | |||
233 | * Disk log fields | 237 | * Disk log fields |
234 | */ | 238 | */ |
235 | int log_dev_failed; | 239 | int log_dev_failed; |
240 | int log_dev_flush_failed; | ||
236 | struct dm_dev *log_dev; | 241 | struct dm_dev *log_dev; |
237 | struct log_header header; | 242 | struct log_header header; |
238 | 243 | ||
@@ -253,14 +258,14 @@ static inline void log_set_bit(struct log_c *l, | |||
253 | uint32_t *bs, unsigned bit) | 258 | uint32_t *bs, unsigned bit) |
254 | { | 259 | { |
255 | ext2_set_bit(bit, (unsigned long *) bs); | 260 | ext2_set_bit(bit, (unsigned long *) bs); |
256 | l->touched = 1; | 261 | l->touched_cleaned = 1; |
257 | } | 262 | } |
258 | 263 | ||
259 | static inline void log_clear_bit(struct log_c *l, | 264 | static inline void log_clear_bit(struct log_c *l, |
260 | uint32_t *bs, unsigned bit) | 265 | uint32_t *bs, unsigned bit) |
261 | { | 266 | { |
262 | ext2_clear_bit(bit, (unsigned long *) bs); | 267 | ext2_clear_bit(bit, (unsigned long *) bs); |
263 | l->touched = 1; | 268 | l->touched_dirtied = 1; |
264 | } | 269 | } |
265 | 270 | ||
266 | /*---------------------------------------------------------------- | 271 | /*---------------------------------------------------------------- |
@@ -287,6 +292,19 @@ static int rw_header(struct log_c *lc, int rw) | |||
287 | return dm_io(&lc->io_req, 1, &lc->header_location, NULL); | 292 | return dm_io(&lc->io_req, 1, &lc->header_location, NULL); |
288 | } | 293 | } |
289 | 294 | ||
295 | static int flush_header(struct log_c *lc) | ||
296 | { | ||
297 | struct dm_io_region null_location = { | ||
298 | .bdev = lc->header_location.bdev, | ||
299 | .sector = 0, | ||
300 | .count = 0, | ||
301 | }; | ||
302 | |||
303 | lc->io_req.bi_rw = WRITE_BARRIER; | ||
304 | |||
305 | return dm_io(&lc->io_req, 1, &null_location, NULL); | ||
306 | } | ||
307 | |||
290 | static int read_header(struct log_c *log) | 308 | static int read_header(struct log_c *log) |
291 | { | 309 | { |
292 | int r; | 310 | int r; |
@@ -378,7 +396,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, | |||
378 | } | 396 | } |
379 | 397 | ||
380 | lc->ti = ti; | 398 | lc->ti = ti; |
381 | lc->touched = 0; | 399 | lc->touched_dirtied = 0; |
400 | lc->touched_cleaned = 0; | ||
401 | lc->flush_failed = 0; | ||
382 | lc->region_size = region_size; | 402 | lc->region_size = region_size; |
383 | lc->region_count = region_count; | 403 | lc->region_count = region_count; |
384 | lc->sync = sync; | 404 | lc->sync = sync; |
@@ -406,6 +426,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti, | |||
406 | } else { | 426 | } else { |
407 | lc->log_dev = dev; | 427 | lc->log_dev = dev; |
408 | lc->log_dev_failed = 0; | 428 | lc->log_dev_failed = 0; |
429 | lc->log_dev_flush_failed = 0; | ||
409 | lc->header_location.bdev = lc->log_dev->bdev; | 430 | lc->header_location.bdev = lc->log_dev->bdev; |
410 | lc->header_location.sector = 0; | 431 | lc->header_location.sector = 0; |
411 | 432 | ||
@@ -614,6 +635,11 @@ static int disk_resume(struct dm_dirty_log *log) | |||
614 | 635 | ||
615 | /* write the new header */ | 636 | /* write the new header */ |
616 | r = rw_header(lc, WRITE); | 637 | r = rw_header(lc, WRITE); |
638 | if (!r) { | ||
639 | r = flush_header(lc); | ||
640 | if (r) | ||
641 | lc->log_dev_flush_failed = 1; | ||
642 | } | ||
617 | if (r) { | 643 | if (r) { |
618 | DMWARN("%s: Failed to write header on dirty region log device", | 644 | DMWARN("%s: Failed to write header on dirty region log device", |
619 | lc->log_dev->name); | 645 | lc->log_dev->name); |
@@ -656,18 +682,40 @@ static int core_flush(struct dm_dirty_log *log) | |||
656 | 682 | ||
657 | static int disk_flush(struct dm_dirty_log *log) | 683 | static int disk_flush(struct dm_dirty_log *log) |
658 | { | 684 | { |
659 | int r; | 685 | int r, i; |
660 | struct log_c *lc = (struct log_c *) log->context; | 686 | struct log_c *lc = log->context; |
661 | 687 | ||
662 | /* only write if the log has changed */ | 688 | /* only write if the log has changed */ |
663 | if (!lc->touched) | 689 | if (!lc->touched_cleaned && !lc->touched_dirtied) |
664 | return 0; | 690 | return 0; |
665 | 691 | ||
692 | if (lc->touched_cleaned && log->flush_callback_fn && | ||
693 | log->flush_callback_fn(lc->ti)) { | ||
694 | /* | ||
695 | * At this point it is impossible to determine which | ||
696 | * regions are clean and which are dirty (without | ||
697 | * re-reading the log off disk). So mark all of them | ||
698 | * dirty. | ||
699 | */ | ||
700 | lc->flush_failed = 1; | ||
701 | for (i = 0; i < lc->region_count; i++) | ||
702 | log_clear_bit(lc, lc->clean_bits, i); | ||
703 | } | ||
704 | |||
666 | r = rw_header(lc, WRITE); | 705 | r = rw_header(lc, WRITE); |
667 | if (r) | 706 | if (r) |
668 | fail_log_device(lc); | 707 | fail_log_device(lc); |
669 | else | 708 | else { |
670 | lc->touched = 0; | 709 | if (lc->touched_dirtied) { |
710 | r = flush_header(lc); | ||
711 | if (r) { | ||
712 | lc->log_dev_flush_failed = 1; | ||
713 | fail_log_device(lc); | ||
714 | } else | ||
715 | lc->touched_dirtied = 0; | ||
716 | } | ||
717 | lc->touched_cleaned = 0; | ||
718 | } | ||
671 | 719 | ||
672 | return r; | 720 | return r; |
673 | } | 721 | } |
@@ -681,7 +729,8 @@ static void core_mark_region(struct dm_dirty_log *log, region_t region) | |||
681 | static void core_clear_region(struct dm_dirty_log *log, region_t region) | 729 | static void core_clear_region(struct dm_dirty_log *log, region_t region) |
682 | { | 730 | { |
683 | struct log_c *lc = (struct log_c *) log->context; | 731 | struct log_c *lc = (struct log_c *) log->context; |
684 | log_set_bit(lc, lc->clean_bits, region); | 732 | if (likely(!lc->flush_failed)) |
733 | log_set_bit(lc, lc->clean_bits, region); | ||
685 | } | 734 | } |
686 | 735 | ||
687 | static int core_get_resync_work(struct dm_dirty_log *log, region_t *region) | 736 | static int core_get_resync_work(struct dm_dirty_log *log, region_t *region) |
@@ -762,7 +811,9 @@ static int disk_status(struct dm_dirty_log *log, status_type_t status, | |||
762 | switch(status) { | 811 | switch(status) { |
763 | case STATUSTYPE_INFO: | 812 | case STATUSTYPE_INFO: |
764 | DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name, | 813 | DMEMIT("3 %s %s %c", log->type->name, lc->log_dev->name, |
765 | lc->log_dev_failed ? 'D' : 'A'); | 814 | lc->log_dev_flush_failed ? 'F' : |
815 | lc->log_dev_failed ? 'D' : | ||
816 | 'A'); | ||
766 | break; | 817 | break; |
767 | 818 | ||
768 | case STATUSTYPE_TABLE: | 819 | case STATUSTYPE_TABLE: |