From: Kyungchan Koh <kkc6196@xxxxxx> The testb block device driver is intended for testing, so configuration should be easy. We are using configfs here, which can be configured with a shell script. Basically the the testb will be configured as: mount the configfs fs as usual: mount -t configfs none /mnt Checking which features the driver supports: cat /mnt/testb/features The 'features' attribute is for future extension. We probably will add new features into the driver, userspace can check this attribute to find the supported features. Create a device: mkdir /mnt/testb/a Then configure the device by setting attributes under /mnt/testb/a size: disk size in bytes blocksize: sector size, mush be multiples of 512, and maximum is 4k discard: if the disk supports discard nr_queues: how many queues supported in the disk q_depth: queue depth of the disk Then power on the device: echo 1 > /mnt/testb/a/power this will create a disk, which should be /dev/testb_a We don't allow change attributes after the device poweron once so far We can remove the disk by writing 0 to the 'power' attribute. 'rmdir /mnt/testb/a' will delete the device, which also remove the disk if the disk isn't removed yet. Signed-off-by: Kyungchan Koh <kkc6196@xxxxxx> Signed-off-by: Shaohua Li <shli@xxxxxx> --- drivers/block/Kconfig | 8 ++ drivers/block/Makefile | 2 + drivers/block/test_blk.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 drivers/block/test_blk.c diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 8ddc982..2da5d02 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -15,6 +15,14 @@ menuconfig BLK_DEV if BLK_DEV +config BLK_DEV_TEST_BLK + tristate "A test block driver" + depends on CONFIGFS_FS + help + A memory-based block device driver for testing purposes. Configurable + through configFS. Useful features such as bandwidth throttling, + writeback cache, and power loss emulation. + config BLK_DEV_NULL_BLK tristate "Null test block driver" diff --git a/drivers/block/Makefile b/drivers/block/Makefile index ec8c368..f0f0d21 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o +obj-$(CONFIG_BLK_DEV_TEST_BLK) += test_blk.o + obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/ obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ diff --git a/drivers/block/test_blk.c b/drivers/block/test_blk.c new file mode 100644 index 0000000..93e8ec2 --- /dev/null +++ b/drivers/block/test_blk.c @@ -0,0 +1,261 @@ +/* + * test_blk.c - A memory-based test block device driver. + * + * Copyright (c) 2017 Facebook, Inc. + * + * Parts derived from drivers/block/null_blk.c and drivers/block/brd.c, + * copyright to respective owners. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/configfs.h> +#include <linux/radix-tree.h> + +/* + * Status flags for testb_device. + * + * CONFIGURED: Device has been configured and turned on. Cannot reconfigure. + * UP: Device is currently on and visible in userspace. + */ +enum testb_device_flags { + TESTB_DEV_FL_CONFIGURED = 0, + TESTB_DEV_FL_UP = 1, +}; + +/* + * testb_device represents the characteristics of a virtual device. + * + * @item: The struct used by configfs to represent items in fs. + * @lock: Protect data of the device + * @pages: The storage of the device. + * @flags: TEST_DEV_FL_ flags to indicate various status. + * + * @power: 1 means on; 0 means off. + * @size: The size of the disk (in bytes). + * @blocksize: The block size for the request queue. + * @nr_queues: The number of queues. + * @q_depth: The depth of each queue. + * @discard: If enable discard + */ +struct testb_device { + struct config_item item; + spinlock_t lock; + struct radix_tree_root pages; + unsigned long flags; + + uint power; + u64 size; + uint blocksize; + uint nr_queues; + uint q_depth; + uint discard; +}; + +static inline struct testb_device *to_testb_device(struct config_item *item) +{ + return item ? container_of(item, struct testb_device, item) : NULL; +} + +static inline ssize_t testb_device_uint_attr_show(uint val, char *page) +{ + return snprintf(page, PAGE_SIZE, "%u\n", val); +} + +static ssize_t +testb_device_uint_attr_store(uint *val, const char *page, size_t count) +{ + uint tmp; + int result; + + result = kstrtouint(page, 0, &tmp); + if (result) + return result; + + *val = tmp; + return count; +} + +static inline ssize_t testb_device_u64_attr_show(u64 val, char *page) +{ + return snprintf(page, PAGE_SIZE, "%llu\n", val); +} + +static ssize_t +testb_device_u64_attr_store(u64 *val, const char *page, size_t count) +{ + int result; + u64 tmp; + + result = kstrtoull(page, 0, &tmp); + if (result) + return result; + + *val = tmp; + return count; +} + +/* The following macro should only be used with TYPE = {uint, u64}. */ +#define TESTB_DEVICE_ATTR(NAME, TYPE) \ +static ssize_t \ +testb_device_##NAME##_show(struct config_item *item, char *page) \ +{ \ + return testb_device_##TYPE##_attr_show( \ + to_testb_device(item)->NAME, page); \ +} \ +static ssize_t \ +testb_device_##NAME##_store(struct config_item *item, const char *page, \ + size_t count) \ +{ \ + if (test_bit(TESTB_DEV_FL_CONFIGURED, &to_testb_device(item)->flags)) \ + return -EBUSY; \ + return testb_device_##TYPE##_attr_store( \ + &to_testb_device(item)->NAME, page, count); \ +} \ +CONFIGFS_ATTR(testb_device_, NAME); + +TESTB_DEVICE_ATTR(size, u64); +TESTB_DEVICE_ATTR(blocksize, uint); +TESTB_DEVICE_ATTR(nr_queues, uint); +TESTB_DEVICE_ATTR(q_depth, uint); +TESTB_DEVICE_ATTR(discard, uint); + +static ssize_t testb_device_power_show(struct config_item *item, char *page) +{ + return testb_device_uint_attr_show(to_testb_device(item)->power, page); +} + +static ssize_t testb_device_power_store(struct config_item *item, + const char *page, size_t count) +{ + struct testb_device *t_dev = to_testb_device(item); + uint newp = 0; + ssize_t ret; + + ret = testb_device_uint_attr_store(&newp, page, count); + if (ret < 0) + return ret; + + if (!t_dev->power && newp) { + if (test_and_set_bit(TESTB_DEV_FL_UP, &t_dev->flags)) + return count; + + set_bit(TESTB_DEV_FL_CONFIGURED, &t_dev->flags); + t_dev->power = newp; + } else if (to_testb_device(item)->power && !newp) { + t_dev->power = newp; + clear_bit(TESTB_DEV_FL_UP, &t_dev->flags); + } + + return count; +} + +CONFIGFS_ATTR(testb_device_, power); + +static struct configfs_attribute *testb_device_attrs[] = { + &testb_device_attr_power, + &testb_device_attr_size, + &testb_device_attr_blocksize, + &testb_device_attr_nr_queues, + &testb_device_attr_q_depth, + &testb_device_attr_discard, + NULL, +}; + +static void testb_device_release(struct config_item *item) +{ + kfree(to_testb_device(item)); +} + +static struct configfs_item_operations testb_device_ops = { + .release = testb_device_release, +}; + +static struct config_item_type testb_device_type = { + .ct_item_ops = &testb_device_ops, + .ct_attrs = testb_device_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct +config_item *testb_group_make_item(struct config_group *group, const char *name) +{ + struct testb_device *t_dev; + + t_dev = kzalloc(sizeof(struct testb_device), GFP_KERNEL); + if (!t_dev) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&t_dev->item, name, &testb_device_type); + + /* Initialize attributes with default values. */ + t_dev->size = 1024 * 1024 * 1024ULL; + t_dev->blocksize = 512; + t_dev->nr_queues = 2; + t_dev->q_depth = 64; + t_dev->discard = 1; + + return &t_dev->item; +} + +static void +testb_group_drop_item(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static ssize_t memb_group_features_show(struct config_item *item, char *page) +{ + return snprintf(page, PAGE_SIZE, "\n"); +} + +CONFIGFS_ATTR_RO(memb_group_, features); + +static struct configfs_attribute *testb_group_attrs[] = { + &memb_group_attr_features, + NULL, +}; + +static struct configfs_group_operations testb_group_ops = { + .make_item = testb_group_make_item, + .drop_item = testb_group_drop_item, +}; + +static struct config_item_type testb_group_type = { + .ct_group_ops = &testb_group_ops, + .ct_attrs = testb_group_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem testb_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "testb", + .ci_type = &testb_group_type, + }, + }, +}; + +static int __init testb_init(void) +{ + int ret = 0; + struct configfs_subsystem *subsys = &testb_subsys; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + + ret = configfs_register_subsystem(subsys); + return ret; +} + +static void __exit testb_exit(void) +{ + configfs_unregister_subsystem(&testb_subsys); +} + +module_init(testb_init); +module_exit(testb_exit); + +MODULE_AUTHOR("Will Koh <kkc6196@xxxxxx>, Shaohua Li <shli@xxxxxx>"); +MODULE_LICENSE("GPL"); -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-raid" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html