mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-15 09:55:36 +00:00
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>
This commit is contained in:
parent
cc7a7fb3b6
commit
72d7df4c80
@ -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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
(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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user