Commit bb4c19e0 authored by Akinobu Mita's avatar Akinobu Mita Committed by Jens Axboe
Browse files

block: null_blk: make fault-injection dynamically configurable per device



The null_blk driver has multiple driver-specific fault injection
mechanisms.  Each fault injection configuration can only be specified by a
module parameter and cannot be reconfigured without reloading the driver.
Also, each configuration is common to all devices and is initialized every
time a new device is added.

This change adds the following subdirectories for each null_blk device.

/sys/kernel/config/nullb/<disk>/timeout_inject
/sys/kernel/config/nullb/<disk>/requeue_inject
/sys/kernel/config/nullb/<disk>/init_hctx_fault_inject

Each fault injection attribute can be dynamically set per device by a
corresponding file in these directories.

Signed-off-by: default avatarAkinobu Mita <akinobu.mita@gmail.com>
Link: https://lore.kernel.org/r/20230327143733.14599-3-akinobu.mita@gmail.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 4668c7a2
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -52,6 +52,14 @@ Available fault injection capabilities
  status code is NVME_SC_INVALID_OPCODE with no retry. The status code and
  retry flag can be set via the debugfs.

- Null test block driver fault injection

  inject IO timeouts by setting config items under
  /sys/kernel/config/nullb/<disk>/timeout_inject,
  inject requeue requests by setting config items under
  /sys/kernel/config/nullb/<disk>/requeue_inject, and
  inject init_hctx() errors by setting config items under
  /sys/kernel/config/nullb/<disk>/init_hctx_fault_inject.

Configure fault-injection capabilities behavior
-----------------------------------------------
+1 −1
Original line number Diff line number Diff line
@@ -9,4 +9,4 @@ config BLK_DEV_NULL_BLK

config BLK_DEV_NULL_BLK_FAULT_INJECTION
	bool "Support fault injection for Null test block driver"
	depends on BLK_DEV_NULL_BLK && FAULT_INJECTION
	depends on BLK_DEV_NULL_BLK && FAULT_INJECTION_CONFIGFS
+72 −21
Original line number Diff line number Diff line
@@ -250,7 +250,7 @@ static void null_free_device_storage(struct nullb_device *dev, bool is_cache);

static inline struct nullb_device *to_nullb_device(struct config_item *item)
{
	return item ? container_of(item, struct nullb_device, item) : NULL;
	return item ? container_of(to_config_group(item), struct nullb_device, group) : NULL;
}

static inline ssize_t nullb_device_uint_attr_show(unsigned int val, char *page)
@@ -593,8 +593,29 @@ static const struct config_item_type nullb_device_type = {
	.ct_owner	= THIS_MODULE,
};

#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION

static void nullb_add_fault_config(struct nullb_device *dev)
{
	fault_config_init(&dev->timeout_config, "timeout_inject");
	fault_config_init(&dev->requeue_config, "requeue_inject");
	fault_config_init(&dev->init_hctx_fault_config, "init_hctx_fault_inject");

	configfs_add_default_group(&dev->timeout_config.group, &dev->group);
	configfs_add_default_group(&dev->requeue_config.group, &dev->group);
	configfs_add_default_group(&dev->init_hctx_fault_config.group, &dev->group);
}

#else

static void nullb_add_fault_config(struct nullb_device *dev)
{
}

#endif

static struct
config_item *nullb_group_make_item(struct config_group *group, const char *name)
config_group *nullb_group_make_group(struct config_group *group, const char *name)
{
	struct nullb_device *dev;

@@ -605,9 +626,10 @@ config_item *nullb_group_make_item(struct config_group *group, const char *name)
	if (!dev)
		return ERR_PTR(-ENOMEM);

	config_item_init_type_name(&dev->item, name, &nullb_device_type);
	config_group_init_type_name(&dev->group, name, &nullb_device_type);
	nullb_add_fault_config(dev);

	return &dev->item;
	return &dev->group;
}

static void
@@ -645,7 +667,7 @@ static struct configfs_attribute *nullb_group_attrs[] = {
};

static struct configfs_group_operations nullb_group_ops = {
	.make_item	= nullb_group_make_item,
	.make_group	= nullb_group_make_group,
	.drop_item	= nullb_group_drop_item,
};

@@ -676,6 +698,13 @@ static struct nullb_device *null_alloc_dev(void)
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return NULL;

#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
	dev->timeout_config.attr = null_timeout_attr;
	dev->requeue_config.attr = null_requeue_attr;
	dev->init_hctx_fault_config.attr = null_init_hctx_attr;
#endif

	INIT_RADIX_TREE(&dev->data, GFP_ATOMIC);
	INIT_RADIX_TREE(&dev->cache, GFP_ATOMIC);
	if (badblocks_init(&dev->badblocks, 0)) {
@@ -1515,24 +1544,48 @@ static void null_submit_bio(struct bio *bio)
	null_handle_cmd(alloc_cmd(nq, bio), sector, nr_sectors, bio_op(bio));
}

#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION

static bool should_timeout_request(struct request *rq)
{
	struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
	struct nullb_device *dev = cmd->nq->dev;

	return should_fail(&dev->timeout_config.attr, 1);
}

static bool should_requeue_request(struct request *rq)
{
	struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq);
	struct nullb_device *dev = cmd->nq->dev;

	return should_fail(&dev->requeue_config.attr, 1);
}

static bool should_init_hctx_fail(struct nullb_device *dev)
{
	return should_fail(&dev->init_hctx_fault_config.attr, 1);
}

#else

static bool should_timeout_request(struct request *rq)
{
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
	if (g_timeout_str[0])
		return should_fail(&null_timeout_attr, 1);
#endif
	return false;
}

static bool should_requeue_request(struct request *rq)
{
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
	if (g_requeue_str[0])
		return should_fail(&null_requeue_attr, 1);
#endif
	return false;
}

static bool should_init_hctx_fail(struct nullb_device *dev)
{
	return false;
}

#endif

static void null_map_queues(struct blk_mq_tag_set *set)
{
	struct nullb *nullb = set->driver_data;
@@ -1729,10 +1782,8 @@ static int null_init_hctx(struct blk_mq_hw_ctx *hctx, void *driver_data,
	struct nullb *nullb = hctx->queue->queuedata;
	struct nullb_queue *nq;

#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
	if (g_init_hctx_str[0] && should_fail(&null_init_hctx_attr, 1))
	if (should_init_hctx_fail(nullb->dev))
		return -EFAULT;
#endif

	nq = &nullb->queues[hctx_idx];
	hctx->driver_data = nq;
@@ -2052,9 +2103,6 @@ static int null_add_dev(struct nullb_device *dev)
		if (rv)
			goto out_cleanup_queues;

		if (!null_setup_fault())
			goto out_cleanup_tags;

		nullb->tag_set->timeout = 5 * HZ;
		nullb->disk = blk_mq_alloc_disk(nullb->tag_set, nullb);
		if (IS_ERR(nullb->disk)) {
@@ -2116,10 +2164,10 @@ static int null_add_dev(struct nullb_device *dev)

	null_config_discard(nullb);

	if (config_item_name(&dev->item)) {
	if (config_item_name(&dev->group.cg_item)) {
		/* Use configfs dir name as the device name */
		snprintf(nullb->disk_name, sizeof(nullb->disk_name),
			 "%s", config_item_name(&dev->item));
			 "%s", config_item_name(&dev->group.cg_item));
	} else {
		sprintf(nullb->disk_name, "nullb%d", nullb->index);
	}
@@ -2219,6 +2267,9 @@ static int __init null_init(void)
		g_home_node = NUMA_NO_NODE;
	}

	if (!null_setup_fault())
		return -EINVAL;

	if (g_queue_mode == NULL_Q_RQ) {
		pr_err("legacy IO path is no longer available\n");
		return -EINVAL;
+6 −1
Original line number Diff line number Diff line
@@ -69,7 +69,12 @@ enum {

struct nullb_device {
	struct nullb *nullb;
	struct config_item item;
	struct config_group group;
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
	struct fault_config timeout_config;
	struct fault_config requeue_config;
	struct fault_config init_hctx_fault_config;
#endif
	struct radix_tree_root data; /* data stored in the disk */
	struct radix_tree_root cache; /* disk cache data */
	unsigned long flags; /* device flags */