1e8dcf2d1SChristoph Hellwig // SPDX-License-Identifier: GPL-2.0 2e8dcf2d1SChristoph Hellwig /* 3e8dcf2d1SChristoph Hellwig * Copyright (c) 2026 Christoph Hellwig. 4e8dcf2d1SChristoph Hellwig */ 5e8dcf2d1SChristoph Hellwig #include <linux/debugfs.h> 6e8dcf2d1SChristoph Hellwig #include <linux/blkdev.h> 7e8dcf2d1SChristoph Hellwig #include <linux/parser.h> 8e8dcf2d1SChristoph Hellwig #include <linux/seq_file.h> 9e8dcf2d1SChristoph Hellwig #include "blk.h" 10e8dcf2d1SChristoph Hellwig #include "error-injection.h" 11e8dcf2d1SChristoph Hellwig 12e8dcf2d1SChristoph Hellwig struct blk_error_inject { 13e8dcf2d1SChristoph Hellwig struct list_head entry; 14e8dcf2d1SChristoph Hellwig sector_t start; 15e8dcf2d1SChristoph Hellwig sector_t end; 16e8dcf2d1SChristoph Hellwig enum req_op op; 17e8dcf2d1SChristoph Hellwig blk_status_t status; 18e8dcf2d1SChristoph Hellwig 19e8dcf2d1SChristoph Hellwig /* only inject every 1 / chance times */ 20e8dcf2d1SChristoph Hellwig unsigned int chance; 21e8dcf2d1SChristoph Hellwig }; 22e8dcf2d1SChristoph Hellwig 23e8dcf2d1SChristoph Hellwig DEFINE_STATIC_KEY_FALSE(blk_error_injection_enabled); 24e8dcf2d1SChristoph Hellwig 25e8dcf2d1SChristoph Hellwig bool __blk_error_inject(struct bio *bio) 26e8dcf2d1SChristoph Hellwig { 27e8dcf2d1SChristoph Hellwig struct gendisk *disk = bio->bi_bdev->bd_disk; 28e8dcf2d1SChristoph Hellwig struct blk_error_inject *inj; 29e8dcf2d1SChristoph Hellwig 30e8dcf2d1SChristoph Hellwig rcu_read_lock(); 31e8dcf2d1SChristoph Hellwig list_for_each_entry_rcu(inj, &disk->error_injection_list, entry) { 32e8dcf2d1SChristoph Hellwig if (bio_op(bio) != inj->op) 33e8dcf2d1SChristoph Hellwig continue; 34e8dcf2d1SChristoph Hellwig /* 35e8dcf2d1SChristoph Hellwig * This never matches 0-sized bios like empty WRITEs with 36e8dcf2d1SChristoph Hellwig * REQ_PREFLUSH or ZONE_RESET_ALL. While adding a special case 37e8dcf2d1SChristoph Hellwig * for them would be trivial, that means any WRITE rule would 38e8dcf2d1SChristoph Hellwig * trigger for flushes. So before we can make this work 39e8dcf2d1SChristoph Hellwig * properly, we'll need to start using REQ_OP_FLUSH for pure 40e8dcf2d1SChristoph Hellwig * flushes at the bio level like we already do in blk-mq. 41e8dcf2d1SChristoph Hellwig */ 42e8dcf2d1SChristoph Hellwig if (bio->bi_iter.bi_sector > inj->end || 43e8dcf2d1SChristoph Hellwig bio_end_sector(bio) <= inj->start) 44e8dcf2d1SChristoph Hellwig continue; 45e8dcf2d1SChristoph Hellwig if (inj->chance > 1 && (get_random_u32() % inj->chance) != 0) 46e8dcf2d1SChristoph Hellwig continue; 47e8dcf2d1SChristoph Hellwig 48e8dcf2d1SChristoph Hellwig pr_info_ratelimited("%pg: injecting %s error for %s at sector %llu:%u\n", 49e8dcf2d1SChristoph Hellwig disk->part0, blk_status_to_str(inj->status), 50e8dcf2d1SChristoph Hellwig blk_op_str(inj->op), bio->bi_iter.bi_sector, 51e8dcf2d1SChristoph Hellwig bio_sectors(bio)); 52e8dcf2d1SChristoph Hellwig bio->bi_status = inj->status; 53e8dcf2d1SChristoph Hellwig rcu_read_unlock(); 54e8dcf2d1SChristoph Hellwig bio_endio(bio); 55e8dcf2d1SChristoph Hellwig return true; 56e8dcf2d1SChristoph Hellwig } 57e8dcf2d1SChristoph Hellwig rcu_read_unlock(); 58e8dcf2d1SChristoph Hellwig return false; 59e8dcf2d1SChristoph Hellwig } 60e8dcf2d1SChristoph Hellwig 61e8dcf2d1SChristoph Hellwig static int error_inject_add(struct gendisk *disk, enum req_op op, 62e8dcf2d1SChristoph Hellwig sector_t start, u64 nr_sectors, blk_status_t status, 63e8dcf2d1SChristoph Hellwig unsigned int chance) 64e8dcf2d1SChristoph Hellwig { 65e8dcf2d1SChristoph Hellwig struct blk_error_inject *inj; 66e8dcf2d1SChristoph Hellwig int error = -EINVAL; 67e8dcf2d1SChristoph Hellwig 68e8dcf2d1SChristoph Hellwig if (op == REQ_OP_LAST) 69e8dcf2d1SChristoph Hellwig return -EINVAL; 70e8dcf2d1SChristoph Hellwig if (status == BLK_STS_OK) 71e8dcf2d1SChristoph Hellwig return -EINVAL; 72e8dcf2d1SChristoph Hellwig 73e8dcf2d1SChristoph Hellwig inj = kzalloc_obj(*inj); 74e8dcf2d1SChristoph Hellwig if (!inj) 75e8dcf2d1SChristoph Hellwig return -ENOMEM; 76e8dcf2d1SChristoph Hellwig 77e8dcf2d1SChristoph Hellwig if (nr_sectors) { 78e8dcf2d1SChristoph Hellwig if (U64_MAX - nr_sectors < start) 79e8dcf2d1SChristoph Hellwig goto out_free_inj; 80e8dcf2d1SChristoph Hellwig inj->end = start + nr_sectors - 1; 81e8dcf2d1SChristoph Hellwig } else { 82e8dcf2d1SChristoph Hellwig inj->end = U64_MAX; 83e8dcf2d1SChristoph Hellwig } 84e8dcf2d1SChristoph Hellwig 85e8dcf2d1SChristoph Hellwig inj->op = op; 86e8dcf2d1SChristoph Hellwig inj->start = start; 87e8dcf2d1SChristoph Hellwig inj->status = status; 88e8dcf2d1SChristoph Hellwig inj->chance = chance; 89e8dcf2d1SChristoph Hellwig 90e8dcf2d1SChristoph Hellwig pr_debug_ratelimited("%pg: adding %s injection for %s at sector %llu:%llu\n", 91e8dcf2d1SChristoph Hellwig disk->part0, blk_status_to_str(status), 92e8dcf2d1SChristoph Hellwig blk_op_str(op), 93e8dcf2d1SChristoph Hellwig start, nr_sectors); 94e8dcf2d1SChristoph Hellwig 95e8dcf2d1SChristoph Hellwig /* 96e8dcf2d1SChristoph Hellwig * Add to the front of the list so that newer entries can partially 97e8dcf2d1SChristoph Hellwig * override other entries. This also intentionally allows duplicate 98e8dcf2d1SChristoph Hellwig * entries as there is no real reason to reject them. 99e8dcf2d1SChristoph Hellwig */ 100e8dcf2d1SChristoph Hellwig mutex_lock(&disk->error_injection_lock); 101e8dcf2d1SChristoph Hellwig if (!disk_live(disk)) { 102e8dcf2d1SChristoph Hellwig mutex_unlock(&disk->error_injection_lock); 103e8dcf2d1SChristoph Hellwig error = -ENODEV; 104e8dcf2d1SChristoph Hellwig goto out_free_inj; 105e8dcf2d1SChristoph Hellwig } 106e8dcf2d1SChristoph Hellwig if (list_empty(&disk->error_injection_list)) 107e8dcf2d1SChristoph Hellwig static_branch_inc(&blk_error_injection_enabled); 108e8dcf2d1SChristoph Hellwig list_add_rcu(&inj->entry, &disk->error_injection_list); 109e8dcf2d1SChristoph Hellwig set_bit(GD_ERROR_INJECT, &disk->state); 110e8dcf2d1SChristoph Hellwig mutex_unlock(&disk->error_injection_lock); 111e8dcf2d1SChristoph Hellwig return 0; 112e8dcf2d1SChristoph Hellwig 113e8dcf2d1SChristoph Hellwig out_free_inj: 114e8dcf2d1SChristoph Hellwig kfree(inj); 115e8dcf2d1SChristoph Hellwig return error; 116e8dcf2d1SChristoph Hellwig } 117e8dcf2d1SChristoph Hellwig 118e8dcf2d1SChristoph Hellwig static void error_inject_removeall(struct gendisk *disk) 119e8dcf2d1SChristoph Hellwig { 120e8dcf2d1SChristoph Hellwig struct blk_error_inject *inj; 121e8dcf2d1SChristoph Hellwig 122e8dcf2d1SChristoph Hellwig mutex_lock(&disk->error_injection_lock); 123*214cdae6SChristoph Hellwig if (test_and_clear_bit(GD_ERROR_INJECT, &disk->state)) 124*214cdae6SChristoph Hellwig static_branch_dec(&blk_error_injection_enabled); 125e8dcf2d1SChristoph Hellwig while ((inj = list_first_entry_or_null(&disk->error_injection_list, 126e8dcf2d1SChristoph Hellwig struct blk_error_inject, entry))) { 127e8dcf2d1SChristoph Hellwig list_del_rcu(&inj->entry); 128e8dcf2d1SChristoph Hellwig kfree_rcu_mightsleep(inj); 129e8dcf2d1SChristoph Hellwig } 130e8dcf2d1SChristoph Hellwig mutex_unlock(&disk->error_injection_lock); 131e8dcf2d1SChristoph Hellwig } 132e8dcf2d1SChristoph Hellwig 133e8dcf2d1SChristoph Hellwig enum options { 134e8dcf2d1SChristoph Hellwig Opt_add = (1u << 0), 135e8dcf2d1SChristoph Hellwig Opt_removeall = (1u << 1), 136e8dcf2d1SChristoph Hellwig 137e8dcf2d1SChristoph Hellwig Opt_op = (1u << 16), 138e8dcf2d1SChristoph Hellwig Opt_start = (1u << 17), 139e8dcf2d1SChristoph Hellwig Opt_nr_sectors = (1u << 18), 140e8dcf2d1SChristoph Hellwig Opt_status = (1u << 19), 141e8dcf2d1SChristoph Hellwig Opt_chance = (1u << 20), 142e8dcf2d1SChristoph Hellwig 143e8dcf2d1SChristoph Hellwig Opt_invalid, 144e8dcf2d1SChristoph Hellwig }; 145e8dcf2d1SChristoph Hellwig 146e8dcf2d1SChristoph Hellwig static const match_table_t opt_tokens = { 147e8dcf2d1SChristoph Hellwig { Opt_add, "add", }, 148e8dcf2d1SChristoph Hellwig { Opt_removeall, "removeall", }, 149e8dcf2d1SChristoph Hellwig { Opt_op, "op=%s", }, 150e8dcf2d1SChristoph Hellwig { Opt_start, "start=%u" }, 151e8dcf2d1SChristoph Hellwig { Opt_nr_sectors, "nr_sectors=%u" }, 152e8dcf2d1SChristoph Hellwig { Opt_status, "status=%s" }, 153e8dcf2d1SChristoph Hellwig { Opt_chance, "chance=%u" }, 154e8dcf2d1SChristoph Hellwig { Opt_invalid, NULL, }, 155e8dcf2d1SChristoph Hellwig }; 156e8dcf2d1SChristoph Hellwig 157e8dcf2d1SChristoph Hellwig static int match_op(substring_t *args, enum req_op *op) 158e8dcf2d1SChristoph Hellwig { 159e8dcf2d1SChristoph Hellwig const char *tag; 160e8dcf2d1SChristoph Hellwig 161e8dcf2d1SChristoph Hellwig tag = match_strdup(args); 162e8dcf2d1SChristoph Hellwig if (!tag) 163e8dcf2d1SChristoph Hellwig return -ENOMEM; 164e8dcf2d1SChristoph Hellwig *op = str_to_blk_op(tag); 165e8dcf2d1SChristoph Hellwig if (*op == REQ_OP_LAST) 166e8dcf2d1SChristoph Hellwig pr_warn("invalid op '%s'\n", tag); 167e8dcf2d1SChristoph Hellwig kfree(tag); 168e8dcf2d1SChristoph Hellwig return 0; 169e8dcf2d1SChristoph Hellwig } 170e8dcf2d1SChristoph Hellwig 171e8dcf2d1SChristoph Hellwig static int match_status(substring_t *args, blk_status_t *status) 172e8dcf2d1SChristoph Hellwig { 173e8dcf2d1SChristoph Hellwig const char *tag; 174e8dcf2d1SChristoph Hellwig 175e8dcf2d1SChristoph Hellwig tag = match_strdup(args); 176e8dcf2d1SChristoph Hellwig if (!tag) 177e8dcf2d1SChristoph Hellwig return -ENOMEM; 178e8dcf2d1SChristoph Hellwig *status = tag_to_blk_status(tag); 179e8dcf2d1SChristoph Hellwig if (!*status) 180e8dcf2d1SChristoph Hellwig pr_warn("invalid status '%s'\n", tag); 181e8dcf2d1SChristoph Hellwig kfree(tag); 182e8dcf2d1SChristoph Hellwig return 0; 183e8dcf2d1SChristoph Hellwig } 184e8dcf2d1SChristoph Hellwig 185e8dcf2d1SChristoph Hellwig static ssize_t blk_error_injection_parse_options(struct gendisk *disk, 186e8dcf2d1SChristoph Hellwig char *options) 187e8dcf2d1SChristoph Hellwig { 188e8dcf2d1SChristoph Hellwig enum { Unset, Add, Removeall } action = Unset; 189e8dcf2d1SChristoph Hellwig unsigned int option_mask = 0, chance = 1; 190e8dcf2d1SChristoph Hellwig enum req_op op = REQ_OP_LAST; 191e8dcf2d1SChristoph Hellwig u64 start = 0, nr_sectors = 0; 192e8dcf2d1SChristoph Hellwig blk_status_t status = BLK_STS_OK; 193e8dcf2d1SChristoph Hellwig substring_t args[MAX_OPT_ARGS]; 194e8dcf2d1SChristoph Hellwig char *p; 195e8dcf2d1SChristoph Hellwig 196e8dcf2d1SChristoph Hellwig while ((p = strsep(&options, ",\n")) != NULL) { 197e8dcf2d1SChristoph Hellwig int error = 0; 198e8dcf2d1SChristoph Hellwig ssize_t token; 199e8dcf2d1SChristoph Hellwig 200e8dcf2d1SChristoph Hellwig if (!*p) 201e8dcf2d1SChristoph Hellwig continue; 202e8dcf2d1SChristoph Hellwig token = match_token(p, opt_tokens, args); 203e8dcf2d1SChristoph Hellwig option_mask |= token; 204e8dcf2d1SChristoph Hellwig switch (token) { 205e8dcf2d1SChristoph Hellwig case Opt_add: 206e8dcf2d1SChristoph Hellwig if (action != Unset) 207e8dcf2d1SChristoph Hellwig return -EINVAL; 208e8dcf2d1SChristoph Hellwig action = Add; 209e8dcf2d1SChristoph Hellwig break; 210e8dcf2d1SChristoph Hellwig case Opt_removeall: 211e8dcf2d1SChristoph Hellwig if (action != Unset) 212e8dcf2d1SChristoph Hellwig return -EINVAL; 213e8dcf2d1SChristoph Hellwig action = Removeall; 214e8dcf2d1SChristoph Hellwig break; 215e8dcf2d1SChristoph Hellwig case Opt_op: 216e8dcf2d1SChristoph Hellwig error = match_op(args, &op); 217e8dcf2d1SChristoph Hellwig break; 218e8dcf2d1SChristoph Hellwig case Opt_start: 219e8dcf2d1SChristoph Hellwig error = match_u64(args, &start); 220e8dcf2d1SChristoph Hellwig break; 221e8dcf2d1SChristoph Hellwig case Opt_nr_sectors: 222e8dcf2d1SChristoph Hellwig error = match_u64(args, &nr_sectors); 223e8dcf2d1SChristoph Hellwig break; 224e8dcf2d1SChristoph Hellwig case Opt_status: 225e8dcf2d1SChristoph Hellwig error = match_status(args, &status); 226e8dcf2d1SChristoph Hellwig break; 227e8dcf2d1SChristoph Hellwig case Opt_chance: 228e8dcf2d1SChristoph Hellwig error = match_uint(args, &chance); 229e8dcf2d1SChristoph Hellwig if (!error && chance == 0) 230e8dcf2d1SChristoph Hellwig error = -EINVAL; 231e8dcf2d1SChristoph Hellwig break; 232e8dcf2d1SChristoph Hellwig default: 233e8dcf2d1SChristoph Hellwig pr_warn("unknown parameter or missing value '%s'\n", p); 234e8dcf2d1SChristoph Hellwig error = -EINVAL; 235e8dcf2d1SChristoph Hellwig } 236e8dcf2d1SChristoph Hellwig if (error) 237e8dcf2d1SChristoph Hellwig return error; 238e8dcf2d1SChristoph Hellwig } 239e8dcf2d1SChristoph Hellwig 240e8dcf2d1SChristoph Hellwig switch (action) { 241e8dcf2d1SChristoph Hellwig case Add: 242e8dcf2d1SChristoph Hellwig return error_inject_add(disk, op, start, nr_sectors, status, 243e8dcf2d1SChristoph Hellwig chance); 244e8dcf2d1SChristoph Hellwig case Removeall: 245e8dcf2d1SChristoph Hellwig if (option_mask & ~Opt_removeall) 246e8dcf2d1SChristoph Hellwig return -EINVAL; 247e8dcf2d1SChristoph Hellwig error_inject_removeall(disk); 248e8dcf2d1SChristoph Hellwig return 0; 249e8dcf2d1SChristoph Hellwig default: 250e8dcf2d1SChristoph Hellwig return -EINVAL; 251e8dcf2d1SChristoph Hellwig } 252e8dcf2d1SChristoph Hellwig } 253e8dcf2d1SChristoph Hellwig 254e8dcf2d1SChristoph Hellwig static ssize_t blk_error_injection_write(struct file *file, 255e8dcf2d1SChristoph Hellwig const char __user *ubuf, size_t count, loff_t *pos) 256e8dcf2d1SChristoph Hellwig { 257e8dcf2d1SChristoph Hellwig struct gendisk *disk = file_inode(file)->i_private; 258e8dcf2d1SChristoph Hellwig char *options; 259e8dcf2d1SChristoph Hellwig int error; 260e8dcf2d1SChristoph Hellwig 261e8dcf2d1SChristoph Hellwig options = memdup_user_nul(ubuf, count); 262e8dcf2d1SChristoph Hellwig if (IS_ERR(options)) 263e8dcf2d1SChristoph Hellwig return PTR_ERR(options); 264e8dcf2d1SChristoph Hellwig error = blk_error_injection_parse_options(disk, options); 265e8dcf2d1SChristoph Hellwig kfree(options); 266e8dcf2d1SChristoph Hellwig 267e8dcf2d1SChristoph Hellwig if (error) 268e8dcf2d1SChristoph Hellwig return error; 269e8dcf2d1SChristoph Hellwig return count; 270e8dcf2d1SChristoph Hellwig } 271e8dcf2d1SChristoph Hellwig 272e8dcf2d1SChristoph Hellwig static int blk_error_injection_show(struct seq_file *s, void *private) 273e8dcf2d1SChristoph Hellwig { 274e8dcf2d1SChristoph Hellwig struct gendisk *disk = s->private; 275e8dcf2d1SChristoph Hellwig struct blk_error_inject *inj; 276e8dcf2d1SChristoph Hellwig 277e8dcf2d1SChristoph Hellwig rcu_read_lock(); 278e8dcf2d1SChristoph Hellwig list_for_each_entry_rcu(inj, &disk->error_injection_list, entry) { 279e8dcf2d1SChristoph Hellwig seq_printf(s, "%llu:%llu status=%s,chance=%u", 280e8dcf2d1SChristoph Hellwig inj->start, inj->end, 281e8dcf2d1SChristoph Hellwig blk_status_to_tag(inj->status), inj->chance); 282e8dcf2d1SChristoph Hellwig seq_putc(s, '\n'); 283e8dcf2d1SChristoph Hellwig } 284e8dcf2d1SChristoph Hellwig rcu_read_unlock(); 285e8dcf2d1SChristoph Hellwig return 0; 286e8dcf2d1SChristoph Hellwig } 287e8dcf2d1SChristoph Hellwig 288e8dcf2d1SChristoph Hellwig static int blk_error_injection_open(struct inode *inode, struct file *file) 289e8dcf2d1SChristoph Hellwig { 290e8dcf2d1SChristoph Hellwig return single_open(file, blk_error_injection_show, inode->i_private); 291e8dcf2d1SChristoph Hellwig } 292e8dcf2d1SChristoph Hellwig 293e8dcf2d1SChristoph Hellwig static int blk_error_injection_release(struct inode *inode, struct file *file) 294e8dcf2d1SChristoph Hellwig { 295e8dcf2d1SChristoph Hellwig return single_release(inode, file); 296e8dcf2d1SChristoph Hellwig } 297e8dcf2d1SChristoph Hellwig 298e8dcf2d1SChristoph Hellwig static const struct file_operations blk_error_injection_fops = { 299e8dcf2d1SChristoph Hellwig .owner = THIS_MODULE, 300e8dcf2d1SChristoph Hellwig .write = blk_error_injection_write, 301e8dcf2d1SChristoph Hellwig .read = seq_read, 302e8dcf2d1SChristoph Hellwig .open = blk_error_injection_open, 303e8dcf2d1SChristoph Hellwig .release = blk_error_injection_release, 304e8dcf2d1SChristoph Hellwig }; 305e8dcf2d1SChristoph Hellwig 306e8dcf2d1SChristoph Hellwig void blk_error_injection_init(struct gendisk *disk) 307e8dcf2d1SChristoph Hellwig { 308e8dcf2d1SChristoph Hellwig debugfs_create_file("error_injection", 0600, disk->queue->debugfs_dir, 309e8dcf2d1SChristoph Hellwig disk, &blk_error_injection_fops); 310e8dcf2d1SChristoph Hellwig } 311e8dcf2d1SChristoph Hellwig 312e8dcf2d1SChristoph Hellwig void blk_error_injection_exit(struct gendisk *disk) 313e8dcf2d1SChristoph Hellwig { 314e8dcf2d1SChristoph Hellwig error_inject_removeall(disk); 315e8dcf2d1SChristoph Hellwig } 316