diff options
author | Josef Bacik <josef@redhat.com> | 2011-03-24 09:54:24 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2011-03-24 09:54:24 -0400 |
commit | 3407ef5262b55ca5d7139d2b555ef792fe531eec (patch) | |
tree | fb5e083851c636e515095bb3eb90325f4b175129 /drivers/md | |
parent | 024d37e95ec4a7ccc256973ab2feab01f4fbdd2d (diff) |
dm: add flakey target
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 <josef@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/Kconfig | 6 | ||||
-rw-r--r-- | drivers/md/Makefile | 1 | ||||
-rw-r--r-- | drivers/md/dm-flakey.c | 212 |
3 files changed, 219 insertions, 0 deletions
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 98d9ec85e0eb..8420129fc5ee 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig | |||
@@ -327,4 +327,10 @@ config DM_UEVENT | |||
327 | ---help--- | 327 | ---help--- |
328 | Generate udev events for DM events. | 328 | Generate udev events for DM events. |
329 | 329 | ||
330 | config DM_FLAKEY | ||
331 | tristate "Flakey target (EXPERIMENTAL)" | ||
332 | depends on BLK_DEV_DM && EXPERIMENTAL | ||
333 | ---help--- | ||
334 | A target that intermittently fails I/O for debugging purposes. | ||
335 | |||
330 | endif # MD | 336 | endif # MD |
diff --git a/drivers/md/Makefile b/drivers/md/Makefile index d0138606c2e8..448838b1f92a 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile | |||
@@ -29,6 +29,7 @@ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o | |||
29 | obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o | 29 | obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o |
30 | obj-$(CONFIG_DM_CRYPT) += dm-crypt.o | 30 | obj-$(CONFIG_DM_CRYPT) += dm-crypt.o |
31 | obj-$(CONFIG_DM_DELAY) += dm-delay.o | 31 | obj-$(CONFIG_DM_DELAY) += dm-delay.o |
32 | obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o | ||
32 | obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o | 33 | obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o |
33 | obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o | 34 | obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o |
34 | obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o | 35 | obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o |
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c new file mode 100644 index 000000000000..ea790623c30b --- /dev/null +++ b/drivers/md/dm-flakey.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003 Sistina Software (UK) Limited. | ||
3 | * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved. | ||
4 | * | ||
5 | * This file is released under the GPL. | ||
6 | */ | ||
7 | |||
8 | #include <linux/device-mapper.h> | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/blkdev.h> | ||
13 | #include <linux/bio.h> | ||
14 | #include <linux/slab.h> | ||
15 | |||
16 | #define DM_MSG_PREFIX "flakey" | ||
17 | |||
18 | /* | ||
19 | * Flakey: Used for testing only, simulates intermittent, | ||
20 | * catastrophic device failure. | ||
21 | */ | ||
22 | struct flakey_c { | ||
23 | struct dm_dev *dev; | ||
24 | unsigned long start_time; | ||
25 | sector_t start; | ||
26 | unsigned up_interval; | ||
27 | unsigned down_interval; | ||
28 | }; | ||
29 | |||
30 | /* | ||
31 | * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval> | ||
32 | */ | ||
33 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | ||
34 | { | ||
35 | struct flakey_c *fc; | ||
36 | unsigned long long tmp; | ||
37 | |||
38 | if (argc != 4) { | ||
39 | ti->error = "dm-flakey: Invalid argument count"; | ||
40 | return -EINVAL; | ||
41 | } | ||
42 | |||
43 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | ||
44 | if (!fc) { | ||
45 | ti->error = "dm-flakey: Cannot allocate linear context"; | ||
46 | return -ENOMEM; | ||
47 | } | ||
48 | fc->start_time = jiffies; | ||
49 | |||
50 | if (sscanf(argv[1], "%llu", &tmp) != 1) { | ||
51 | ti->error = "dm-flakey: Invalid device sector"; | ||
52 | goto bad; | ||
53 | } | ||
54 | fc->start = tmp; | ||
55 | |||
56 | if (sscanf(argv[2], "%u", &fc->up_interval) != 1) { | ||
57 | ti->error = "dm-flakey: Invalid up interval"; | ||
58 | goto bad; | ||
59 | } | ||
60 | |||
61 | if (sscanf(argv[3], "%u", &fc->down_interval) != 1) { | ||
62 | ti->error = "dm-flakey: Invalid down interval"; | ||
63 | goto bad; | ||
64 | } | ||
65 | |||
66 | if (!(fc->up_interval + fc->down_interval)) { | ||
67 | ti->error = "dm-flakey: Total (up + down) interval is zero"; | ||
68 | goto bad; | ||
69 | } | ||
70 | |||
71 | if (fc->up_interval + fc->down_interval < fc->up_interval) { | ||
72 | ti->error = "dm-flakey: Interval overflow"; | ||
73 | goto bad; | ||
74 | } | ||
75 | |||
76 | if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) { | ||
77 | ti->error = "dm-flakey: Device lookup failed"; | ||
78 | goto bad; | ||
79 | } | ||
80 | |||
81 | ti->num_flush_requests = 1; | ||
82 | ti->private = fc; | ||
83 | return 0; | ||
84 | |||
85 | bad: | ||
86 | kfree(fc); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | static void flakey_dtr(struct dm_target *ti) | ||
91 | { | ||
92 | struct flakey_c *fc = ti->private; | ||
93 | |||
94 | dm_put_device(ti, fc->dev); | ||
95 | kfree(fc); | ||
96 | } | ||
97 | |||
98 | static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | ||
99 | { | ||
100 | struct flakey_c *fc = ti->private; | ||
101 | |||
102 | return fc->start + (bi_sector - ti->begin); | ||
103 | } | ||
104 | |||
105 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | ||
106 | { | ||
107 | struct flakey_c *fc = ti->private; | ||
108 | |||
109 | bio->bi_bdev = fc->dev->bdev; | ||
110 | if (bio_sectors(bio)) | ||
111 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | ||
112 | } | ||
113 | |||
114 | static int flakey_map(struct dm_target *ti, struct bio *bio, | ||
115 | union map_info *map_context) | ||
116 | { | ||
117 | struct flakey_c *fc = ti->private; | ||
118 | unsigned elapsed; | ||
119 | |||
120 | /* Are we alive ? */ | ||
121 | elapsed = (jiffies - fc->start_time) / HZ; | ||
122 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | ||
123 | return -EIO; | ||
124 | |||
125 | flakey_map_bio(ti, bio); | ||
126 | |||
127 | return DM_MAPIO_REMAPPED; | ||
128 | } | ||
129 | |||
130 | static int flakey_status(struct dm_target *ti, status_type_t type, | ||
131 | char *result, unsigned int maxlen) | ||
132 | { | ||
133 | struct flakey_c *fc = ti->private; | ||
134 | |||
135 | switch (type) { | ||
136 | case STATUSTYPE_INFO: | ||
137 | result[0] = '\0'; | ||
138 | break; | ||
139 | |||
140 | case STATUSTYPE_TABLE: | ||
141 | snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | ||
142 | (unsigned long long)fc->start, fc->up_interval, | ||
143 | fc->down_interval); | ||
144 | break; | ||
145 | } | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) | ||
150 | { | ||
151 | struct flakey_c *fc = ti->private; | ||
152 | |||
153 | return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); | ||
154 | } | ||
155 | |||
156 | static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | ||
157 | struct bio_vec *biovec, int max_size) | ||
158 | { | ||
159 | struct flakey_c *fc = ti->private; | ||
160 | struct request_queue *q = bdev_get_queue(fc->dev->bdev); | ||
161 | |||
162 | if (!q->merge_bvec_fn) | ||
163 | return max_size; | ||
164 | |||
165 | bvm->bi_bdev = fc->dev->bdev; | ||
166 | bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); | ||
167 | |||
168 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | ||
169 | } | ||
170 | |||
171 | static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) | ||
172 | { | ||
173 | struct flakey_c *fc = ti->private; | ||
174 | |||
175 | return fn(ti, fc->dev, fc->start, ti->len, data); | ||
176 | } | ||
177 | |||
178 | static struct target_type flakey_target = { | ||
179 | .name = "flakey", | ||
180 | .version = {1, 1, 0}, | ||
181 | .module = THIS_MODULE, | ||
182 | .ctr = flakey_ctr, | ||
183 | .dtr = flakey_dtr, | ||
184 | .map = flakey_map, | ||
185 | .status = flakey_status, | ||
186 | .ioctl = flakey_ioctl, | ||
187 | .merge = flakey_merge, | ||
188 | .iterate_devices = flakey_iterate_devices, | ||
189 | }; | ||
190 | |||
191 | static int __init dm_flakey_init(void) | ||
192 | { | ||
193 | int r = dm_register_target(&flakey_target); | ||
194 | |||
195 | if (r < 0) | ||
196 | DMERR("register failed %d", r); | ||
197 | |||
198 | return r; | ||
199 | } | ||
200 | |||
201 | static void __exit dm_flakey_exit(void) | ||
202 | { | ||
203 | dm_unregister_target(&flakey_target); | ||
204 | } | ||
205 | |||
206 | /* Module hooks */ | ||
207 | module_init(dm_flakey_init); | ||
208 | module_exit(dm_flakey_exit); | ||
209 | |||
210 | MODULE_DESCRIPTION(DM_NAME " flakey target"); | ||
211 | MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); | ||
212 | MODULE_LICENSE("GPL"); | ||