1 /* 2 * Copyright (c) 2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Tomohiro Kusumi <tkusumi@netbsd.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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. 20 * 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. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/malloc.h> 37 #include <dev/disk/dm/dm.h> 38 39 MALLOC_DEFINE(M_DMFLAKEY, "dm_flakey", "Device Mapper Target Flakey"); 40 41 /* dm_flakey never updates any field after initialization */ 42 typedef struct target_flakey_config { 43 dm_pdev_t *pdev; 44 uint64_t offset; 45 int up_int; 46 int down_int; 47 int offset_time; 48 49 /* drop_writes feature */ 50 int drop_writes; 51 52 /* corrupt_bio_byte feature */ 53 unsigned int corrupt_buf_byte; 54 unsigned int corrupt_buf_rw; 55 unsigned int corrupt_buf_value; 56 unsigned int corrupt_buf_flags; /* for B_XXX flags */ 57 } dm_target_flakey_config_t; 58 59 #define FLAKEY_CORRUPT_DIR(tfc) \ 60 ((tfc)->corrupt_buf_rw == BUF_CMD_READ ? 'r' : 'w') 61 62 static int _init_features(dm_target_flakey_config_t*, int, char**); 63 static __inline void _submit(dm_target_flakey_config_t*, struct bio*); 64 static int _flakey_read(dm_target_flakey_config_t*, struct buf*); 65 static int _flakey_write(dm_target_flakey_config_t*, struct buf*); 66 static int _flakey_corrupt_buf(dm_target_flakey_config_t*, struct bio*); 67 68 static int 69 dm_target_flakey_init(dm_table_entry_t *table_en, int argc, char **argv) 70 { 71 dm_target_flakey_config_t *tfc; 72 dm_pdev_t *dmp; 73 int err; 74 75 dmdebug("Flakey target init: argc=%d\n", argc); 76 77 if (argc < 4) { 78 kprintf("Flakey target takes 4 or more args\n"); 79 return EINVAL; 80 } 81 82 tfc = kmalloc(sizeof(*tfc), M_DMFLAKEY, M_WAITOK | M_ZERO); 83 if (tfc == NULL) 84 return ENOMEM; 85 86 if ((dmp = dm_pdev_insert(argv[0])) == NULL) { 87 err = ENOENT; 88 goto fail; 89 } 90 tfc->pdev = dmp; 91 tfc->offset = atoi64(argv[1]); 92 tfc->up_int = atoi64(argv[2]); 93 tfc->down_int = atoi64(argv[3]); 94 tfc->offset_time = ticks; 95 96 if ((tfc->up_int + tfc->down_int) == 0) { 97 kprintf("Sum of up/down interval is 0\n"); 98 err = EINVAL; 99 goto fail; 100 } 101 102 if (tfc->up_int + tfc->down_int < tfc->up_int) { 103 kprintf("Interval time overflow\n"); 104 err = EINVAL; 105 goto fail; 106 } 107 108 err = _init_features(tfc, argc - 4, argv + 4); 109 if (err) 110 goto fail; 111 112 dm_table_add_deps(table_en, dmp); 113 114 dm_table_init_target(table_en, tfc); 115 116 return 0; 117 fail: 118 kfree(tfc, M_DMFLAKEY); 119 return err; 120 } 121 122 static int 123 _init_features(dm_target_flakey_config_t *tfc, int argc, char **argv) 124 { 125 char *arg; 126 unsigned int value; 127 128 if (argc == 0) 129 return 0; 130 131 argc = atoi64(*argv++); /* # of args for features */ 132 if (argc > 6) { 133 kprintf("Invalid # of feature args %d\n", argc); 134 return EINVAL; 135 } 136 137 while (argc) { 138 argc--; 139 arg = *argv++; 140 141 /* drop_writes */ 142 if (strcmp(arg, "drop_writes") == 0) { 143 tfc->drop_writes = 1; 144 continue; 145 } 146 147 /* corrupt_bio_byte <Nth_byte> <direction> <value> <flags> */ 148 if (strcmp(arg, "corrupt_bio_byte") == 0) { 149 if (argc < 4) { 150 kprintf("Invalid # of feature args %d for " 151 "corrupt_bio_byte\n", argc); 152 return EINVAL; 153 } 154 155 /* <Nth_byte> */ 156 argc--; 157 value = atoi64(*argv++); 158 if (value < 1) { 159 kprintf("Invalid corrupt_bio_byte " 160 "<Nth_byte> arg %u\n", value); 161 return EINVAL; 162 } 163 tfc->corrupt_buf_byte = value; 164 165 /* <direction> */ 166 argc--; 167 arg = *argv++; 168 if (strcmp(arg, "r") == 0) { 169 tfc->corrupt_buf_rw = BUF_CMD_READ; 170 } else if (strcmp(arg, "w") == 0) { 171 tfc->corrupt_buf_rw = BUF_CMD_WRITE; 172 } else { 173 kprintf("Invalid corrupt_bio_byte " 174 "<direction> arg %s\n", arg); 175 return EINVAL; 176 } 177 178 /* <value> */ 179 argc--; 180 value = atoi64(*argv++); 181 if (value > 0xff) { 182 kprintf("Invalid corrupt_bio_byte " 183 "<value> arg %u\n", value); 184 return EINVAL; 185 } 186 tfc->corrupt_buf_value = value; 187 188 /* <flags> */ 189 argc--; 190 tfc->corrupt_buf_flags = atoi64(*argv++); 191 192 continue; 193 } 194 195 kprintf("Unknown Flakey target feature %s\n", arg); 196 return EINVAL; 197 } 198 199 if (tfc->drop_writes && (tfc->corrupt_buf_rw == BUF_CMD_WRITE)) { 200 kprintf("Flakey target doesn't allow drop_writes feature " 201 "and corrupt_bio_byte feature with 'w' set\n"); 202 return EINVAL; 203 } 204 205 return 0; 206 } 207 208 static int 209 dm_target_flakey_destroy(dm_table_entry_t *table_en) 210 { 211 dm_target_flakey_config_t *tfc; 212 213 tfc = table_en->target_config; 214 if (tfc == NULL) 215 return 0; 216 217 dm_pdev_decr(tfc->pdev); 218 219 kfree(tfc, M_DMFLAKEY); 220 221 return 0; 222 } 223 224 static int 225 dm_target_flakey_strategy(dm_table_entry_t *table_en, struct buf *bp) 226 { 227 dm_target_flakey_config_t *tfc; 228 int elapsed; 229 230 tfc = table_en->target_config; 231 232 elapsed = (ticks - tfc->offset_time) / hz; 233 if (elapsed % (tfc->up_int + tfc->down_int) >= tfc->up_int) { 234 switch (bp->b_cmd) { 235 case BUF_CMD_READ: 236 return _flakey_read(tfc, bp); 237 case BUF_CMD_WRITE: 238 return _flakey_write(tfc, bp); 239 default: 240 break; 241 } 242 } 243 244 /* This is what linear target does */ 245 _submit(tfc, &bp->b_bio1); 246 247 return 0; 248 } 249 250 static __inline 251 void 252 _submit(dm_target_flakey_config_t *tfc, struct bio *bio) 253 { 254 bio->bio_offset += tfc->offset * DEV_BSIZE; 255 vn_strategy(tfc->pdev->pdev_vnode, bio); 256 } 257 258 static __inline 259 void 260 _flakey_eio_buf(struct buf *bp) 261 { 262 bp->b_error = EIO; 263 bp->b_resid = 0; 264 } 265 266 static void 267 _flakey_read_iodone(struct bio *bio) 268 { 269 struct bio *obio; 270 dm_target_flakey_config_t *tfc; 271 272 tfc = bio->bio_caller_info1.ptr; 273 obio = pop_bio(bio); 274 275 /* 276 * Linux dm-flakey has changed its read behavior in 2016. 277 * This conditional is to sync with that change. 278 */ 279 if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_READ) 280 _flakey_corrupt_buf(tfc, obio); 281 else if (!tfc->drop_writes) 282 _flakey_eio_buf(bio->bio_buf); 283 284 biodone(obio); 285 } 286 287 static int 288 _flakey_read(dm_target_flakey_config_t *tfc, struct buf *bp) 289 { 290 struct bio *bio = &bp->b_bio1; 291 struct bio *nbio; 292 293 /* 294 * Linux dm-flakey has changed its read behavior in 2016. 295 * This conditional is to sync with that change. 296 */ 297 if (!tfc->corrupt_buf_byte && !tfc->drop_writes) { 298 _flakey_eio_buf(bp); 299 biodone(bio); 300 return 0; 301 } 302 303 nbio = push_bio(bio); 304 nbio->bio_done = _flakey_read_iodone; 305 nbio->bio_caller_info1.ptr = tfc; 306 nbio->bio_offset = pop_bio(nbio)->bio_offset; 307 308 _submit(tfc, nbio); 309 310 return 0; 311 } 312 313 static int 314 _flakey_write(dm_target_flakey_config_t *tfc, struct buf *bp) 315 { 316 struct bio *bio = &bp->b_bio1; 317 318 if (tfc->drop_writes) { 319 dmdebug("bio=%p drop_writes offset=%ju\n", 320 bio, bio->bio_offset); 321 biodone(bio); 322 return 0; 323 } 324 325 if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_WRITE) { 326 _flakey_corrupt_buf(tfc, bio); 327 _submit(tfc, bio); 328 return 0; 329 } 330 331 /* Error all I/Os if neither of the above two */ 332 _flakey_eio_buf(bp); 333 biodone(bio); 334 335 return 0; 336 } 337 338 static int 339 _flakey_corrupt_buf(dm_target_flakey_config_t *tfc, struct bio *bio) 340 { 341 struct buf *bp; 342 343 bp = bio->bio_buf; 344 345 if (bp->b_data == NULL) 346 return 1; 347 if (bp->b_error) 348 return 1; /* Don't corrupt on error */ 349 if (bp->b_bcount < tfc->corrupt_buf_byte) 350 return 1; 351 if ((bp->b_flags & tfc->corrupt_buf_flags) != tfc->corrupt_buf_flags) 352 return 1; 353 354 bp->b_data[tfc->corrupt_buf_byte - 1] = tfc->corrupt_buf_value; 355 dmdebug("bio=%p dir=%c offset=%ju Nth=%u value=%u\n", 356 bio, 357 FLAKEY_CORRUPT_DIR(tfc), 358 bio->bio_offset, 359 tfc->corrupt_buf_byte, 360 tfc->corrupt_buf_value); 361 362 return 0; 363 } 364 365 static char * 366 dm_target_flakey_table(void *target_config) 367 { 368 dm_target_flakey_config_t *tfc; 369 char *params, *p; 370 int drop_writes; 371 372 tfc = target_config; 373 KKASSERT(tfc != NULL); 374 375 drop_writes = tfc->drop_writes; 376 377 params = dm_alloc_string(DM_MAX_PARAMS_SIZE); 378 p = params; 379 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, "%s %d %d %d %u ", 380 tfc->pdev->udev_name, tfc->offset_time, 381 tfc->up_int, tfc->down_int, 382 drop_writes + (tfc->corrupt_buf_byte > 0) * 5); 383 384 if (drop_writes) 385 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, "drop_writes "); 386 387 if (tfc->corrupt_buf_byte) 388 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, 389 "corrupt_bio_byte %u %c %u %u ", 390 tfc->corrupt_buf_byte, 391 FLAKEY_CORRUPT_DIR(tfc), 392 tfc->corrupt_buf_value, 393 tfc->corrupt_buf_flags); 394 *(--p) = '\0'; 395 396 return params; 397 } 398 399 static int 400 dmtf_mod_handler(module_t mod, int type, void *unused) 401 { 402 dm_target_t *dmt = NULL; 403 int err = 0; 404 405 switch(type) { 406 case MOD_LOAD: 407 if ((dmt = dm_target_lookup("flakey")) != NULL) { 408 dm_target_unbusy(dmt); 409 return EEXIST; 410 } 411 dmt = dm_target_alloc("flakey"); 412 dmt->version[0] = 1; 413 dmt->version[1] = 0; 414 dmt->version[2] = 0; 415 strlcpy(dmt->name, "flakey", DM_MAX_TYPE_NAME); 416 dmt->init = &dm_target_flakey_init; 417 dmt->destroy = &dm_target_flakey_destroy; 418 dmt->strategy = &dm_target_flakey_strategy; 419 dmt->table = &dm_target_flakey_table; 420 421 err = dm_target_insert(dmt); 422 if (err == 0) 423 kprintf("dm_target_flakey: Successfully initialized\n"); 424 break; 425 426 case MOD_UNLOAD: 427 err = dm_target_remove("flakey"); 428 if (err == 0) 429 kprintf("dm_target_flakey: unloaded\n"); 430 break; 431 } 432 433 return err; 434 } 435 436 DM_TARGET_MODULE(dm_target_flakey, dmtf_mod_handler); 437