blob: dd963dc3ca08e403f1ebe178c7d62860202b6d23 [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;
28};
29
Mike Snitzerdfd068b2011-08-02 12:32:05 +010030static 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
Josef Bacik3407ef52011-03-24 13:54:24 +000059/*
Mike Snitzerdfd068b2011-08-02 12:32:05 +010060 * Construct a flakey mapping:
61 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
Josef Bacik3407ef52011-03-24 13:54:24 +000062 */
63static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
64{
Mike Snitzerdfd068b2011-08-02 12:32:05 +010065 static struct dm_arg _args[] = {
66 {0, UINT_MAX, "Invalid up interval"},
67 {0, UINT_MAX, "Invalid down interval"},
68 };
Josef Bacik3407ef52011-03-24 13:54:24 +000069
Mike Snitzerdfd068b2011-08-02 12:32:05 +010070 int r;
71 struct flakey_c *fc;
72 unsigned long long tmpll;
73 struct dm_arg_set as;
74 const char *devname;
75
76 as.argc = argc;
77 as.argv = argv;
78
79 if (argc < 4) {
80 ti->error = "Invalid argument count";
Josef Bacik3407ef52011-03-24 13:54:24 +000081 return -EINVAL;
82 }
83
84 fc = kmalloc(sizeof(*fc), GFP_KERNEL);
85 if (!fc) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +010086 ti->error = "Cannot allocate linear context";
Josef Bacik3407ef52011-03-24 13:54:24 +000087 return -ENOMEM;
88 }
89 fc->start_time = jiffies;
90
Mike Snitzerdfd068b2011-08-02 12:32:05 +010091 devname = dm_shift_arg(&as);
Josef Bacik3407ef52011-03-24 13:54:24 +000092
Mike Snitzerdfd068b2011-08-02 12:32:05 +010093 if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
94 ti->error = "Invalid device sector";
Josef Bacik3407ef52011-03-24 13:54:24 +000095 goto bad;
96 }
Mike Snitzerdfd068b2011-08-02 12:32:05 +010097 fc->start = tmpll;
Josef Bacik3407ef52011-03-24 13:54:24 +000098
Mike Snitzerdfd068b2011-08-02 12:32:05 +010099 r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
100 if (r)
Josef Bacik3407ef52011-03-24 13:54:24 +0000101 goto bad;
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100102
103 r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
104 if (r)
105 goto bad;
Josef Bacik3407ef52011-03-24 13:54:24 +0000106
107 if (!(fc->up_interval + fc->down_interval)) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100108 ti->error = "Total (up + down) interval is zero";
Josef Bacik3407ef52011-03-24 13:54:24 +0000109 goto bad;
110 }
111
112 if (fc->up_interval + fc->down_interval < fc->up_interval) {
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100113 ti->error = "Interval overflow";
Josef Bacik3407ef52011-03-24 13:54:24 +0000114 goto bad;
115 }
116
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100117 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";
Josef Bacik3407ef52011-03-24 13:54:24 +0000123 goto bad;
124 }
125
126 ti->num_flush_requests = 1;
Mike Snitzer30e41712011-08-02 12:32:05 +0100127 ti->num_discard_requests = 1;
Josef Bacik3407ef52011-03-24 13:54:24 +0000128 ti->private = fc;
129 return 0;
130
131bad:
132 kfree(fc);
133 return -EINVAL;
134}
135
136static 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
144static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
145{
146 struct flakey_c *fc = ti->private;
147
Mike Snitzer30e41712011-08-02 12:32:05 +0100148 return fc->start + dm_target_offset(ti, bi_sector);
Josef Bacik3407ef52011-03-24 13:54:24 +0000149}
150
151static 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
160static 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
176static 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
195static 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
202static 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
217static 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
224static struct target_type flakey_target = {
225 .name = "flakey",
Mike Snitzerdfd068b2011-08-02 12:32:05 +0100226 .version = {1, 2, 0},
Josef Bacik3407ef52011-03-24 13:54:24 +0000227 .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
237static 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
247static void __exit dm_flakey_exit(void)
248{
249 dm_unregister_target(&flakey_target);
250}
251
252/* Module hooks */
253module_init(dm_flakey_init);
254module_exit(dm_flakey_exit);
255
256MODULE_DESCRIPTION(DM_NAME " flakey target");
257MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
258MODULE_LICENSE("GPL");