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