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
, 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
);