Commit 72d7df4c authored by Bryan Gurney's avatar Bryan Gurney Committed by Mike Snitzer

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: default avatarBryan Gurney <bgurney@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent cc7a7fb3
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
struct badblock { struct badblock {
struct rb_node node; struct rb_node node;
sector_t bb; sector_t bb;
unsigned char wr_fail_cnt;
}; };
struct dust_device { struct dust_device {
...@@ -101,7 +102,8 @@ static int dust_remove_block(struct dust_device *dd, unsigned long long block) ...@@ -101,7 +102,8 @@ static int dust_remove_block(struct dust_device *dd, unsigned long long block)
return 0; 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; struct badblock *bblock;
unsigned long flags; unsigned long flags;
...@@ -115,6 +117,7 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block) ...@@ -115,6 +117,7 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block)
spin_lock_irqsave(&dd->dust_lock, flags); spin_lock_irqsave(&dd->dust_lock, flags);
bblock->bb = block; bblock->bb = block;
bblock->wr_fail_cnt = wr_fail_cnt;
if (!dust_rb_insert(&dd->badblocklist, bblock)) { if (!dust_rb_insert(&dd->badblocklist, bblock)) {
if (!dd->quiet_mode) { if (!dd->quiet_mode) {
DMERR("%s: block %llu already in badblocklist", DMERR("%s: block %llu already in badblocklist",
...@@ -126,8 +129,10 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block) ...@@ -126,8 +129,10 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block)
} }
dd->badblock_count++; dd->badblock_count++;
if (!dd->quiet_mode) if (!dd->quiet_mode) {
DMINFO("%s: badblock added at block %llu", __func__, block); DMINFO("%s: badblock added at block %llu with write fail count %hhu",
__func__, block, wr_fail_cnt);
}
spin_unlock_irqrestore(&dd->dust_lock, flags); spin_unlock_irqrestore(&dd->dust_lock, flags);
return 0; return 0;
...@@ -175,10 +180,15 @@ static int dust_map_read(struct dust_device *dd, sector_t thisblock, ...@@ -175,10 +180,15 @@ static int dust_map_read(struct dust_device *dd, sector_t thisblock,
return r; 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); 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) { if (bblk) {
rb_erase(&bblk->node, &dd->badblocklist); rb_erase(&bblk->node, &dd->badblocklist);
dd->badblock_count--; dd->badblock_count--;
...@@ -189,21 +199,24 @@ static void __dust_map_write(struct dust_device *dd, sector_t thisblock) ...@@ -189,21 +199,24 @@ static void __dust_map_write(struct dust_device *dd, sector_t thisblock)
(unsigned long long)thisblock); (unsigned long long)thisblock);
} }
} }
return DM_MAPIO_REMAPPED;
} }
static int dust_map_write(struct dust_device *dd, sector_t thisblock, static int dust_map_write(struct dust_device *dd, sector_t thisblock,
bool fail_read_on_bb) bool fail_read_on_bb)
{ {
unsigned long flags; unsigned long flags;
int ret = DM_MAPIO_REMAPPED;
if (fail_read_on_bb) { if (fail_read_on_bb) {
thisblock >>= dd->sect_per_block_shift; thisblock >>= dd->sect_per_block_shift;
spin_lock_irqsave(&dd->dust_lock, flags); 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); spin_unlock_irqrestore(&dd->dust_lock, flags);
} }
return DM_MAPIO_REMAPPED; return ret;
} }
static int dust_map(struct dm_target *ti, struct bio *bio) 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, ...@@ -377,6 +390,8 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv,
bool invalid_msg = false; bool invalid_msg = false;
int r = -EINVAL; int r = -EINVAL;
unsigned long long tmp, block; unsigned long long tmp, block;
unsigned char wr_fail_cnt;
unsigned int tmp_ui;
unsigned long flags; unsigned long flags;
char dummy; char dummy;
...@@ -422,7 +437,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, ...@@ -422,7 +437,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv,
} }
if (!strcasecmp(argv[0], "addbadblock")) if (!strcasecmp(argv[0], "addbadblock"))
r = dust_add_block(dd, block); r = dust_add_block(dd, block, 0);
else if (!strcasecmp(argv[0], "removebadblock")) else if (!strcasecmp(argv[0], "removebadblock"))
r = dust_remove_block(dd, block); r = dust_remove_block(dd, block);
else if (!strcasecmp(argv[0], "queryblock")) else if (!strcasecmp(argv[0], "queryblock"))
...@@ -430,6 +445,30 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, ...@@ -430,6 +445,30 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv,
else else
invalid_msg = true; 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 } else
DMERR("invalid number of arguments '%d'", argc); DMERR("invalid number of arguments '%d'", argc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment