diff options
author | Bryan Gurney <bgurney@redhat.com> | 2019-10-22 22:46:01 +0200 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2019-11-05 21:25:34 +0100 |
commit | 72d7df4c8079306e7fbd6243bf951461ed647a47 (patch) | |
tree | 0203357542ba038301a43950d2d84aa740dc7bf7 /drivers/md/dm-dust.c | |
parent | dm dust: change ret to r in dust_map_read and dust_map (diff) | |
download | linux-72d7df4c8079306e7fbd6243bf951461ed647a47.tar.xz linux-72d7df4c8079306e7fbd6243bf951461ed647a47.zip |
dm dust: add limited write failure mode
Add a limited write failure mode which allows a write to a block to fail
a specified amount of times, prior to remapping. The "addbadblock"
message is extended to allow specifying the limited number of times a
write fails.
Example: add bad block on block 60, with 5 write failures:
dmsetup message 0 dust1 addbadblock 60 5
The write failure counter will be printed for newly added bad blocks.
Signed-off-by: Bryan Gurney <bgurney@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md/dm-dust.c')
-rw-r--r-- | drivers/md/dm-dust.c | 53 |
1 files changed, 46 insertions, 7 deletions
diff --git a/drivers/md/dm-dust.c b/drivers/md/dm-dust.c index 9a926a799fb4..eb37584427a4 100644 --- a/drivers/md/dm-dust.c +++ b/drivers/md/dm-dust.c @@ -17,6 +17,7 @@ struct badblock { struct rb_node node; sector_t bb; + unsigned char wr_fail_cnt; }; struct dust_device { @@ -101,7 +102,8 @@ static int dust_remove_block(struct dust_device *dd, unsigned long long block) return 0; } -static int dust_add_block(struct dust_device *dd, unsigned long long block) +static int dust_add_block(struct dust_device *dd, unsigned long long block, + unsigned char wr_fail_cnt) { struct badblock *bblock; unsigned long flags; @@ -115,6 +117,7 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block) spin_lock_irqsave(&dd->dust_lock, flags); bblock->bb = block; + bblock->wr_fail_cnt = wr_fail_cnt; if (!dust_rb_insert(&dd->badblocklist, bblock)) { if (!dd->quiet_mode) { DMERR("%s: block %llu already in badblocklist", @@ -126,8 +129,10 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block) } dd->badblock_count++; - if (!dd->quiet_mode) - DMINFO("%s: badblock added at block %llu", __func__, block); + if (!dd->quiet_mode) { + DMINFO("%s: badblock added at block %llu with write fail count %hhu", + __func__, block, wr_fail_cnt); + } spin_unlock_irqrestore(&dd->dust_lock, flags); return 0; @@ -175,10 +180,15 @@ static int dust_map_read(struct dust_device *dd, sector_t thisblock, return r; } -static void __dust_map_write(struct dust_device *dd, sector_t thisblock) +static int __dust_map_write(struct dust_device *dd, sector_t thisblock) { struct badblock *bblk = dust_rb_search(&dd->badblocklist, thisblock); + if (bblk && bblk->wr_fail_cnt > 0) { + bblk->wr_fail_cnt--; + return DM_MAPIO_KILL; + } + if (bblk) { rb_erase(&bblk->node, &dd->badblocklist); dd->badblock_count--; @@ -189,21 +199,24 @@ static void __dust_map_write(struct dust_device *dd, sector_t thisblock) (unsigned long long)thisblock); } } + + return DM_MAPIO_REMAPPED; } static int dust_map_write(struct dust_device *dd, sector_t thisblock, bool fail_read_on_bb) { unsigned long flags; + int ret = DM_MAPIO_REMAPPED; if (fail_read_on_bb) { thisblock >>= dd->sect_per_block_shift; spin_lock_irqsave(&dd->dust_lock, flags); - __dust_map_write(dd, thisblock); + ret = __dust_map_write(dd, thisblock); spin_unlock_irqrestore(&dd->dust_lock, flags); } - return DM_MAPIO_REMAPPED; + return ret; } static int dust_map(struct dm_target *ti, struct bio *bio) @@ -377,6 +390,8 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, bool invalid_msg = false; int r = -EINVAL; unsigned long long tmp, block; + unsigned char wr_fail_cnt; + unsigned int tmp_ui; unsigned long flags; char dummy; @@ -422,7 +437,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, } if (!strcasecmp(argv[0], "addbadblock")) - r = dust_add_block(dd, block); + r = dust_add_block(dd, block, 0); else if (!strcasecmp(argv[0], "removebadblock")) r = dust_remove_block(dd, block); else if (!strcasecmp(argv[0], "queryblock")) @@ -430,6 +445,30 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, else invalid_msg = true; + } else if (argc == 3) { + if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) + return r; + + if (sscanf(argv[2], "%u%c", &tmp_ui, &dummy) != 1) + return r; + + block = tmp; + if (tmp_ui > 255) { + DMERR("selected write fail count out of range"); + return r; + } + wr_fail_cnt = tmp_ui; + sector_div(size, dd->sect_per_block); + if (block > size) { + DMERR("selected block value out of range"); + return r; + } + + if (!strcasecmp(argv[0], "addbadblock")) + r = dust_add_block(dd, block, wr_fail_cnt); + else + invalid_msg = true; + } else DMERR("invalid number of arguments '%d'", argc); |