forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This target is the same as the linear target except that it returns I/O errors periodically. It's been found useful in simulating failing devices for testing purposes. I needed a dm target to do some failure testing on btrfs's raid code, and Mike pointed me at this. Signed-off-by: Josef Bacik <[email protected]> Signed-off-by: Alasdair G Kergon <[email protected]>
- Loading branch information
Showing
4 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
dm-flakey | ||
========= | ||
|
||
This target is the same as the linear target except that it returns I/O | ||
errors periodically. It's been found useful in simulating failing | ||
devices for testing purposes. | ||
|
||
Starting from the time the table is loaded, the device is available for | ||
<up interval> seconds, then returns errors for <down interval> seconds, | ||
and then this cycle repeats. | ||
|
||
Parameters: <dev path> <offset> <up interval> <down interval> | ||
<dev path>: Full pathname to the underlying block-device, or a | ||
"major:minor" device-number. | ||
<offset>: Starting sector within the device. | ||
<up interval>: Number of seconds device is available. | ||
<down interval>: Number of seconds device returns errors. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/* | ||
* Copyright (C) 2003 Sistina Software (UK) Limited. | ||
* Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved. | ||
* | ||
* This file is released under the GPL. | ||
*/ | ||
|
||
#include <linux/device-mapper.h> | ||
|
||
#include <linux/module.h> | ||
#include <linux/init.h> | ||
#include <linux/blkdev.h> | ||
#include <linux/bio.h> | ||
#include <linux/slab.h> | ||
|
||
#define DM_MSG_PREFIX "flakey" | ||
|
||
/* | ||
* Flakey: Used for testing only, simulates intermittent, | ||
* catastrophic device failure. | ||
*/ | ||
struct flakey_c { | ||
struct dm_dev *dev; | ||
unsigned long start_time; | ||
sector_t start; | ||
unsigned up_interval; | ||
unsigned down_interval; | ||
}; | ||
|
||
/* | ||
* Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval> | ||
*/ | ||
static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | ||
{ | ||
struct flakey_c *fc; | ||
unsigned long long tmp; | ||
|
||
if (argc != 4) { | ||
ti->error = "dm-flakey: Invalid argument count"; | ||
return -EINVAL; | ||
} | ||
|
||
fc = kmalloc(sizeof(*fc), GFP_KERNEL); | ||
if (!fc) { | ||
ti->error = "dm-flakey: Cannot allocate linear context"; | ||
return -ENOMEM; | ||
} | ||
fc->start_time = jiffies; | ||
|
||
if (sscanf(argv[1], "%llu", &tmp) != 1) { | ||
ti->error = "dm-flakey: Invalid device sector"; | ||
goto bad; | ||
} | ||
fc->start = tmp; | ||
|
||
if (sscanf(argv[2], "%u", &fc->up_interval) != 1) { | ||
ti->error = "dm-flakey: Invalid up interval"; | ||
goto bad; | ||
} | ||
|
||
if (sscanf(argv[3], "%u", &fc->down_interval) != 1) { | ||
ti->error = "dm-flakey: Invalid down interval"; | ||
goto bad; | ||
} | ||
|
||
if (!(fc->up_interval + fc->down_interval)) { | ||
ti->error = "dm-flakey: Total (up + down) interval is zero"; | ||
goto bad; | ||
} | ||
|
||
if (fc->up_interval + fc->down_interval < fc->up_interval) { | ||
ti->error = "dm-flakey: Interval overflow"; | ||
goto bad; | ||
} | ||
|
||
if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) { | ||
ti->error = "dm-flakey: Device lookup failed"; | ||
goto bad; | ||
} | ||
|
||
ti->num_flush_requests = 1; | ||
ti->private = fc; | ||
return 0; | ||
|
||
bad: | ||
kfree(fc); | ||
return -EINVAL; | ||
} | ||
|
||
static void flakey_dtr(struct dm_target *ti) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
dm_put_device(ti, fc->dev); | ||
kfree(fc); | ||
} | ||
|
||
static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
return fc->start + (bi_sector - ti->begin); | ||
} | ||
|
||
static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
bio->bi_bdev = fc->dev->bdev; | ||
if (bio_sectors(bio)) | ||
bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | ||
} | ||
|
||
static int flakey_map(struct dm_target *ti, struct bio *bio, | ||
union map_info *map_context) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
unsigned elapsed; | ||
|
||
/* Are we alive ? */ | ||
elapsed = (jiffies - fc->start_time) / HZ; | ||
if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | ||
return -EIO; | ||
|
||
flakey_map_bio(ti, bio); | ||
|
||
return DM_MAPIO_REMAPPED; | ||
} | ||
|
||
static int flakey_status(struct dm_target *ti, status_type_t type, | ||
char *result, unsigned int maxlen) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
switch (type) { | ||
case STATUSTYPE_INFO: | ||
result[0] = '\0'; | ||
break; | ||
|
||
case STATUSTYPE_TABLE: | ||
snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | ||
(unsigned long long)fc->start, fc->up_interval, | ||
fc->down_interval); | ||
break; | ||
} | ||
return 0; | ||
} | ||
|
||
static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); | ||
} | ||
|
||
static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | ||
struct bio_vec *biovec, int max_size) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
struct request_queue *q = bdev_get_queue(fc->dev->bdev); | ||
|
||
if (!q->merge_bvec_fn) | ||
return max_size; | ||
|
||
bvm->bi_bdev = fc->dev->bdev; | ||
bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); | ||
|
||
return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | ||
} | ||
|
||
static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) | ||
{ | ||
struct flakey_c *fc = ti->private; | ||
|
||
return fn(ti, fc->dev, fc->start, ti->len, data); | ||
} | ||
|
||
static struct target_type flakey_target = { | ||
.name = "flakey", | ||
.version = {1, 1, 0}, | ||
.module = THIS_MODULE, | ||
.ctr = flakey_ctr, | ||
.dtr = flakey_dtr, | ||
.map = flakey_map, | ||
.status = flakey_status, | ||
.ioctl = flakey_ioctl, | ||
.merge = flakey_merge, | ||
.iterate_devices = flakey_iterate_devices, | ||
}; | ||
|
||
static int __init dm_flakey_init(void) | ||
{ | ||
int r = dm_register_target(&flakey_target); | ||
|
||
if (r < 0) | ||
DMERR("register failed %d", r); | ||
|
||
return r; | ||
} | ||
|
||
static void __exit dm_flakey_exit(void) | ||
{ | ||
dm_unregister_target(&flakey_target); | ||
} | ||
|
||
/* Module hooks */ | ||
module_init(dm_flakey_init); | ||
module_exit(dm_flakey_exit); | ||
|
||
MODULE_DESCRIPTION(DM_NAME " flakey target"); | ||
MODULE_AUTHOR("Joe Thornber <[email protected]>"); | ||
MODULE_LICENSE("GPL"); |