Commit | Line | Data |
---|---|---|
3bd94003 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3407ef52 JB |
2 | /* |
3 | * Copyright (C) 2003 Sistina Software (UK) Limited. | |
a3998799 | 4 | * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved. |
3407ef52 JB |
5 | * |
6 | * This file is released under the GPL. | |
7 | */ | |
8 | ||
9 | #include <linux/device-mapper.h> | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/blkdev.h> | |
14 | #include <linux/bio.h> | |
15 | #include <linux/slab.h> | |
16 | ||
17 | #define DM_MSG_PREFIX "flakey" | |
18 | ||
a3998799 | 19 | #define all_corrupt_bio_flags_match(bio, fc) \ |
1eff9d32 | 20 | (((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags) |
a3998799 | 21 | |
3407ef52 JB |
22 | /* |
23 | * Flakey: Used for testing only, simulates intermittent, | |
24 | * catastrophic device failure. | |
25 | */ | |
26 | struct flakey_c { | |
27 | struct dm_dev *dev; | |
28 | unsigned long start_time; | |
29 | sector_t start; | |
86a3238c HM |
30 | unsigned int up_interval; |
31 | unsigned int down_interval; | |
b26f5e3d | 32 | unsigned long flags; |
86a3238c HM |
33 | unsigned int corrupt_bio_byte; |
34 | unsigned int corrupt_bio_rw; | |
35 | unsigned int corrupt_bio_value; | |
eff17e51 | 36 | blk_opf_t corrupt_bio_flags; |
3407ef52 JB |
37 | }; |
38 | ||
b26f5e3d | 39 | enum feature_flag_bits { |
aa7d7bc9 | 40 | ERROR_READS, |
ef548c55 MS |
41 | DROP_WRITES, |
42 | ERROR_WRITES | |
b26f5e3d MS |
43 | }; |
44 | ||
c7cfdf59 MP |
45 | struct per_bio_data { |
46 | bool bio_submitted; | |
47 | }; | |
48 | ||
b26f5e3d MS |
49 | static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, |
50 | struct dm_target *ti) | |
dfd068b0 MS |
51 | { |
52 | int r; | |
86a3238c | 53 | unsigned int argc; |
dfd068b0 MS |
54 | const char *arg_name; |
55 | ||
5916a22b | 56 | static const struct dm_arg _args[] = { |
aa7d7bc9 | 57 | {0, 7, "Invalid number of feature args"}, |
a3998799 MS |
58 | {1, UINT_MAX, "Invalid corrupt bio byte"}, |
59 | {0, 255, "Invalid corrupt value to write into bio byte (0-255)"}, | |
60 | {0, UINT_MAX, "Invalid corrupt bio flags mask"}, | |
dfd068b0 MS |
61 | }; |
62 | ||
63 | /* No feature arguments supplied. */ | |
64 | if (!as->argc) | |
65 | return 0; | |
66 | ||
67 | r = dm_read_arg_group(_args, as, &argc, &ti->error); | |
68 | if (r) | |
a3998799 | 69 | return r; |
dfd068b0 | 70 | |
a3998799 | 71 | while (argc) { |
dfd068b0 MS |
72 | arg_name = dm_shift_arg(as); |
73 | argc--; | |
74 | ||
7690e253 GR |
75 | if (!arg_name) { |
76 | ti->error = "Insufficient feature arguments"; | |
77 | return -EINVAL; | |
78 | } | |
79 | ||
aa7d7bc9 MP |
80 | /* |
81 | * error_reads | |
82 | */ | |
83 | if (!strcasecmp(arg_name, "error_reads")) { | |
84 | if (test_and_set_bit(ERROR_READS, &fc->flags)) { | |
85 | ti->error = "Feature error_reads duplicated"; | |
86 | return -EINVAL; | |
87 | } | |
88 | continue; | |
89 | } | |
90 | ||
b26f5e3d MS |
91 | /* |
92 | * drop_writes | |
93 | */ | |
94 | if (!strcasecmp(arg_name, "drop_writes")) { | |
95 | if (test_and_set_bit(DROP_WRITES, &fc->flags)) { | |
96 | ti->error = "Feature drop_writes duplicated"; | |
97 | return -EINVAL; | |
ef548c55 MS |
98 | } else if (test_bit(ERROR_WRITES, &fc->flags)) { |
99 | ti->error = "Feature drop_writes conflicts with feature error_writes"; | |
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | continue; | |
104 | } | |
105 | ||
106 | /* | |
107 | * error_writes | |
108 | */ | |
109 | if (!strcasecmp(arg_name, "error_writes")) { | |
110 | if (test_and_set_bit(ERROR_WRITES, &fc->flags)) { | |
111 | ti->error = "Feature error_writes duplicated"; | |
112 | return -EINVAL; | |
113 | ||
114 | } else if (test_bit(DROP_WRITES, &fc->flags)) { | |
115 | ti->error = "Feature error_writes conflicts with feature drop_writes"; | |
116 | return -EINVAL; | |
b26f5e3d MS |
117 | } |
118 | ||
119 | continue; | |
120 | } | |
121 | ||
a3998799 MS |
122 | /* |
123 | * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags> | |
124 | */ | |
125 | if (!strcasecmp(arg_name, "corrupt_bio_byte")) { | |
68e58a29 | 126 | if (!argc) { |
a3998799 | 127 | ti->error = "Feature corrupt_bio_byte requires parameters"; |
68e58a29 MS |
128 | return -EINVAL; |
129 | } | |
a3998799 MS |
130 | |
131 | r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error); | |
132 | if (r) | |
133 | return r; | |
134 | argc--; | |
135 | ||
136 | /* | |
137 | * Direction r or w? | |
138 | */ | |
139 | arg_name = dm_shift_arg(as); | |
98dba02d | 140 | if (arg_name && !strcasecmp(arg_name, "w")) |
a3998799 | 141 | fc->corrupt_bio_rw = WRITE; |
98dba02d | 142 | else if (arg_name && !strcasecmp(arg_name, "r")) |
a3998799 MS |
143 | fc->corrupt_bio_rw = READ; |
144 | else { | |
145 | ti->error = "Invalid corrupt bio direction (r or w)"; | |
146 | return -EINVAL; | |
147 | } | |
148 | argc--; | |
149 | ||
150 | /* | |
151 | * Value of byte (0-255) to write in place of correct one. | |
152 | */ | |
153 | r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error); | |
154 | if (r) | |
155 | return r; | |
156 | argc--; | |
157 | ||
158 | /* | |
159 | * Only corrupt bios with these flags set. | |
160 | */ | |
eff17e51 BVA |
161 | BUILD_BUG_ON(sizeof(fc->corrupt_bio_flags) != |
162 | sizeof(unsigned int)); | |
163 | r = dm_read_arg(_args + 3, as, | |
86a3238c | 164 | (__force unsigned int *)&fc->corrupt_bio_flags, |
eff17e51 | 165 | &ti->error); |
a3998799 MS |
166 | if (r) |
167 | return r; | |
168 | argc--; | |
169 | ||
170 | continue; | |
171 | } | |
172 | ||
dfd068b0 | 173 | ti->error = "Unrecognised flakey feature requested"; |
a3998799 | 174 | return -EINVAL; |
dfd068b0 MS |
175 | } |
176 | ||
a3998799 MS |
177 | if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) { |
178 | ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set"; | |
179 | return -EINVAL; | |
ef548c55 MS |
180 | |
181 | } else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) { | |
182 | ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set"; | |
183 | return -EINVAL; | |
a3998799 MS |
184 | } |
185 | ||
aa7d7bc9 MP |
186 | if (!fc->corrupt_bio_byte && !test_bit(ERROR_READS, &fc->flags) && |
187 | !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags)) { | |
188 | set_bit(ERROR_WRITES, &fc->flags); | |
189 | set_bit(ERROR_READS, &fc->flags); | |
190 | } | |
191 | ||
a3998799 | 192 | return 0; |
dfd068b0 MS |
193 | } |
194 | ||
3407ef52 | 195 | /* |
dfd068b0 MS |
196 | * Construct a flakey mapping: |
197 | * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*] | |
b26f5e3d MS |
198 | * |
199 | * Feature args: | |
200 | * [drop_writes] | |
a3998799 MS |
201 | * [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>] |
202 | * | |
203 | * Nth_byte starts from 1 for the first byte. | |
204 | * Direction is r for READ or w for WRITE. | |
205 | * bio_flags is ignored if 0. | |
3407ef52 JB |
206 | */ |
207 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |
208 | { | |
5916a22b | 209 | static const struct dm_arg _args[] = { |
dfd068b0 MS |
210 | {0, UINT_MAX, "Invalid up interval"}, |
211 | {0, UINT_MAX, "Invalid down interval"}, | |
212 | }; | |
213 | ||
214 | int r; | |
3407ef52 | 215 | struct flakey_c *fc; |
dfd068b0 MS |
216 | unsigned long long tmpll; |
217 | struct dm_arg_set as; | |
218 | const char *devname; | |
31998ef1 | 219 | char dummy; |
3407ef52 | 220 | |
dfd068b0 MS |
221 | as.argc = argc; |
222 | as.argv = argv; | |
223 | ||
224 | if (argc < 4) { | |
225 | ti->error = "Invalid argument count"; | |
3407ef52 JB |
226 | return -EINVAL; |
227 | } | |
228 | ||
b26f5e3d | 229 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); |
3407ef52 | 230 | if (!fc) { |
75e3a0f5 | 231 | ti->error = "Cannot allocate context"; |
3407ef52 JB |
232 | return -ENOMEM; |
233 | } | |
234 | fc->start_time = jiffies; | |
235 | ||
dfd068b0 MS |
236 | devname = dm_shift_arg(&as); |
237 | ||
e80d1c80 | 238 | r = -EINVAL; |
ef87bfc2 | 239 | if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) { |
dfd068b0 | 240 | ti->error = "Invalid device sector"; |
3407ef52 JB |
241 | goto bad; |
242 | } | |
dfd068b0 | 243 | fc->start = tmpll; |
3407ef52 | 244 | |
dfd068b0 MS |
245 | r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error); |
246 | if (r) | |
3407ef52 | 247 | goto bad; |
3407ef52 | 248 | |
dfd068b0 MS |
249 | r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error); |
250 | if (r) | |
3407ef52 | 251 | goto bad; |
3407ef52 JB |
252 | |
253 | if (!(fc->up_interval + fc->down_interval)) { | |
dfd068b0 | 254 | ti->error = "Total (up + down) interval is zero"; |
bff7e067 | 255 | r = -EINVAL; |
3407ef52 JB |
256 | goto bad; |
257 | } | |
258 | ||
259 | if (fc->up_interval + fc->down_interval < fc->up_interval) { | |
dfd068b0 | 260 | ti->error = "Interval overflow"; |
bff7e067 | 261 | r = -EINVAL; |
3407ef52 JB |
262 | goto bad; |
263 | } | |
264 | ||
b26f5e3d | 265 | r = parse_features(&as, fc, ti); |
dfd068b0 MS |
266 | if (r) |
267 | goto bad; | |
268 | ||
e80d1c80 VG |
269 | r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev); |
270 | if (r) { | |
dfd068b0 | 271 | ti->error = "Device lookup failed"; |
3407ef52 JB |
272 | goto bad; |
273 | } | |
274 | ||
55a62eef AK |
275 | ti->num_flush_bios = 1; |
276 | ti->num_discard_bios = 1; | |
30187e1d | 277 | ti->per_io_data_size = sizeof(struct per_bio_data); |
3407ef52 JB |
278 | ti->private = fc; |
279 | return 0; | |
280 | ||
281 | bad: | |
282 | kfree(fc); | |
e80d1c80 | 283 | return r; |
3407ef52 JB |
284 | } |
285 | ||
286 | static void flakey_dtr(struct dm_target *ti) | |
287 | { | |
288 | struct flakey_c *fc = ti->private; | |
289 | ||
290 | dm_put_device(ti, fc->dev); | |
291 | kfree(fc); | |
292 | } | |
293 | ||
294 | static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | |
295 | { | |
296 | struct flakey_c *fc = ti->private; | |
297 | ||
30e4171b | 298 | return fc->start + dm_target_offset(ti, bi_sector); |
3407ef52 JB |
299 | } |
300 | ||
301 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |
302 | { | |
303 | struct flakey_c *fc = ti->private; | |
304 | ||
74d46992 | 305 | bio_set_dev(bio, fc->dev->bdev); |
e86f2b00 | 306 | bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector); |
3407ef52 JB |
307 | } |
308 | ||
a3998799 MS |
309 | static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) |
310 | { | |
a00f5276 ST |
311 | unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1; |
312 | ||
313 | struct bvec_iter iter; | |
314 | struct bio_vec bvec; | |
315 | ||
316 | if (!bio_has_data(bio)) | |
317 | return; | |
a3998799 MS |
318 | |
319 | /* | |
a00f5276 ST |
320 | * Overwrite the Nth byte of the bio's data, on whichever page |
321 | * it falls. | |
a3998799 | 322 | */ |
a00f5276 ST |
323 | bio_for_each_segment(bvec, bio, iter) { |
324 | if (bio_iter_len(bio, iter) > corrupt_bio_byte) { | |
f50714b5 MP |
325 | char *segment; |
326 | struct page *page = bio_iter_page(bio, iter); | |
327 | if (unlikely(page == ZERO_PAGE(0))) | |
328 | break; | |
8eb29c4f | 329 | segment = bvec_kmap_local(&bvec); |
a00f5276 | 330 | segment[corrupt_bio_byte] = fc->corrupt_bio_value; |
8eb29c4f | 331 | kunmap_local(segment); |
a00f5276 ST |
332 | DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " |
333 | "(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n", | |
334 | bio, fc->corrupt_bio_value, fc->corrupt_bio_byte, | |
335 | (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf, | |
336 | (unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size); | |
337 | break; | |
338 | } | |
339 | corrupt_bio_byte -= bio_iter_len(bio, iter); | |
a3998799 MS |
340 | } |
341 | } | |
342 | ||
7de3ee57 | 343 | static int flakey_map(struct dm_target *ti, struct bio *bio) |
3407ef52 JB |
344 | { |
345 | struct flakey_c *fc = ti->private; | |
86a3238c | 346 | unsigned int elapsed; |
c7cfdf59 | 347 | struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data)); |
0ef0b471 | 348 | |
c7cfdf59 | 349 | pb->bio_submitted = false; |
3407ef52 | 350 | |
2e2d6f7e | 351 | if (op_is_zone_mgmt(bio_op(bio))) |
124c4454 DLM |
352 | goto map_bio; |
353 | ||
3407ef52 JB |
354 | /* Are we alive ? */ |
355 | elapsed = (jiffies - fc->start_time) / HZ; | |
b26f5e3d | 356 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { |
a3998799 MS |
357 | /* |
358 | * Flag this bio as submitted while down. | |
359 | */ | |
c7cfdf59 | 360 | pb->bio_submitted = true; |
a3998799 MS |
361 | |
362 | /* | |
ef548c55 | 363 | * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set. |
299f6230 | 364 | * Otherwise, flakey_end_io() will decide if the reads should be modified. |
a3998799 | 365 | */ |
99f3c90d | 366 | if (bio_data_dir(bio) == READ) { |
aa7d7bc9 | 367 | if (test_bit(ERROR_READS, &fc->flags)) |
846785e6 | 368 | return DM_MAPIO_KILL; |
299f6230 | 369 | goto map_bio; |
99f3c90d | 370 | } |
b26f5e3d MS |
371 | |
372 | /* | |
ef548c55 | 373 | * Drop or error writes? |
b26f5e3d MS |
374 | */ |
375 | if (test_bit(DROP_WRITES, &fc->flags)) { | |
4246a0b6 | 376 | bio_endio(bio); |
a3998799 | 377 | return DM_MAPIO_SUBMITTED; |
03b18887 | 378 | } else if (test_bit(ERROR_WRITES, &fc->flags)) { |
ef548c55 MS |
379 | bio_io_error(bio); |
380 | return DM_MAPIO_SUBMITTED; | |
381 | } | |
a3998799 MS |
382 | |
383 | /* | |
384 | * Corrupt matching writes. | |
385 | */ | |
aa56b9b7 MP |
386 | if (fc->corrupt_bio_byte) { |
387 | if (fc->corrupt_bio_rw == WRITE) { | |
388 | if (all_corrupt_bio_flags_match(bio, fc)) | |
389 | corrupt_bio_data(bio, fc); | |
390 | } | |
b26f5e3d MS |
391 | goto map_bio; |
392 | } | |
b26f5e3d | 393 | } |
3407ef52 | 394 | |
b26f5e3d | 395 | map_bio: |
3407ef52 JB |
396 | flakey_map_bio(ti, bio); |
397 | ||
398 | return DM_MAPIO_REMAPPED; | |
399 | } | |
400 | ||
4e4cbee9 | 401 | static int flakey_end_io(struct dm_target *ti, struct bio *bio, |
124c4454 | 402 | blk_status_t *error) |
a3998799 MS |
403 | { |
404 | struct flakey_c *fc = ti->private; | |
c7cfdf59 | 405 | struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data)); |
a3998799 | 406 | |
2e2d6f7e | 407 | if (op_is_zone_mgmt(bio_op(bio))) |
124c4454 DLM |
408 | return DM_ENDIO_DONE; |
409 | ||
1be56909 | 410 | if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) { |
aa56b9b7 MP |
411 | if (fc->corrupt_bio_byte) { |
412 | if ((fc->corrupt_bio_rw == READ) && | |
413 | all_corrupt_bio_flags_match(bio, fc)) { | |
414 | /* | |
415 | * Corrupt successful matching READs while in down state. | |
416 | */ | |
417 | corrupt_bio_data(bio, fc); | |
418 | } | |
aa7d7bc9 MP |
419 | } |
420 | if (test_bit(ERROR_READS, &fc->flags)) { | |
299f6230 MS |
421 | /* |
422 | * Error read during the down_interval if drop_writes | |
ef548c55 | 423 | * and error_writes were not configured. |
299f6230 | 424 | */ |
4e4cbee9 | 425 | *error = BLK_STS_IOERR; |
299f6230 | 426 | } |
99f3c90d | 427 | } |
a3998799 | 428 | |
1be56909 | 429 | return DM_ENDIO_DONE; |
a3998799 MS |
430 | } |
431 | ||
fd7c092e | 432 | static void flakey_status(struct dm_target *ti, status_type_t type, |
86a3238c | 433 | unsigned int status_flags, char *result, unsigned int maxlen) |
3407ef52 | 434 | { |
86a3238c | 435 | unsigned int sz = 0; |
3407ef52 | 436 | struct flakey_c *fc = ti->private; |
aa7d7bc9 | 437 | unsigned int error_reads, drop_writes, error_writes; |
3407ef52 JB |
438 | |
439 | switch (type) { | |
440 | case STATUSTYPE_INFO: | |
441 | result[0] = '\0'; | |
442 | break; | |
443 | ||
444 | case STATUSTYPE_TABLE: | |
e3675dc1 | 445 | DMEMIT("%s %llu %u %u", fc->dev->name, |
b26f5e3d MS |
446 | (unsigned long long)fc->start, fc->up_interval, |
447 | fc->down_interval); | |
448 | ||
aa7d7bc9 | 449 | error_reads = test_bit(ERROR_READS, &fc->flags); |
b26f5e3d | 450 | drop_writes = test_bit(DROP_WRITES, &fc->flags); |
ef548c55 | 451 | error_writes = test_bit(ERROR_WRITES, &fc->flags); |
aa7d7bc9 | 452 | DMEMIT(" %u", error_reads + drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5); |
a3998799 | 453 | |
aa7d7bc9 MP |
454 | if (error_reads) |
455 | DMEMIT(" error_reads"); | |
b26f5e3d | 456 | if (drop_writes) |
e3675dc1 | 457 | DMEMIT(" drop_writes"); |
ef548c55 | 458 | else if (error_writes) |
e3675dc1 | 459 | DMEMIT(" error_writes"); |
a3998799 MS |
460 | |
461 | if (fc->corrupt_bio_byte) | |
e3675dc1 | 462 | DMEMIT(" corrupt_bio_byte %u %c %u %u", |
a3998799 MS |
463 | fc->corrupt_bio_byte, |
464 | (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r', | |
465 | fc->corrupt_bio_value, fc->corrupt_bio_flags); | |
466 | ||
3407ef52 | 467 | break; |
8ec45662 TS |
468 | |
469 | case STATUSTYPE_IMA: | |
470 | result[0] = '\0'; | |
471 | break; | |
3407ef52 | 472 | } |
3407ef52 JB |
473 | } |
474 | ||
5bd5e8d8 | 475 | static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) |
3407ef52 JB |
476 | { |
477 | struct flakey_c *fc = ti->private; | |
e56f81e0 CH |
478 | |
479 | *bdev = fc->dev->bdev; | |
3407ef52 | 480 | |
ec8013be PB |
481 | /* |
482 | * Only pass ioctls through if the device sizes match exactly. | |
483 | */ | |
6dcbb52c | 484 | if (fc->start || ti->len != bdev_nr_sectors((*bdev))) |
e56f81e0 CH |
485 | return 1; |
486 | return 0; | |
3407ef52 JB |
487 | } |
488 | ||
e76239a3 | 489 | #ifdef CONFIG_BLK_DEV_ZONED |
d4100351 CH |
490 | static int flakey_report_zones(struct dm_target *ti, |
491 | struct dm_report_zones_args *args, unsigned int nr_zones) | |
e76239a3 CH |
492 | { |
493 | struct flakey_c *fc = ti->private; | |
e76239a3 | 494 | |
912e8875 DLM |
495 | return dm_report_zones(fc->dev->bdev, fc->start, |
496 | flakey_map_sector(ti, args->next_sector), | |
497 | args, nr_zones); | |
e76239a3 | 498 | } |
e3290b94 MS |
499 | #else |
500 | #define flakey_report_zones NULL | |
e76239a3 CH |
501 | #endif |
502 | ||
3407ef52 JB |
503 | static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) |
504 | { | |
505 | struct flakey_c *fc = ti->private; | |
506 | ||
507 | return fn(ti, fc->dev, fc->start, ti->len, data); | |
508 | } | |
509 | ||
510 | static struct target_type flakey_target = { | |
511 | .name = "flakey", | |
124c4454 | 512 | .version = {1, 5, 0}, |
3db564b4 | 513 | .features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO, |
e76239a3 | 514 | .report_zones = flakey_report_zones, |
3407ef52 JB |
515 | .module = THIS_MODULE, |
516 | .ctr = flakey_ctr, | |
517 | .dtr = flakey_dtr, | |
518 | .map = flakey_map, | |
a3998799 | 519 | .end_io = flakey_end_io, |
3407ef52 | 520 | .status = flakey_status, |
e56f81e0 | 521 | .prepare_ioctl = flakey_prepare_ioctl, |
3407ef52 JB |
522 | .iterate_devices = flakey_iterate_devices, |
523 | }; | |
3664ff82 | 524 | module_dm(flakey); |
3407ef52 JB |
525 | |
526 | MODULE_DESCRIPTION(DM_NAME " flakey target"); | |
527 | MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); | |
528 | MODULE_LICENSE("GPL"); |