dm-dust.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2018 Red Hat, Inc.
  4. *
  5. * This is a test "dust" device, which fails reads on specified
  6. * sectors, emulating the behavior of a hard disk drive sending
  7. * a "Read Medium Error" sense.
  8. *
  9. */
  10. #include <linux/device-mapper.h>
  11. #include <linux/module.h>
  12. #include <linux/rbtree.h>
  13. #define DM_MSG_PREFIX "dust"
  14. struct badblock {
  15. struct rb_node node;
  16. sector_t bb;
  17. };
  18. struct dust_device {
  19. struct dm_dev *dev;
  20. struct rb_root badblocklist;
  21. unsigned long long badblock_count;
  22. spinlock_t dust_lock;
  23. unsigned int blksz;
  24. int sect_per_block_shift;
  25. unsigned int sect_per_block;
  26. sector_t start;
  27. bool fail_read_on_bb:1;
  28. bool quiet_mode:1;
  29. };
  30. static struct badblock *dust_rb_search(struct rb_root *root, sector_t blk)
  31. {
  32. struct rb_node *node = root->rb_node;
  33. while (node) {
  34. struct badblock *bblk = rb_entry(node, struct badblock, node);
  35. if (bblk->bb > blk)
  36. node = node->rb_left;
  37. else if (bblk->bb < blk)
  38. node = node->rb_right;
  39. else
  40. return bblk;
  41. }
  42. return NULL;
  43. }
  44. static bool dust_rb_insert(struct rb_root *root, struct badblock *new)
  45. {
  46. struct badblock *bblk;
  47. struct rb_node **link = &root->rb_node, *parent = NULL;
  48. sector_t value = new->bb;
  49. while (*link) {
  50. parent = *link;
  51. bblk = rb_entry(parent, struct badblock, node);
  52. if (bblk->bb > value)
  53. link = &(*link)->rb_left;
  54. else if (bblk->bb < value)
  55. link = &(*link)->rb_right;
  56. else
  57. return false;
  58. }
  59. rb_link_node(&new->node, parent, link);
  60. rb_insert_color(&new->node, root);
  61. return true;
  62. }
  63. static int dust_remove_block(struct dust_device *dd, unsigned long long block)
  64. {
  65. struct badblock *bblock;
  66. unsigned long flags;
  67. spin_lock_irqsave(&dd->dust_lock, flags);
  68. bblock = dust_rb_search(&dd->badblocklist, block);
  69. if (bblock == NULL) {
  70. if (!dd->quiet_mode) {
  71. DMERR("%s: block %llu not found in badblocklist",
  72. __func__, block);
  73. }
  74. spin_unlock_irqrestore(&dd->dust_lock, flags);
  75. return -EINVAL;
  76. }
  77. rb_erase(&bblock->node, &dd->badblocklist);
  78. dd->badblock_count--;
  79. if (!dd->quiet_mode)
  80. DMINFO("%s: badblock removed at block %llu", __func__, block);
  81. kfree(bblock);
  82. spin_unlock_irqrestore(&dd->dust_lock, flags);
  83. return 0;
  84. }
  85. static int dust_add_block(struct dust_device *dd, unsigned long long block)
  86. {
  87. struct badblock *bblock;
  88. unsigned long flags;
  89. bblock = kmalloc(sizeof(*bblock), GFP_KERNEL);
  90. if (bblock == NULL) {
  91. if (!dd->quiet_mode)
  92. DMERR("%s: badblock allocation failed", __func__);
  93. return -ENOMEM;
  94. }
  95. spin_lock_irqsave(&dd->dust_lock, flags);
  96. bblock->bb = block;
  97. if (!dust_rb_insert(&dd->badblocklist, bblock)) {
  98. if (!dd->quiet_mode) {
  99. DMERR("%s: block %llu already in badblocklist",
  100. __func__, block);
  101. }
  102. spin_unlock_irqrestore(&dd->dust_lock, flags);
  103. kfree(bblock);
  104. return -EINVAL;
  105. }
  106. dd->badblock_count++;
  107. if (!dd->quiet_mode)
  108. DMINFO("%s: badblock added at block %llu", __func__, block);
  109. spin_unlock_irqrestore(&dd->dust_lock, flags);
  110. return 0;
  111. }
  112. static int dust_query_block(struct dust_device *dd, unsigned long long block)
  113. {
  114. struct badblock *bblock;
  115. unsigned long flags;
  116. spin_lock_irqsave(&dd->dust_lock, flags);
  117. bblock = dust_rb_search(&dd->badblocklist, block);
  118. if (bblock != NULL)
  119. DMINFO("%s: block %llu found in badblocklist", __func__, block);
  120. else
  121. DMINFO("%s: block %llu not found in badblocklist", __func__, block);
  122. spin_unlock_irqrestore(&dd->dust_lock, flags);
  123. return 0;
  124. }
  125. static int __dust_map_read(struct dust_device *dd, sector_t thisblock)
  126. {
  127. struct badblock *bblk = dust_rb_search(&dd->badblocklist, thisblock);
  128. if (bblk)
  129. return DM_MAPIO_KILL;
  130. return DM_MAPIO_REMAPPED;
  131. }
  132. static int dust_map_read(struct dust_device *dd, sector_t thisblock,
  133. bool fail_read_on_bb)
  134. {
  135. unsigned long flags;
  136. int ret = DM_MAPIO_REMAPPED;
  137. if (fail_read_on_bb) {
  138. thisblock >>= dd->sect_per_block_shift;
  139. spin_lock_irqsave(&dd->dust_lock, flags);
  140. ret = __dust_map_read(dd, thisblock);
  141. spin_unlock_irqrestore(&dd->dust_lock, flags);
  142. }
  143. return ret;
  144. }
  145. static void __dust_map_write(struct dust_device *dd, sector_t thisblock)
  146. {
  147. struct badblock *bblk = dust_rb_search(&dd->badblocklist, thisblock);
  148. if (bblk) {
  149. rb_erase(&bblk->node, &dd->badblocklist);
  150. dd->badblock_count--;
  151. kfree(bblk);
  152. if (!dd->quiet_mode) {
  153. sector_div(thisblock, dd->sect_per_block);
  154. DMINFO("block %llu removed from badblocklist by write",
  155. (unsigned long long)thisblock);
  156. }
  157. }
  158. }
  159. static int dust_map_write(struct dust_device *dd, sector_t thisblock,
  160. bool fail_read_on_bb)
  161. {
  162. unsigned long flags;
  163. if (fail_read_on_bb) {
  164. thisblock >>= dd->sect_per_block_shift;
  165. spin_lock_irqsave(&dd->dust_lock, flags);
  166. __dust_map_write(dd, thisblock);
  167. spin_unlock_irqrestore(&dd->dust_lock, flags);
  168. }
  169. return DM_MAPIO_REMAPPED;
  170. }
  171. static int dust_map(struct dm_target *ti, struct bio *bio)
  172. {
  173. struct dust_device *dd = ti->private;
  174. int ret;
  175. bio_set_dev(bio, dd->dev->bdev);
  176. bio->bi_iter.bi_sector = dd->start + dm_target_offset(ti, bio->bi_iter.bi_sector);
  177. if (bio_data_dir(bio) == READ)
  178. ret = dust_map_read(dd, bio->bi_iter.bi_sector, dd->fail_read_on_bb);
  179. else
  180. ret = dust_map_write(dd, bio->bi_iter.bi_sector, dd->fail_read_on_bb);
  181. return ret;
  182. }
  183. static bool __dust_clear_badblocks(struct rb_root *tree,
  184. unsigned long long count)
  185. {
  186. struct rb_node *node = NULL, *nnode = NULL;
  187. nnode = rb_first(tree);
  188. if (nnode == NULL) {
  189. BUG_ON(count != 0);
  190. return false;
  191. }
  192. while (nnode) {
  193. node = nnode;
  194. nnode = rb_next(node);
  195. rb_erase(node, tree);
  196. count--;
  197. kfree(node);
  198. }
  199. BUG_ON(count != 0);
  200. BUG_ON(tree->rb_node != NULL);
  201. return true;
  202. }
  203. static int dust_clear_badblocks(struct dust_device *dd)
  204. {
  205. unsigned long flags;
  206. struct rb_root badblocklist;
  207. unsigned long long badblock_count;
  208. spin_lock_irqsave(&dd->dust_lock, flags);
  209. badblocklist = dd->badblocklist;
  210. badblock_count = dd->badblock_count;
  211. dd->badblocklist = RB_ROOT;
  212. dd->badblock_count = 0;
  213. spin_unlock_irqrestore(&dd->dust_lock, flags);
  214. if (!__dust_clear_badblocks(&badblocklist, badblock_count))
  215. DMINFO("%s: no badblocks found", __func__);
  216. else
  217. DMINFO("%s: badblocks cleared", __func__);
  218. return 0;
  219. }
  220. /*
  221. * Target parameters:
  222. *
  223. * <device_path> <offset> <blksz>
  224. *
  225. * device_path: path to the block device
  226. * offset: offset to data area from start of device_path
  227. * blksz: block size (minimum 512, maximum 1073741824, must be a power of 2)
  228. */
  229. static int dust_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  230. {
  231. struct dust_device *dd;
  232. unsigned long long tmp;
  233. char dummy;
  234. unsigned int blksz;
  235. unsigned int sect_per_block;
  236. sector_t DUST_MAX_BLKSZ_SECTORS = 2097152;
  237. sector_t max_block_sectors = min(ti->len, DUST_MAX_BLKSZ_SECTORS);
  238. if (argc != 3) {
  239. ti->error = "Invalid argument count";
  240. return -EINVAL;
  241. }
  242. if (kstrtouint(argv[2], 10, &blksz) || !blksz) {
  243. ti->error = "Invalid block size parameter";
  244. return -EINVAL;
  245. }
  246. if (blksz < 512) {
  247. ti->error = "Block size must be at least 512";
  248. return -EINVAL;
  249. }
  250. if (!is_power_of_2(blksz)) {
  251. ti->error = "Block size must be a power of 2";
  252. return -EINVAL;
  253. }
  254. if (to_sector(blksz) > max_block_sectors) {
  255. ti->error = "Block size is too large";
  256. return -EINVAL;
  257. }
  258. sect_per_block = (blksz >> SECTOR_SHIFT);
  259. if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 || tmp != (sector_t)tmp) {
  260. ti->error = "Invalid device offset sector";
  261. return -EINVAL;
  262. }
  263. dd = kzalloc(sizeof(struct dust_device), GFP_KERNEL);
  264. if (dd == NULL) {
  265. ti->error = "Cannot allocate context";
  266. return -ENOMEM;
  267. }
  268. if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dd->dev)) {
  269. ti->error = "Device lookup failed";
  270. kfree(dd);
  271. return -EINVAL;
  272. }
  273. dd->sect_per_block = sect_per_block;
  274. dd->blksz = blksz;
  275. dd->start = tmp;
  276. dd->sect_per_block_shift = __ffs(sect_per_block);
  277. /*
  278. * Whether to fail a read on a "bad" block.
  279. * Defaults to false; enabled later by message.
  280. */
  281. dd->fail_read_on_bb = false;
  282. /*
  283. * Initialize bad block list rbtree.
  284. */
  285. dd->badblocklist = RB_ROOT;
  286. dd->badblock_count = 0;
  287. spin_lock_init(&dd->dust_lock);
  288. dd->quiet_mode = false;
  289. BUG_ON(dm_set_target_max_io_len(ti, dd->sect_per_block) != 0);
  290. ti->num_discard_bios = 1;
  291. ti->num_flush_bios = 1;
  292. ti->private = dd;
  293. return 0;
  294. }
  295. static void dust_dtr(struct dm_target *ti)
  296. {
  297. struct dust_device *dd = ti->private;
  298. __dust_clear_badblocks(&dd->badblocklist, dd->badblock_count);
  299. dm_put_device(ti, dd->dev);
  300. kfree(dd);
  301. }
  302. static int dust_message(struct dm_target *ti, unsigned int argc, char **argv,
  303. char *result_buf, unsigned int maxlen)
  304. {
  305. struct dust_device *dd = ti->private;
  306. sector_t size = i_size_read(dd->dev->bdev->bd_inode) >> SECTOR_SHIFT;
  307. bool invalid_msg = false;
  308. int result = -EINVAL;
  309. unsigned long long tmp, block;
  310. unsigned long flags;
  311. char dummy;
  312. if (argc == 1) {
  313. if (!strcasecmp(argv[0], "addbadblock") ||
  314. !strcasecmp(argv[0], "removebadblock") ||
  315. !strcasecmp(argv[0], "queryblock")) {
  316. DMERR("%s requires an additional argument", argv[0]);
  317. } else if (!strcasecmp(argv[0], "disable")) {
  318. DMINFO("disabling read failures on bad sectors");
  319. dd->fail_read_on_bb = false;
  320. result = 0;
  321. } else if (!strcasecmp(argv[0], "enable")) {
  322. DMINFO("enabling read failures on bad sectors");
  323. dd->fail_read_on_bb = true;
  324. result = 0;
  325. } else if (!strcasecmp(argv[0], "countbadblocks")) {
  326. spin_lock_irqsave(&dd->dust_lock, flags);
  327. DMINFO("countbadblocks: %llu badblock(s) found",
  328. dd->badblock_count);
  329. spin_unlock_irqrestore(&dd->dust_lock, flags);
  330. result = 0;
  331. } else if (!strcasecmp(argv[0], "clearbadblocks")) {
  332. result = dust_clear_badblocks(dd);
  333. } else if (!strcasecmp(argv[0], "quiet")) {
  334. if (!dd->quiet_mode)
  335. dd->quiet_mode = true;
  336. else
  337. dd->quiet_mode = false;
  338. result = 0;
  339. } else {
  340. invalid_msg = true;
  341. }
  342. } else if (argc == 2) {
  343. if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1)
  344. return result;
  345. block = tmp;
  346. sector_div(size, dd->sect_per_block);
  347. if (block > size) {
  348. DMERR("selected block value out of range");
  349. return result;
  350. }
  351. if (!strcasecmp(argv[0], "addbadblock"))
  352. result = dust_add_block(dd, block);
  353. else if (!strcasecmp(argv[0], "removebadblock"))
  354. result = dust_remove_block(dd, block);
  355. else if (!strcasecmp(argv[0], "queryblock"))
  356. result = dust_query_block(dd, block);
  357. else
  358. invalid_msg = true;
  359. } else
  360. DMERR("invalid number of arguments '%d'", argc);
  361. if (invalid_msg)
  362. DMERR("unrecognized message '%s' received", argv[0]);
  363. return result;
  364. }
  365. static void dust_status(struct dm_target *ti, status_type_t type,
  366. unsigned int status_flags, char *result, unsigned int maxlen)
  367. {
  368. struct dust_device *dd = ti->private;
  369. unsigned int sz = 0;
  370. switch (type) {
  371. case STATUSTYPE_INFO:
  372. DMEMIT("%s %s %s", dd->dev->name,
  373. dd->fail_read_on_bb ? "fail_read_on_bad_block" : "bypass",
  374. dd->quiet_mode ? "quiet" : "verbose");
  375. break;
  376. case STATUSTYPE_TABLE:
  377. DMEMIT("%s %llu %u", dd->dev->name,
  378. (unsigned long long)dd->start, dd->blksz);
  379. break;
  380. }
  381. }
  382. static int dust_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
  383. {
  384. struct dust_device *dd = ti->private;
  385. struct dm_dev *dev = dd->dev;
  386. *bdev = dev->bdev;
  387. /*
  388. * Only pass ioctls through if the device sizes match exactly.
  389. */
  390. if (dd->start ||
  391. ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
  392. return 1;
  393. return 0;
  394. }
  395. static int dust_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn,
  396. void *data)
  397. {
  398. struct dust_device *dd = ti->private;
  399. return fn(ti, dd->dev, dd->start, ti->len, data);
  400. }
  401. static struct target_type dust_target = {
  402. .name = "dust",
  403. .version = {1, 0, 0},
  404. .module = THIS_MODULE,
  405. .ctr = dust_ctr,
  406. .dtr = dust_dtr,
  407. .iterate_devices = dust_iterate_devices,
  408. .map = dust_map,
  409. .message = dust_message,
  410. .status = dust_status,
  411. .prepare_ioctl = dust_prepare_ioctl,
  412. };
  413. static int __init dm_dust_init(void)
  414. {
  415. int result = dm_register_target(&dust_target);
  416. if (result < 0)
  417. DMERR("dm_register_target failed %d", result);
  418. return result;
  419. }
  420. static void __exit dm_dust_exit(void)
  421. {
  422. dm_unregister_target(&dust_target);
  423. }
  424. module_init(dm_dust_init);
  425. module_exit(dm_dust_exit);
  426. MODULE_DESCRIPTION(DM_NAME " dust test target");
  427. MODULE_AUTHOR("Bryan Gurney <dm-devel@redhat.com>");
  428. MODULE_LICENSE("GPL");