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; | |
82 | ti->private = fc; | |
83 | return 0; | |
84 | ||
85 | bad: | |
86 | kfree(fc); | |
87 | return -EINVAL; | |
88 | } | |
89 | ||
90 | static void flakey_dtr(struct dm_target *ti) | |
91 | { | |
92 | struct flakey_c *fc = ti->private; | |
93 | ||
94 | dm_put_device(ti, fc->dev); | |
95 | kfree(fc); | |
96 | } | |
97 | ||
98 | static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | |
99 | { | |
100 | struct flakey_c *fc = ti->private; | |
101 | ||
102 | return fc->start + (bi_sector - ti->begin); | |
103 | } | |
104 | ||
105 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |
106 | { | |
107 | struct flakey_c *fc = ti->private; | |
108 | ||
109 | bio->bi_bdev = fc->dev->bdev; | |
110 | if (bio_sectors(bio)) | |
111 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | |
112 | } | |
113 | ||
114 | static int flakey_map(struct dm_target *ti, struct bio *bio, | |
115 | union map_info *map_context) | |
116 | { | |
117 | struct flakey_c *fc = ti->private; | |
118 | unsigned elapsed; | |
119 | ||
120 | /* Are we alive ? */ | |
121 | elapsed = (jiffies - fc->start_time) / HZ; | |
122 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | |
123 | return -EIO; | |
124 | ||
125 | flakey_map_bio(ti, bio); | |
126 | ||
127 | return DM_MAPIO_REMAPPED; | |
128 | } | |
129 | ||
130 | static int flakey_status(struct dm_target *ti, status_type_t type, | |
131 | char *result, unsigned int maxlen) | |
132 | { | |
133 | struct flakey_c *fc = ti->private; | |
134 | ||
135 | switch (type) { | |
136 | case STATUSTYPE_INFO: | |
137 | result[0] = '\0'; | |
138 | break; | |
139 | ||
140 | case STATUSTYPE_TABLE: | |
141 | snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | |
142 | (unsigned long long)fc->start, fc->up_interval, | |
143 | fc->down_interval); | |
144 | break; | |
145 | } | |
146 | return 0; | |
147 | } | |
148 | ||
149 | static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) | |
150 | { | |
151 | struct flakey_c *fc = ti->private; | |
152 | ||
153 | return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); | |
154 | } | |
155 | ||
156 | static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, | |
157 | struct bio_vec *biovec, int max_size) | |
158 | { | |
159 | struct flakey_c *fc = ti->private; | |
160 | struct request_queue *q = bdev_get_queue(fc->dev->bdev); | |
161 | ||
162 | if (!q->merge_bvec_fn) | |
163 | return max_size; | |
164 | ||
165 | bvm->bi_bdev = fc->dev->bdev; | |
166 | bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); | |
167 | ||
168 | return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); | |
169 | } | |
170 | ||
171 | static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) | |
172 | { | |
173 | struct flakey_c *fc = ti->private; | |
174 | ||
175 | return fn(ti, fc->dev, fc->start, ti->len, data); | |
176 | } | |
177 | ||
178 | static struct target_type flakey_target = { | |
179 | .name = "flakey", | |
180 | .version = {1, 1, 0}, | |
181 | .module = THIS_MODULE, | |
182 | .ctr = flakey_ctr, | |
183 | .dtr = flakey_dtr, | |
184 | .map = flakey_map, | |
185 | .status = flakey_status, | |
186 | .ioctl = flakey_ioctl, | |
187 | .merge = flakey_merge, | |
188 | .iterate_devices = flakey_iterate_devices, | |
189 | }; | |
190 | ||
191 | static int __init dm_flakey_init(void) | |
192 | { | |
193 | int r = dm_register_target(&flakey_target); | |
194 | ||
195 | if (r < 0) | |
196 | DMERR("register failed %d", r); | |
197 | ||
198 | return r; | |
199 | } | |
200 | ||
201 | static void __exit dm_flakey_exit(void) | |
202 | { | |
203 | dm_unregister_target(&flakey_target); | |
204 | } | |
205 | ||
206 | /* Module hooks */ | |
207 | module_init(dm_flakey_init); | |
208 | module_exit(dm_flakey_exit); | |
209 | ||
210 | MODULE_DESCRIPTION(DM_NAME " flakey target"); | |
211 | MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); | |
212 | MODULE_LICENSE("GPL"); |