Commit | Line | Data |
---|---|---|
3407ef52 JB |
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 | ||
dfd068b0 MS |
30 | static int parse_features(struct dm_arg_set *as, struct dm_target *ti) |
31 | { | |
32 | int r; | |
33 | unsigned argc; | |
34 | const char *arg_name; | |
35 | ||
36 | static struct dm_arg _args[] = { | |
37 | {0, 0, "Invalid number of feature args"}, | |
38 | }; | |
39 | ||
40 | /* No feature arguments supplied. */ | |
41 | if (!as->argc) | |
42 | return 0; | |
43 | ||
44 | r = dm_read_arg_group(_args, as, &argc, &ti->error); | |
45 | if (r) | |
46 | return -EINVAL; | |
47 | ||
48 | while (argc && !r) { | |
49 | arg_name = dm_shift_arg(as); | |
50 | argc--; | |
51 | ||
52 | ti->error = "Unrecognised flakey feature requested"; | |
53 | r = -EINVAL; | |
54 | } | |
55 | ||
56 | return r; | |
57 | } | |
58 | ||
3407ef52 | 59 | /* |
dfd068b0 MS |
60 | * Construct a flakey mapping: |
61 | * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*] | |
3407ef52 JB |
62 | */ |
63 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |
64 | { | |
dfd068b0 MS |
65 | static struct dm_arg _args[] = { |
66 | {0, UINT_MAX, "Invalid up interval"}, | |
67 | {0, UINT_MAX, "Invalid down interval"}, | |
68 | }; | |
69 | ||
70 | int r; | |
3407ef52 | 71 | struct flakey_c *fc; |
dfd068b0 MS |
72 | unsigned long long tmpll; |
73 | struct dm_arg_set as; | |
74 | const char *devname; | |
3407ef52 | 75 | |
dfd068b0 MS |
76 | as.argc = argc; |
77 | as.argv = argv; | |
78 | ||
79 | if (argc < 4) { | |
80 | ti->error = "Invalid argument count"; | |
3407ef52 JB |
81 | return -EINVAL; |
82 | } | |
83 | ||
84 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | |
85 | if (!fc) { | |
dfd068b0 | 86 | ti->error = "Cannot allocate linear context"; |
3407ef52 JB |
87 | return -ENOMEM; |
88 | } | |
89 | fc->start_time = jiffies; | |
90 | ||
dfd068b0 MS |
91 | devname = dm_shift_arg(&as); |
92 | ||
93 | if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) { | |
94 | ti->error = "Invalid device sector"; | |
3407ef52 JB |
95 | goto bad; |
96 | } | |
dfd068b0 | 97 | fc->start = tmpll; |
3407ef52 | 98 | |
dfd068b0 MS |
99 | r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error); |
100 | if (r) | |
3407ef52 | 101 | goto bad; |
3407ef52 | 102 | |
dfd068b0 MS |
103 | r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error); |
104 | if (r) | |
3407ef52 | 105 | goto bad; |
3407ef52 JB |
106 | |
107 | if (!(fc->up_interval + fc->down_interval)) { | |
dfd068b0 | 108 | ti->error = "Total (up + down) interval is zero"; |
3407ef52 JB |
109 | goto bad; |
110 | } | |
111 | ||
112 | if (fc->up_interval + fc->down_interval < fc->up_interval) { | |
dfd068b0 | 113 | ti->error = "Interval overflow"; |
3407ef52 JB |
114 | goto bad; |
115 | } | |
116 | ||
dfd068b0 MS |
117 | r = parse_features(&as, ti); |
118 | if (r) | |
119 | goto bad; | |
120 | ||
121 | if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) { | |
122 | ti->error = "Device lookup failed"; | |
3407ef52 JB |
123 | goto bad; |
124 | } | |
125 | ||
126 | ti->num_flush_requests = 1; | |
30e4171b | 127 | ti->num_discard_requests = 1; |
3407ef52 JB |
128 | ti->private = fc; |
129 | return 0; | |
130 | ||
131 | bad: | |
132 | kfree(fc); | |
133 | return -EINVAL; | |
134 | } | |
135 | ||
136 | static void flakey_dtr(struct dm_target *ti) | |
137 | { | |
138 | struct flakey_c *fc = ti->private; | |
139 | ||
140 | dm_put_device(ti, fc->dev); | |
141 | kfree(fc); | |
142 | } | |
143 | ||
144 | static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | |
145 | { | |
146 | struct flakey_c *fc = ti->private; | |
147 | ||
30e4171b | 148 | return fc->start + dm_target_offset(ti, bi_sector); |
3407ef52 JB |
149 | } |
150 | ||
151 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |
152 | { | |
153 | struct flakey_c *fc = ti->private; | |
154 | ||
155 | bio->bi_bdev = fc->dev->bdev; | |
156 | if (bio_sectors(bio)) | |
157 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | |
158 | } | |
159 | ||
160 | static int flakey_map(struct dm_target *ti, struct bio *bio, | |
161 | union map_info *map_context) | |
162 | { | |
163 | struct flakey_c *fc = ti->private; | |
164 | unsigned elapsed; | |
165 | ||
166 | /* Are we alive ? */ | |
167 | elapsed = (jiffies - fc->start_time) / HZ; | |
168 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | |
169 | return -EIO; | |
170 | ||
171 | flakey_map_bio(ti, bio); | |
172 | ||
173 | return DM_MAPIO_REMAPPED; | |
174 | } | |
175 | ||
176 | static int flakey_status(struct dm_target *ti, status_type_t type, | |
177 | char *result, unsigned int maxlen) | |
178 | { | |
179 | struct flakey_c *fc = ti->private; | |
180 | ||
181 | switch (type) { | |
182 | case STATUSTYPE_INFO: | |
183 | result[0] = '\0'; | |
184 | break; | |
185 | ||
186 | case STATUSTYPE_TABLE: | |
187 | snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | |
188 | (unsigned long long)fc->start, fc->up_interval, | |
189 | fc->down_interval); | |
190 | break; | |
191 | } | |
192 | return 0; | |
193 | } | |
194 | ||
195 | static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) | |
196 | { | |
197 | struct flakey_c *fc = ti->private; | |
198 | ||
199 | return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); | |
200 | } | |
201 | ||
202 | static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | |
203 | struct bio_vec *biovec, int max_size) | |
204 | { | |
205 | struct flakey_c *fc = ti->private; | |
206 | struct request_queue *q = bdev_get_queue(fc->dev->bdev); | |
207 | ||
208 | if (!q->merge_bvec_fn) | |
209 | return max_size; | |
210 | ||
211 | bvm->bi_bdev = fc->dev->bdev; | |
212 | bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); | |
213 | ||
214 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | |
215 | } | |
216 | ||
217 | static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) | |
218 | { | |
219 | struct flakey_c *fc = ti->private; | |
220 | ||
221 | return fn(ti, fc->dev, fc->start, ti->len, data); | |
222 | } | |
223 | ||
224 | static struct target_type flakey_target = { | |
225 | .name = "flakey", | |
dfd068b0 | 226 | .version = {1, 2, 0}, |
3407ef52 JB |
227 | .module = THIS_MODULE, |
228 | .ctr = flakey_ctr, | |
229 | .dtr = flakey_dtr, | |
230 | .map = flakey_map, | |
231 | .status = flakey_status, | |
232 | .ioctl = flakey_ioctl, | |
233 | .merge = flakey_merge, | |
234 | .iterate_devices = flakey_iterate_devices, | |
235 | }; | |
236 | ||
237 | static int __init dm_flakey_init(void) | |
238 | { | |
239 | int r = dm_register_target(&flakey_target); | |
240 | ||
241 | if (r < 0) | |
242 | DMERR("register failed %d", r); | |
243 | ||
244 | return r; | |
245 | } | |
246 | ||
247 | static void __exit dm_flakey_exit(void) | |
248 | { | |
249 | dm_unregister_target(&flakey_target); | |
250 | } | |
251 | ||
252 | /* Module hooks */ | |
253 | module_init(dm_flakey_init); | |
254 | module_exit(dm_flakey_exit); | |
255 | ||
256 | MODULE_DESCRIPTION(DM_NAME " flakey target"); | |
257 | MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); | |
258 | MODULE_LICENSE("GPL"); |