From patchwork Sat Aug 5 15:51:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shaohua Li X-Patchwork-Id: 9883123 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id F048460360 for ; Sat, 5 Aug 2017 15:52:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ED94E27C05 for ; Sat, 5 Aug 2017 15:52:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E219A28866; Sat, 5 Aug 2017 15:52:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3F30427C05 for ; Sat, 5 Aug 2017 15:52:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752413AbdHEPv5 (ORCPT ); Sat, 5 Aug 2017 11:51:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:34486 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752081AbdHEPvx (ORCPT ); Sat, 5 Aug 2017 11:51:53 -0400 Received: from shli-virt.localdomain (unknown [199.201.64.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id D528922DA7; Sat, 5 Aug 2017 15:51:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D528922DA7 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=kernel.org Authentication-Results: mail.kernel.org; spf=fail smtp.mailfrom=shli@fb.com From: Shaohua Li To: linux-block@vger.kernel.org, linux-raid@vger.kernel.org Cc: kernel-team@fb.com, Kyungchan Koh , Kyungchan Koh Subject: [PATCH 1/5] testb: add interface Date: Sat, 5 Aug 2017 08:51:45 -0700 Message-Id: X-Mailer: git-send-email 2.11.0 In-Reply-To: References: In-Reply-To: References: Sender: linux-block-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-block@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kyungchan Koh 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 Signed-off-by: Shaohua Li --- 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 +#include +#include +#include +#include + +/* + * 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 , Shaohua Li "); +MODULE_LICENSE("GPL");