blob: e7c4c2a64f4bba0f88923d2e77a9f11bfd34c124 [file] [log] [blame]
Josef Bacik3407ef52011-03-24 13:54:24 +00001/*
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 */
22struct flakey_c {
23 struct dm_dev *dev;
24 unsigned long start_time;
25 sector_t start;
26 unsigned up_interval;
27 unsigned down_interval;
Mike Snitzerb26f5e32011-08-02 12:32:05 +010028 unsigned long flags;
Josef Bacik3407ef52011-03-24 13:54:24 +000029};
30
Mike Snitzerb26f5e32011-08-02 12:32:05 +010031enum feature_flag_bits {
32 DROP_WRITES
33};
34
35static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
36 struct dm_target *ti)
Mike Snitzerdfd068b2011-08-02 12:32:05 +010037{
38 int r;
39 unsigned argc;
40 const char *arg_name;
41
42 static struct dm_arg _args[] = {
Mike Snitzerb26f5e32011-08-02 12:32:05 +010043 {0, 1, "Invalid number of feature args"},
Mike Snitzerdfd068b2011-08-02 12:32:05 +010044 };
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
Mike Snitzerb26f5e32011-08-02 12:32:05 +010058 /*
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
Mike Snitzerdfd068b2011-08-02 12:32:05 +010070 ti->error = "Unrecognised flakey feature requested";
71 r = -EINVAL;
72 }
73
74 return r;
75}
76
Josef Bacik3407ef52011-03-24 13:54:24 +000077/*
Mike Snitzerdfd068b2011-08-02 12:32:05 +010078 * Construct a flakey mapping:
79 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
Mike Snitzerb26f5e32011-08-02 12:32:05 +010080 *
81 * Feature args:
82 * [drop_writes]
Josef Bacik3407ef52011-03-24 13:54:24 +000083 */
84static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
85{
Mike Snitzerdfd068b2011-08-02 12:32:05 +010086 static struct dm_arg _args[] = {
87 {0, UINT_MAX, "Invalid up interval"},
88 {0, UINT_MAX, "Invalid down interval"},
89 };
Josef Bacik3407ef52011-03-24 13:54:24 +000090
Mike Snitzerdfd068b2011-08-02 12:32:05 +010091 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";
Josef Bacik3407ef52011-03-24 13:54:24 +0000102 return -EINVAL;
103 }
104
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100105 fc = kzalloc(sizeof(*fc), GFP_KERNEL);
Josef Bacik3407ef52011-03-24 13:54:24 +0000106 if (!fc) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100107 ti->error = "Cannot allocate linear context";
Josef Bacik3407ef52011-03-24 13:54:24 +0000108 return -ENOMEM;
109 }
110 fc->start_time = jiffies;
111
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100112 devname = dm_shift_arg(&as);
Josef Bacik3407ef52011-03-24 13:54:24 +0000113
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100114 if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
115 ti->error = "Invalid device sector";
Josef Bacik3407ef52011-03-24 13:54:24 +0000116 goto bad;
117 }
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100118 fc->start = tmpll;
Josef Bacik3407ef52011-03-24 13:54:24 +0000119
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100120 r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
121 if (r)
Josef Bacik3407ef52011-03-24 13:54:24 +0000122 goto bad;
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100123
124 r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
125 if (r)
126 goto bad;
Josef Bacik3407ef52011-03-24 13:54:24 +0000127
128 if (!(fc->up_interval + fc->down_interval)) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100129 ti->error = "Total (up + down) interval is zero";
Josef Bacik3407ef52011-03-24 13:54:24 +0000130 goto bad;
131 }
132
133 if (fc->up_interval + fc->down_interval < fc->up_interval) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100134 ti->error = "Interval overflow";
Josef Bacik3407ef52011-03-24 13:54:24 +0000135 goto bad;
136 }
137
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100138 r = parse_features(&as, fc, ti);
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100139 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";
Josef Bacik3407ef52011-03-24 13:54:24 +0000144 goto bad;
145 }
146
147 ti->num_flush_requests = 1;
Mike Snitzer30e41712011-08-02 12:32:05 +0100148 ti->num_discard_requests = 1;
Josef Bacik3407ef52011-03-24 13:54:24 +0000149 ti->private = fc;
150 return 0;
151
152bad:
153 kfree(fc);
154 return -EINVAL;
155}
156
157static 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
165static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
166{
167 struct flakey_c *fc = ti->private;
168
Mike Snitzer30e41712011-08-02 12:32:05 +0100169 return fc->start + dm_target_offset(ti, bi_sector);
Josef Bacik3407ef52011-03-24 13:54:24 +0000170}
171
172static 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
181static 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;
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100186 unsigned rw;
Josef Bacik3407ef52011-03-24 13:54:24 +0000187
188 /* Are we alive ? */
189 elapsed = (jiffies - fc->start_time) / HZ;
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100190 if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
191 rw = bio_data_dir(bio);
Josef Bacik3407ef52011-03-24 13:54:24 +0000192
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100193 /*
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
210map_bio:
Josef Bacik3407ef52011-03-24 13:54:24 +0000211 flakey_map_bio(ti, bio);
212
213 return DM_MAPIO_REMAPPED;
214}
215
216static int flakey_status(struct dm_target *ti, status_type_t type,
217 char *result, unsigned int maxlen)
218{
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100219 unsigned sz = 0;
Josef Bacik3407ef52011-03-24 13:54:24 +0000220 struct flakey_c *fc = ti->private;
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100221 unsigned drop_writes;
Josef Bacik3407ef52011-03-24 13:54:24 +0000222
223 switch (type) {
224 case STATUSTYPE_INFO:
225 result[0] = '\0';
226 break;
227
228 case STATUSTYPE_TABLE:
Mike Snitzerb26f5e32011-08-02 12:32:05 +0100229 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 ");
Josef Bacik3407ef52011-03-24 13:54:24 +0000237 break;
238 }
239 return 0;
240}
241
242static 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
249static 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
264static 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
271static struct target_type flakey_target = {
272 .name = "flakey",
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100273 .version = {1, 2, 0},
Josef Bacik3407ef52011-03-24 13:54:24 +0000274 .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
284static 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
294static void __exit dm_flakey_exit(void)
295{
296 dm_unregister_target(&flakey_target);
297}
298
299/* Module hooks */
300module_init(dm_flakey_init);
301module_exit(dm_flakey_exit);
302
303MODULE_DESCRIPTION(DM_NAME " flakey target");
304MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
305MODULE_LICENSE("GPL");