sys/dev/disk/dm: Remove dm_dev::dev_type
[dragonfly.git] / sys / dev / disk / dm / flakey / dm_target_flakey.c
bloba5eb06e6f8b262f30e52c6c05e3b46e8d80731e8
1 /*
2 * Copyright (c) 2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 #include <dev/disk/dm/dm.h>
37 MALLOC_DEFINE(M_DMFLAKEY, "dm_flakey", "Device Mapper Target Flakey");
39 /* dm_flakey never updates any field after initialization */
40 typedef struct target_flakey_config {
41 dm_pdev_t *pdev;
42 uint64_t offset;
43 int up_int;
44 int down_int;
45 int offset_time;
47 /* drop_writes feature */
48 int drop_writes;
50 /* corrupt_bio_byte feature */
51 unsigned int corrupt_buf_byte;
52 unsigned int corrupt_buf_rw;
53 unsigned int corrupt_buf_value;
54 unsigned int corrupt_buf_flags; /* for B_XXX flags */
55 } dm_target_flakey_config_t;
57 #define FLAKEY_CORRUPT_DIR(tfc) \
58 ((tfc)->corrupt_buf_rw == BUF_CMD_READ ? 'r' : 'w')
60 static int _init_features(dm_target_flakey_config_t*, int, char**);
61 static __inline void _submit(dm_target_flakey_config_t*, struct bio*);
62 static int _flakey_read(dm_target_flakey_config_t*, struct buf*);
63 static int _flakey_write(dm_target_flakey_config_t*, struct buf*);
64 static int _flakey_corrupt_buf(dm_target_flakey_config_t*, struct bio*);
66 static int
67 dm_target_flakey_init(dm_table_entry_t *table_en, int argc, char **argv)
69 dm_target_flakey_config_t *tfc;
70 dm_pdev_t *dmp;
71 int err;
73 dmdebug("Flakey target init: argc=%d\n", argc);
75 if (argc < 4) {
76 kprintf("Flakey target takes 4 or more args\n");
77 return EINVAL;
80 tfc = kmalloc(sizeof(*tfc), M_DMFLAKEY, M_WAITOK | M_ZERO);
81 if (tfc == NULL)
82 return ENOMEM;
84 if ((dmp = dm_pdev_insert(argv[0])) == NULL) {
85 err = ENOENT;
86 goto fail;
88 tfc->pdev = dmp;
89 tfc->offset = atoi64(argv[1]);
90 tfc->up_int = atoi64(argv[2]);
91 tfc->down_int = atoi64(argv[3]);
92 tfc->offset_time = ticks;
94 if ((tfc->up_int + tfc->down_int) == 0) {
95 kprintf("Sum of up/down interval is 0\n");
96 err = EINVAL;
97 goto fail;
100 if (tfc->up_int + tfc->down_int < tfc->up_int) {
101 kprintf("Interval time overflow\n");
102 err = EINVAL;
103 goto fail;
106 err = _init_features(tfc, argc - 4, argv + 4);
107 if (err)
108 goto fail;
110 dm_table_add_deps(table_en, dmp);
112 dm_table_init_target(table_en, tfc);
114 return 0;
115 fail:
116 kfree(tfc, M_DMFLAKEY);
117 return err;
120 static int
121 _init_features(dm_target_flakey_config_t *tfc, int argc, char **argv)
123 char *arg;
124 unsigned int value;
126 if (argc == 0)
127 return 0;
129 argc = atoi64(*argv++); /* # of args for features */
130 if (argc > 6) {
131 kprintf("Invalid # of feature args %d\n", argc);
132 return EINVAL;
135 while (argc) {
136 argc--;
137 arg = *argv++;
139 /* drop_writes */
140 if (strcmp(arg, "drop_writes") == 0) {
141 tfc->drop_writes = 1;
142 continue;
145 /* corrupt_bio_byte <Nth_byte> <direction> <value> <flags> */
146 if (strcmp(arg, "corrupt_bio_byte") == 0) {
147 if (argc < 4) {
148 kprintf("Invalid # of feature args %d for "
149 "corrupt_bio_byte\n", argc);
150 return EINVAL;
153 /* <Nth_byte> */
154 argc--;
155 value = atoi64(*argv++);
156 if (value < 1) {
157 kprintf("Invalid corrupt_bio_byte "
158 "<Nth_byte> arg %u\n", value);
159 return EINVAL;
161 tfc->corrupt_buf_byte = value;
163 /* <direction> */
164 argc--;
165 arg = *argv++;
166 if (strcmp(arg, "r") == 0) {
167 tfc->corrupt_buf_rw = BUF_CMD_READ;
168 } else if (strcmp(arg, "w") == 0) {
169 tfc->corrupt_buf_rw = BUF_CMD_WRITE;
170 } else {
171 kprintf("Invalid corrupt_bio_byte "
172 "<direction> arg %s\n", arg);
173 return EINVAL;
176 /* <value> */
177 argc--;
178 value = atoi64(*argv++);
179 if (value > 0xff) {
180 kprintf("Invalid corrupt_bio_byte "
181 "<value> arg %u\n", value);
182 return EINVAL;
184 tfc->corrupt_buf_value = value;
186 /* <flags> */
187 argc--;
188 tfc->corrupt_buf_flags = atoi64(*argv++);
190 continue;
193 kprintf("Unknown Flakey target feature %s\n", arg);
194 return EINVAL;
197 if (tfc->drop_writes && (tfc->corrupt_buf_rw == BUF_CMD_WRITE)) {
198 kprintf("Flakey target doesn't allow drop_writes feature "
199 "and corrupt_bio_byte feature with 'w' set\n");
200 return EINVAL;
203 return 0;
206 static int
207 dm_target_flakey_destroy(dm_table_entry_t *table_en)
209 dm_target_flakey_config_t *tfc;
211 tfc = table_en->target_config;
212 if (tfc == NULL)
213 return 0;
215 dm_pdev_decr(tfc->pdev);
217 kfree(tfc, M_DMFLAKEY);
219 return 0;
222 static int
223 dm_target_flakey_strategy(dm_table_entry_t *table_en, struct buf *bp)
225 dm_target_flakey_config_t *tfc;
226 int elapsed;
228 tfc = table_en->target_config;
230 elapsed = (ticks - tfc->offset_time) / hz;
231 if (elapsed % (tfc->up_int + tfc->down_int) >= tfc->up_int) {
232 switch (bp->b_cmd) {
233 case BUF_CMD_READ:
234 return _flakey_read(tfc, bp);
235 case BUF_CMD_WRITE:
236 return _flakey_write(tfc, bp);
237 default:
238 break;
242 /* This is what linear target does */
243 _submit(tfc, &bp->b_bio1);
245 return 0;
248 static __inline
249 void
250 _submit(dm_target_flakey_config_t *tfc, struct bio *bio)
252 bio->bio_offset += tfc->offset * DEV_BSIZE;
253 vn_strategy(tfc->pdev->pdev_vnode, bio);
256 static __inline
257 void
258 _flakey_eio_buf(struct buf *bp)
260 bp->b_error = EIO;
261 bp->b_resid = 0;
264 static void
265 _flakey_read_iodone(struct bio *bio)
267 struct bio *obio;
268 dm_target_flakey_config_t *tfc;
270 tfc = bio->bio_caller_info1.ptr;
271 obio = pop_bio(bio);
274 * Linux dm-flakey has changed its read behavior in 2016.
275 * This conditional is to sync with that change.
277 if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_READ)
278 _flakey_corrupt_buf(tfc, obio);
279 else if (!tfc->drop_writes)
280 _flakey_eio_buf(bio->bio_buf);
282 biodone(obio);
285 static int
286 _flakey_read(dm_target_flakey_config_t *tfc, struct buf *bp)
288 struct bio *bio = &bp->b_bio1;
289 struct bio *nbio;
292 * Linux dm-flakey has changed its read behavior in 2016.
293 * This conditional is to sync with that change.
295 if (!tfc->corrupt_buf_byte && !tfc->drop_writes) {
296 _flakey_eio_buf(bp);
297 biodone(bio);
298 return 0;
301 nbio = push_bio(bio);
302 nbio->bio_done = _flakey_read_iodone;
303 nbio->bio_caller_info1.ptr = tfc;
304 nbio->bio_offset = pop_bio(nbio)->bio_offset;
306 _submit(tfc, nbio);
308 return 0;
311 static int
312 _flakey_write(dm_target_flakey_config_t *tfc, struct buf *bp)
314 struct bio *bio = &bp->b_bio1;
316 if (tfc->drop_writes) {
317 dmdebug("bio=%p drop_writes offset=%ju\n",
318 bio, bio->bio_offset);
319 biodone(bio);
320 return 0;
323 if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_WRITE) {
324 _flakey_corrupt_buf(tfc, bio);
325 _submit(tfc, bio);
326 return 0;
329 /* Error all I/Os if neither of the above two */
330 _flakey_eio_buf(bp);
331 biodone(bio);
333 return 0;
336 static int
337 _flakey_corrupt_buf(dm_target_flakey_config_t *tfc, struct bio *bio)
339 struct buf *bp;
341 bp = bio->bio_buf;
343 if (bp->b_data == NULL)
344 return 1;
345 if (bp->b_error)
346 return 1; /* Don't corrupt on error */
347 if (bp->b_bcount < tfc->corrupt_buf_byte)
348 return 1;
349 if ((bp->b_flags & tfc->corrupt_buf_flags) != tfc->corrupt_buf_flags)
350 return 1;
352 bp->b_data[tfc->corrupt_buf_byte - 1] = tfc->corrupt_buf_value;
353 dmdebug("bio=%p dir=%c offset=%ju Nth=%u value=%u\n",
354 bio,
355 FLAKEY_CORRUPT_DIR(tfc),
356 bio->bio_offset,
357 tfc->corrupt_buf_byte,
358 tfc->corrupt_buf_value);
360 return 0;
363 static char *
364 dm_target_flakey_table(void *target_config)
366 dm_target_flakey_config_t *tfc;
367 char *params, *p;
368 int drop_writes;
370 tfc = target_config;
371 KKASSERT(tfc != NULL);
373 drop_writes = tfc->drop_writes;
375 params = dm_alloc_string(DM_MAX_PARAMS_SIZE);
376 p = params;
377 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, "%s %d %d %d %u ",
378 tfc->pdev->udev_name, tfc->offset_time,
379 tfc->up_int, tfc->down_int,
380 drop_writes + (tfc->corrupt_buf_byte > 0) * 5);
382 if (drop_writes)
383 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, "drop_writes ");
385 if (tfc->corrupt_buf_byte)
386 p += ksnprintf(p, DM_MAX_PARAMS_SIZE,
387 "corrupt_bio_byte %u %c %u %u ",
388 tfc->corrupt_buf_byte,
389 FLAKEY_CORRUPT_DIR(tfc),
390 tfc->corrupt_buf_value,
391 tfc->corrupt_buf_flags);
392 *(--p) = '\0';
394 return params;
397 static int
398 dmtf_mod_handler(module_t mod, int type, void *unused)
400 dm_target_t *dmt = NULL;
401 int err = 0;
403 switch(type) {
404 case MOD_LOAD:
405 if ((dmt = dm_target_lookup("flakey")) != NULL) {
406 dm_target_unbusy(dmt);
407 return EEXIST;
409 dmt = dm_target_alloc("flakey");
410 dmt->version[0] = 1;
411 dmt->version[1] = 0;
412 dmt->version[2] = 0;
413 strlcpy(dmt->name, "flakey", DM_MAX_TYPE_NAME);
414 dmt->init = &dm_target_flakey_init;
415 dmt->destroy = &dm_target_flakey_destroy;
416 dmt->strategy = &dm_target_flakey_strategy;
417 dmt->table = &dm_target_flakey_table;
419 err = dm_target_insert(dmt);
420 if (err == 0)
421 kprintf("dm_target_flakey: Successfully initialized\n");
422 break;
424 case MOD_UNLOAD:
425 err = dm_target_remove("flakey");
426 if (err == 0)
427 kprintf("dm_target_flakey: unloaded\n");
428 break;
431 return err;
434 DM_TARGET_MODULE(dm_target_flakey, dmtf_mod_handler);