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