Commit | Line | Data |
---|---|---|
3407ef52 JB |
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 | /* | |
31 | * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval> | |
32 | */ | |
33 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |
34 | { | |
35 | struct flakey_c *fc; | |
36 | unsigned long long tmp; | |
37 | ||
38 | if (argc != 4) { | |
39 | ti->error = "dm-flakey: Invalid argument count"; | |
40 | return -EINVAL; | |
41 | } | |
42 | ||
43 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | |
44 | if (!fc) { | |
45 | ti->error = "dm-flakey: Cannot allocate linear context"; | |
46 | return -ENOMEM; | |
47 | } | |
48 | fc->start_time = jiffies; | |
49 | ||
50 | if (sscanf(argv[1], "%llu", &tmp) != 1) { | |
51 | ti->error = "dm-flakey: Invalid device sector"; | |
52 | goto bad; | |
53 | } | |
54 | fc->start = tmp; | |
55 | ||
56 | if (sscanf(argv[2], "%u", &fc->up_interval) != 1) { | |
57 | ti->error = "dm-flakey: Invalid up interval"; | |
58 | goto bad; | |
59 | } | |
60 | ||
61 | if (sscanf(argv[3], "%u", &fc->down_interval) != 1) { | |
62 | ti->error = "dm-flakey: Invalid down interval"; | |
63 | goto bad; | |
64 | } | |
65 | ||
66 | if (!(fc->up_interval + fc->down_interval)) { | |
67 | ti->error = "dm-flakey: Total (up + down) interval is zero"; | |
68 | goto bad; | |
69 | } | |
70 | ||
71 | if (fc->up_interval + fc->down_interval < fc->up_interval) { | |
72 | ti->error = "dm-flakey: Interval overflow"; | |
73 | goto bad; | |
74 | } | |
75 | ||
76 | if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) { | |
77 | ti->error = "dm-flakey: Device lookup failed"; | |
78 | goto bad; | |
79 | } | |
80 | ||
81 | ti->num_flush_requests = 1; | |
30e4171b | 82 | ti->num_discard_requests = 1; |
3407ef52 JB |
83 | ti->private = fc; |
84 | return 0; | |
85 | ||
86 | bad: | |
87 | kfree(fc); | |
88 | return -EINVAL; | |
89 | } | |
90 | ||
91 | static void flakey_dtr(struct dm_target *ti) | |
92 | { | |
93 | struct flakey_c *fc = ti->private; | |
94 | ||
95 | dm_put_device(ti, fc->dev); | |
96 | kfree(fc); | |
97 | } | |
98 | ||
99 | static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | |
100 | { | |
101 | struct flakey_c *fc = ti->private; | |
102 | ||
30e4171b | 103 | return fc->start + dm_target_offset(ti, bi_sector); |
3407ef52 JB |
104 | } |
105 | ||
106 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |
107 | { | |
108 | struct flakey_c *fc = ti->private; | |
109 | ||
110 | bio->bi_bdev = fc->dev->bdev; | |
111 | if (bio_sectors(bio)) | |
112 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | |
113 | } | |
114 | ||
115 | static int flakey_map(struct dm_target *ti, struct bio *bio, | |
116 | union map_info *map_context) | |
117 | { | |
118 | struct flakey_c *fc = ti->private; | |
119 | unsigned elapsed; | |
120 | ||
121 | /* Are we alive ? */ | |
122 | elapsed = (jiffies - fc->start_time) / HZ; | |
123 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | |
124 | return -EIO; | |
125 | ||
126 | flakey_map_bio(ti, bio); | |
127 | ||
128 | return DM_MAPIO_REMAPPED; | |
129 | } | |
130 | ||
131 | static int flakey_status(struct dm_target *ti, status_type_t type, | |
132 | char *result, unsigned int maxlen) | |
133 | { | |
134 | struct flakey_c *fc = ti->private; | |
135 | ||
136 | switch (type) { | |
137 | case STATUSTYPE_INFO: | |
138 | result[0] = '\0'; | |
139 | break; | |
140 | ||
141 | case STATUSTYPE_TABLE: | |
142 | snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | |
143 | (unsigned long long)fc->start, fc->up_interval, | |
144 | fc->down_interval); | |
145 | break; | |
146 | } | |
147 | return 0; | |
148 | } | |
149 | ||
150 | static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) | |
151 | { | |
152 | struct flakey_c *fc = ti->private; | |
153 | ||
154 | return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); | |
155 | } | |
156 | ||
157 | static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | |
158 | struct bio_vec *biovec, int max_size) | |
159 | { | |
160 | struct flakey_c *fc = ti->private; | |
161 | struct request_queue *q = bdev_get_queue(fc->dev->bdev); | |
162 | ||
163 | if (!q->merge_bvec_fn) | |
164 | return max_size; | |
165 | ||
166 | bvm->bi_bdev = fc->dev->bdev; | |
167 | bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); | |
168 | ||
169 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | |
170 | } | |
171 | ||
172 | static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) | |
173 | { | |
174 | struct flakey_c *fc = ti->private; | |
175 | ||
176 | return fn(ti, fc->dev, fc->start, ti->len, data); | |
177 | } | |
178 | ||
179 | static struct target_type flakey_target = { | |
180 | .name = "flakey", | |
181 | .version = {1, 1, 0}, | |
182 | .module = THIS_MODULE, | |
183 | .ctr = flakey_ctr, | |
184 | .dtr = flakey_dtr, | |
185 | .map = flakey_map, | |
186 | .status = flakey_status, | |
187 | .ioctl = flakey_ioctl, | |
188 | .merge = flakey_merge, | |
189 | .iterate_devices = flakey_iterate_devices, | |
190 | }; | |
191 | ||
192 | static int __init dm_flakey_init(void) | |
193 | { | |
194 | int r = dm_register_target(&flakey_target); | |
195 | ||
196 | if (r < 0) | |
197 | DMERR("register failed %d", r); | |
198 | ||
199 | return r; | |
200 | } | |
201 | ||
202 | static void __exit dm_flakey_exit(void) | |
203 | { | |
204 | dm_unregister_target(&flakey_target); | |
205 | } | |
206 | ||
207 | /* Module hooks */ | |
208 | module_init(dm_flakey_init); | |
209 | module_exit(dm_flakey_exit); | |
210 | ||
211 | MODULE_DESCRIPTION(DM_NAME " flakey target"); | |
212 | MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); | |
213 | MODULE_LICENSE("GPL"); |