diff mbox

[1/5] testb: add interface

Message ID c4ad20449f985430bc7f7af11b56ec776335a4c1.1501945859.git.shli@fb.com (mailing list archive)
State New, archived
Headers show

Commit Message

Shaohua Li Aug. 5, 2017, 3:51 p.m. UTC
From: Kyungchan Koh <kkc6196@fb.com>

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@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
 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 mbox

Patch

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@fb.com>, Shaohua Li <shli@fb.com>");
+MODULE_LICENSE("GPL");