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
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
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
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 {
47 /* drop_writes feature */
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*);
67 dm_target_flakey_init(dm_table_entry_t *table_en, int argc, char **argv)
69 dm_target_flakey_config_t *tfc;
73 dmdebug("Flakey target init: argc=%d\n", argc);
76 kprintf("Flakey target takes 4 or more args\n");
80 tfc = kmalloc(sizeof(*tfc), M_DMFLAKEY, M_WAITOK | M_ZERO);
84 if ((dmp = dm_pdev_insert(argv[0])) == NULL) {
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");
100 if (tfc->up_int + tfc->down_int < tfc->up_int) {
101 kprintf("Interval time overflow\n");
106 err = _init_features(tfc, argc - 4, argv + 4);
110 dm_table_add_deps(table_en, dmp);
112 dm_table_init_target(table_en, DM_FLAKEY_DEV, tfc);
116 kfree(tfc, M_DMFLAKEY);
121 _init_features(dm_target_flakey_config_t *tfc, int argc, char **argv)
129 argc = atoi64(*argv++); /* # of args for features */
131 kprintf("Invalid # of feature args %d\n", argc);
140 if (strcmp(arg, "drop_writes") == 0) {
141 tfc->drop_writes = 1;
145 /* corrupt_bio_byte <Nth_byte> <direction> <value> <flags> */
146 if (strcmp(arg, "corrupt_bio_byte") == 0) {
148 kprintf("Invalid # of feature args %d for "
149 "corrupt_bio_byte\n", argc);
155 value = atoi64(*argv++);
157 kprintf("Invalid corrupt_bio_byte "
158 "<Nth_byte> arg %u\n", value);
161 tfc->corrupt_buf_byte = value;
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;
171 kprintf("Invalid corrupt_bio_byte "
172 "<direction> arg %s\n", arg);
178 value = atoi64(*argv++);
180 kprintf("Invalid corrupt_bio_byte "
181 "<value> arg %u\n", value);
184 tfc->corrupt_buf_value = value;
188 tfc->corrupt_buf_flags = atoi64(*argv++);
193 kprintf("Unknown Flakey target feature %s\n", arg);
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");
207 dm_target_flakey_destroy(dm_table_entry_t *table_en)
209 dm_target_flakey_config_t *tfc;
211 tfc = table_en->target_config;
215 dm_pdev_decr(tfc->pdev);
217 kfree(tfc, M_DMFLAKEY);
223 dm_target_flakey_strategy(dm_table_entry_t *table_en, struct buf *bp)
225 dm_target_flakey_config_t *tfc;
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) {
234 return _flakey_read(tfc, bp);
236 return _flakey_write(tfc, bp);
242 /* This is what linear target does */
243 _submit(tfc, &bp->b_bio1);
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);
258 _flakey_eio_buf(struct buf *bp)
265 _flakey_read_iodone(struct bio *bio)
268 dm_target_flakey_config_t *tfc;
270 tfc = bio->bio_caller_info1.ptr;
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);
286 _flakey_read(dm_target_flakey_config_t *tfc, struct buf *bp)
288 struct bio *bio = &bp->b_bio1;
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) {
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;
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);
323 if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_WRITE) {
324 _flakey_corrupt_buf(tfc, bio);
329 /* Error all I/Os if neither of the above two */
337 _flakey_corrupt_buf(dm_target_flakey_config_t *tfc, struct bio *bio)
343 if (bp->b_data == NULL)
346 return 1; /* Don't corrupt on error */
347 if (bp->b_bcount < tfc->corrupt_buf_byte)
349 if ((bp->b_flags & tfc->corrupt_buf_flags) != tfc->corrupt_buf_flags)
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",
355 FLAKEY_CORRUPT_DIR(tfc),
357 tfc->corrupt_buf_byte,
358 tfc->corrupt_buf_value);
364 dm_target_flakey_table(void *target_config)
366 dm_target_flakey_config_t *tfc;
371 KKASSERT(tfc != NULL);
373 drop_writes = tfc->drop_writes;
375 params = dm_alloc_string(DM_MAX_PARAMS_SIZE);
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);
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);
398 dmtf_mod_handler(module_t mod, int type, void *unused)
400 dm_target_t *dmt = NULL;
405 if ((dmt = dm_target_lookup("flakey")) != NULL) {
406 dm_target_unbusy(dmt);
409 dmt = dm_target_alloc("flakey");
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);
421 kprintf("dm_target_flakey: Successfully initialized\n");
425 err = dm_target_remove("flakey");
427 kprintf("dm_target_flakey: unloaded\n");
434 DM_TARGET_MODULE(dm_target_flakey, dmtf_mod_handler);