]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/md/dm-flakey.c
dm flakey: support feature args
[mirror_ubuntu-hirsute-kernel.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 };
29
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
59 /*
60 * Construct a flakey mapping:
61 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
62 */
63 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
64 {
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;
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";
81 return -EINVAL;
82 }
83
84 fc = kmalloc(sizeof(*fc), GFP_KERNEL);
85 if (!fc) {
86 ti->error = "Cannot allocate linear context";
87 return -ENOMEM;
88 }
89 fc->start_time = jiffies;
90
91 devname = dm_shift_arg(&as);
92
93 if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
94 ti->error = "Invalid device sector";
95 goto bad;
96 }
97 fc->start = tmpll;
98
99 r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
100 if (r)
101 goto bad;
102
103 r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
104 if (r)
105 goto bad;
106
107 if (!(fc->up_interval + fc->down_interval)) {
108 ti->error = "Total (up + down) interval is zero";
109 goto bad;
110 }
111
112 if (fc->up_interval + fc->down_interval < fc->up_interval) {
113 ti->error = "Interval overflow";
114 goto bad;
115 }
116
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";
123 goto bad;
124 }
125
126 ti->num_flush_requests = 1;
127 ti->num_discard_requests = 1;
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
148 return fc->start + dm_target_offset(ti, bi_sector);
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",
226 .version = {1, 2, 0},
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");