dm flakey: add drop_writes
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / md / dm-flakey.c
blobe7c4c2a64f4bba0f88923d2e77a9f11bfd34c124
1 /*
2 * Copyright (C) 2003 Sistina Software (UK) Limited.
3 * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
5 * This file is released under the GPL.
6 */
8 #include <linux/device-mapper.h>
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>
16 #define DM_MSG_PREFIX "flakey"
19 * Flakey: Used for testing only, simulates intermittent,
20 * catastrophic device failure.
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;
31 enum feature_flag_bits {
32 DROP_WRITES
35 static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
36 struct dm_target *ti)
38 int r;
39 unsigned argc;
40 const char *arg_name;
42 static struct dm_arg _args[] = {
43 {0, 1, "Invalid number of feature args"},
46 /* No feature arguments supplied. */
47 if (!as->argc)
48 return 0;
50 r = dm_read_arg_group(_args, as, &argc, &ti->error);
51 if (r)
52 return -EINVAL;
54 while (argc && !r) {
55 arg_name = dm_shift_arg(as);
56 argc--;
59 * drop_writes
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;
67 continue;
70 ti->error = "Unrecognised flakey feature requested";
71 r = -EINVAL;
74 return r;
78 * Construct a flakey mapping:
79 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
81 * Feature args:
82 * [drop_writes]
84 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
86 static struct dm_arg _args[] = {
87 {0, UINT_MAX, "Invalid up interval"},
88 {0, UINT_MAX, "Invalid down interval"},
91 int r;
92 struct flakey_c *fc;
93 unsigned long long tmpll;
94 struct dm_arg_set as;
95 const char *devname;
97 as.argc = argc;
98 as.argv = argv;
100 if (argc < 4) {
101 ti->error = "Invalid argument count";
102 return -EINVAL;
105 fc = kzalloc(sizeof(*fc), GFP_KERNEL);
106 if (!fc) {
107 ti->error = "Cannot allocate linear context";
108 return -ENOMEM;
110 fc->start_time = jiffies;
112 devname = dm_shift_arg(&as);
114 if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
115 ti->error = "Invalid device sector";
116 goto bad;
118 fc->start = tmpll;
120 r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
121 if (r)
122 goto bad;
124 r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
125 if (r)
126 goto bad;
128 if (!(fc->up_interval + fc->down_interval)) {
129 ti->error = "Total (up + down) interval is zero";
130 goto bad;
133 if (fc->up_interval + fc->down_interval < fc->up_interval) {
134 ti->error = "Interval overflow";
135 goto bad;
138 r = parse_features(&as, fc, ti);
139 if (r)
140 goto bad;
142 if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
143 ti->error = "Device lookup failed";
144 goto bad;
147 ti->num_flush_requests = 1;
148 ti->num_discard_requests = 1;
149 ti->private = fc;
150 return 0;
152 bad:
153 kfree(fc);
154 return -EINVAL;
157 static void flakey_dtr(struct dm_target *ti)
159 struct flakey_c *fc = ti->private;
161 dm_put_device(ti, fc->dev);
162 kfree(fc);
165 static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
167 struct flakey_c *fc = ti->private;
169 return fc->start + dm_target_offset(ti, bi_sector);
172 static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
174 struct flakey_c *fc = ti->private;
176 bio->bi_bdev = fc->dev->bdev;
177 if (bio_sectors(bio))
178 bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
181 static int flakey_map(struct dm_target *ti, struct bio *bio,
182 union map_info *map_context)
184 struct flakey_c *fc = ti->private;
185 unsigned elapsed;
186 unsigned rw;
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);
194 * Drop writes. Map reads as normal.
196 if (test_bit(DROP_WRITES, &fc->flags)) {
197 if (rw == WRITE) {
198 bio_endio(bio, 0);
199 return DM_MAPIO_SUBMITTED;
201 goto map_bio;
205 * Default setting errors all I/O.
207 return -EIO;
210 map_bio:
211 flakey_map_bio(ti, bio);
213 return DM_MAPIO_REMAPPED;
216 static int flakey_status(struct dm_target *ti, status_type_t type,
217 char *result, unsigned int maxlen)
219 unsigned sz = 0;
220 struct flakey_c *fc = ti->private;
221 unsigned drop_writes;
223 switch (type) {
224 case STATUSTYPE_INFO:
225 result[0] = '\0';
226 break;
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);
233 drop_writes = test_bit(DROP_WRITES, &fc->flags);
234 DMEMIT("%u ", drop_writes);
235 if (drop_writes)
236 DMEMIT("drop_writes ");
237 break;
239 return 0;
242 static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg)
244 struct flakey_c *fc = ti->private;
246 return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg);
249 static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
250 struct bio_vec *biovec, int max_size)
252 struct flakey_c *fc = ti->private;
253 struct request_queue *q = bdev_get_queue(fc->dev->bdev);
255 if (!q->merge_bvec_fn)
256 return max_size;
258 bvm->bi_bdev = fc->dev->bdev;
259 bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector);
261 return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
264 static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
266 struct flakey_c *fc = ti->private;
268 return fn(ti, fc->dev, fc->start, ti->len, data);
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,
284 static int __init dm_flakey_init(void)
286 int r = dm_register_target(&flakey_target);
288 if (r < 0)
289 DMERR("register failed %d", r);
291 return r;
294 static void __exit dm_flakey_exit(void)
296 dm_unregister_target(&flakey_target);
299 /* Module hooks */
300 module_init(dm_flakey_init);
301 module_exit(dm_flakey_exit);
303 MODULE_DESCRIPTION(DM_NAME " flakey target");
304 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
305 MODULE_LICENSE("GPL");