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 <kusumi.tomohiro@gmail.com> 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 <dev/disk/dm/dm.h> 36 37 MALLOC_DEFINE(M_DMFLAKEY, "dm_flakey", "Device Mapper Target Flakey"); 38 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; 46 47 /* drop_writes feature */ 48 int drop_writes; 49 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; 56 57 #define FLAKEY_CORRUPT_DIR(tfc) \ 58 ((tfc)->corrupt_buf_rw == BUF_CMD_READ ? 'r' : 'w') 59 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*); 65 66 static int 67 dm_target_flakey_init(dm_table_entry_t *table_en, int argc, char **argv) 68 { 69 dm_target_flakey_config_t *tfc; 70 dm_pdev_t *dmp; 71 int err; 72 73 dmdebug("Flakey target init: argc=%d\n", argc); 74 75 if (argc < 4) { 76 kprintf("Flakey target takes 4 or more args\n"); 77 return EINVAL; 78 } 79 80 tfc = kmalloc(sizeof(*tfc), M_DMFLAKEY, M_WAITOK | M_ZERO); 81 if (tfc == NULL) 82 return ENOMEM; 83 84 if ((dmp = dm_pdev_insert(argv[0])) == NULL) { 85 err = ENOENT; 86 goto fail; 87 } 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; 93 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; 98 } 99 100 if (tfc->up_int + tfc->down_int < tfc->up_int) { 101 kprintf("Interval time overflow\n"); 102 err = EINVAL; 103 goto fail; 104 } 105 106 err = _init_features(tfc, argc - 4, argv + 4); 107 if (err) 108 goto fail; 109 110 dm_table_add_deps(table_en, dmp); 111 112 dm_table_init_target(table_en, tfc); 113 114 return 0; 115 fail: 116 kfree(tfc, M_DMFLAKEY); 117 return err; 118 } 119 120 static int 121 _init_features(dm_target_flakey_config_t *tfc, int argc, char **argv) 122 { 123 char *arg; 124 unsigned int value; 125 126 if (argc == 0) 127 return 0; 128 129 argc = atoi64(*argv++); /* # of args for features */ 130 if (argc > 6) { 131 kprintf("Invalid # of feature args %d\n", argc); 132 return EINVAL; 133 } 134 135 while (argc) { 136 argc--; 137 arg = *argv++; 138 139 /* drop_writes */ 140 if (strcmp(arg, "drop_writes") == 0) { 141 tfc->drop_writes = 1; 142 continue; 143 } 144 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; 151 } 152 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; 160 } 161 tfc->corrupt_buf_byte = value; 162 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; 174 } 175 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; 183 } 184 tfc->corrupt_buf_value = value; 185 186 /* <flags> */ 187 argc--; 188 tfc->corrupt_buf_flags = atoi64(*argv++); 189 190 continue; 191 } 192 193 kprintf("Unknown Flakey target feature %s\n", arg); 194 return EINVAL; 195 } 196 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; 201 } 202 203 return 0; 204 } 205 206 static int 207 dm_target_flakey_destroy(dm_table_entry_t *table_en) 208 { 209 dm_target_flakey_config_t *tfc; 210 211 tfc = table_en->target_config; 212 if (tfc == NULL) 213 return 0; 214 215 dm_pdev_decr(tfc->pdev); 216 217 kfree(tfc, M_DMFLAKEY); 218 219 return 0; 220 } 221 222 static int 223 dm_target_flakey_strategy(dm_table_entry_t *table_en, struct buf *bp) 224 { 225 dm_target_flakey_config_t *tfc; 226 int elapsed; 227 228 tfc = table_en->target_config; 229 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; 239 } 240 } 241 242 /* This is what linear target does */ 243 _submit(tfc, &bp->b_bio1); 244 245 return 0; 246 } 247 248 static __inline 249 void 250 _submit(dm_target_flakey_config_t *tfc, struct bio *bio) 251 { 252 bio->bio_offset += tfc->offset * DEV_BSIZE; 253 vn_strategy(tfc->pdev->pdev_vnode, bio); 254 } 255 256 static __inline 257 void 258 _flakey_eio_buf(struct buf *bp) 259 { 260 bp->b_error = EIO; 261 bp->b_resid = 0; 262 } 263 264 static void 265 _flakey_read_iodone(struct bio *bio) 266 { 267 struct bio *obio; 268 dm_target_flakey_config_t *tfc; 269 270 tfc = bio->bio_caller_info1.ptr; 271 obio = pop_bio(bio); 272 273 /* 274 * Linux dm-flakey has changed its read behavior in 2016. 275 * This conditional is to sync with that change. 276 */ 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); 281 282 biodone(obio); 283 } 284 285 static int 286 _flakey_read(dm_target_flakey_config_t *tfc, struct buf *bp) 287 { 288 struct bio *bio = &bp->b_bio1; 289 struct bio *nbio; 290 291 /* 292 * Linux dm-flakey has changed its read behavior in 2016. 293 * This conditional is to sync with that change. 294 */ 295 if (!tfc->corrupt_buf_byte && !tfc->drop_writes) { 296 _flakey_eio_buf(bp); 297 biodone(bio); 298 return 0; 299 } 300 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; 305 306 _submit(tfc, nbio); 307 308 return 0; 309 } 310 311 static int 312 _flakey_write(dm_target_flakey_config_t *tfc, struct buf *bp) 313 { 314 struct bio *bio = &bp->b_bio1; 315 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; 321 } 322 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; 327 } 328 329 /* Error all I/Os if neither of the above two */ 330 _flakey_eio_buf(bp); 331 biodone(bio); 332 333 return 0; 334 } 335 336 static int 337 _flakey_corrupt_buf(dm_target_flakey_config_t *tfc, struct bio *bio) 338 { 339 struct buf *bp; 340 341 bp = bio->bio_buf; 342 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; 351 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); 359 360 return 0; 361 } 362 363 static char * 364 dm_target_flakey_table(void *target_config) 365 { 366 dm_target_flakey_config_t *tfc; 367 char *params, *p; 368 int drop_writes; 369 370 tfc = target_config; 371 KKASSERT(tfc != NULL); 372 373 drop_writes = tfc->drop_writes; 374 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); 381 382 if (drop_writes) 383 p += ksnprintf(p, DM_MAX_PARAMS_SIZE, "drop_writes "); 384 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'; 393 394 return params; 395 } 396 397 static int 398 dmtf_mod_handler(module_t mod, int type, void *unused) 399 { 400 dm_target_t *dmt = NULL; 401 int err = 0; 402 403 switch(type) { 404 case MOD_LOAD: 405 if ((dmt = dm_target_lookup("flakey")) != NULL) { 406 dm_target_unbusy(dmt); 407 return EEXIST; 408 } 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; 418 419 err = dm_target_insert(dmt); 420 if (err == 0) 421 kprintf("dm_target_flakey: Successfully initialized\n"); 422 break; 423 424 case MOD_UNLOAD: 425 err = dm_target_remove("flakey"); 426 if (err == 0) 427 kprintf("dm_target_flakey: unloaded\n"); 428 break; 429 } 430 431 return err; 432 } 433 434 DM_TARGET_MODULE(dm_target_flakey, dmtf_mod_handler); 435