* Re: [PATCH v2 1/4] block: Add concurrent positioning ranges support [not found] <202107241107.Xndcu52J-lkp@intel.com> @ 2021-07-24 9:36 ` kernel test robot 0 siblings, 0 replies; 2+ messages in thread From: kernel test robot @ 2021-07-24 9:36 UTC (permalink / raw) To: Damien Le Moal, linux-block, Jens Axboe, linux-scsi, Martin K . Petersen, linux-ide, Hannes Reinecke Cc: kbuild-all [-- Attachment #1: Type: text/plain, Size: 16180 bytes --] Hi Damien, I love your patch! Perhaps something to improve: [auto build test WARNING on block/for-next] [also build test WARNING on scsi/for-next mkp-scsi/for-next v5.14-rc2 next-20210723] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Damien-Le-Moal/Initial-support-for-multi-actuator-HDDs/20210723-092320 base: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next :::::: branch date: 26 hours ago :::::: commit date: 26 hours ago config: x86_64-randconfig-c001-20210723 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 9625ca5b602616b2f5584e8a49ba93c52c141e40) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install x86_64 cross compiling tool for clang build # apt-get install binutils-x86-64-linux-gnu # https://github.com/0day-ci/linux/commit/5bede30cfe0b7db2174fbf8393a311fb21baa66a git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Damien-Le-Moal/Initial-support-for-multi-actuator-HDDs/20210723-092320 git checkout 5bede30cfe0b7db2174fbf8393a311fb21baa66a # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross clang-analyzer If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> clang-analyzer warnings: (new ones prefixed by >>) ^~~~~~~~~~~~ fs/fs-writeback.c:1198:6: note: Left side of '||' is false if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) { ^ fs/fs-writeback.c:1198:23: note: Assuming the condition is true if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/fs-writeback.c:1198:2: note: Taking true branch if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) { ^ fs/fs-writeback.c:1200:3: note: Calling 'wb_queue_work' wb_queue_work(&bdi->wb, base_work); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/fs-writeback.c:163:6: note: Assuming field 'done' is null if (work->done) ^~~~~~~~~~ fs/fs-writeback.c:163:2: note: Taking false branch if (work->done) ^ fs/fs-writeback.c:168:6: note: Assuming the condition is false if (test_bit(WB_registered, &wb->state)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/fs-writeback.c:168:2: note: Taking false branch if (test_bit(WB_registered, &wb->state)) { ^ fs/fs-writeback.c:172:3: note: Calling 'finish_writeback_work' finish_writeback_work(wb, work); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/fs-writeback.c:147:6: note: Assuming field 'auto_free' is not equal to 0 if (work->auto_free) ^~~~~~~~~~~~~~~ fs/fs-writeback.c:147:2: note: Taking true branch if (work->auto_free) ^ fs/fs-writeback.c:148:3: note: Argument to kfree() is the address of the local variable 'work', which is not memory allocated by malloc() kfree(work); ^ ~~~~ Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 2 warnings generated. Suppressed 2 warnings (2 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 2 warnings generated. Suppressed 2 warnings (2 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 2 warnings generated. Suppressed 2 warnings (2 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 4 warnings generated. Suppressed 4 warnings (4 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 3 warnings generated. >> block/blk-cranges.c:206:32: warning: Access to field 'nr_ranges' results in a dereference of a null pointer (loaded from variable 'new') [clang-analyzer-core.NullDereference] if (!old || old->nr_ranges != new->nr_ranges) ^ block/blk-cranges.c:251:19: note: Assuming 'cr' is non-null if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { ^ include/asm-generic/bug.h:104:25: note: expanded from macro 'WARN_ON_ONCE' int __ret_warn_on = !!(condition); \ ^~~~~~~~~ block/blk-cranges.c:251:19: note: Left side of '&&' is true if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { ^ block/blk-cranges.c:251:25: note: Assuming field 'nr_ranges' is not equal to 0 if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { ^ include/asm-generic/bug.h:104:25: note: expanded from macro 'WARN_ON_ONCE' int __ret_warn_on = !!(condition); \ ^~~~~~~~~ block/blk-cranges.c:251:6: note: Taking false branch if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { ^ include/asm-generic/bug.h:105:2: note: expanded from macro 'WARN_ON_ONCE' if (unlikely(__ret_warn_on)) \ ^ block/blk-cranges.c:251:2: note: Taking false branch if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { ^ block/blk-cranges.c:259:6: note: 'cr' is non-null if (cr && !blk_check_ranges(disk, cr)) { ^~ block/blk-cranges.c:259:6: note: Left side of '&&' is true block/blk-cranges.c:259:12: note: Assuming the condition is true if (cr && !blk_check_ranges(disk, cr)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~ block/blk-cranges.c:259:2: note: Taking true branch if (cr && !blk_check_ranges(disk, cr)) { ^ block/blk-cranges.c:261:3: note: Null pointer value stored to 'cr' cr = NULL; ^~~~~~~~~ block/blk-cranges.c:264:33: note: Passing null pointer value via 2nd parameter 'new' if (!blk_cranges_changed(disk, cr)) { ^~ block/blk-cranges.c:264:7: note: Calling 'blk_cranges_changed' if (!blk_cranges_changed(disk, cr)) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ block/blk-cranges.c:206:6: note: Assuming 'old' is non-null if (!old || old->nr_ranges != new->nr_ranges) ^~~~ block/blk-cranges.c:206:6: note: Left side of '||' is false block/blk-cranges.c:206:32: note: Access to field 'nr_ranges' results in a dereference of a null pointer (loaded from variable 'new') if (!old || old->nr_ranges != new->nr_ranges) ^~~ Suppressed 2 warnings (2 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 3 warnings generated. Suppressed 3 warnings (3 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 3 warnings generated. Suppressed 3 warnings (3 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 2 warnings generated. Suppressed 2 warnings (2 in non-user code). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 8 warnings generated. drivers/char/tpm/eventlog/tpm1.c:276:2: warning: Value stored to 'len' is never read [clang-analyzer-deadcode.DeadStores] len += get_event_name(eventname, event, event_entry); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/char/tpm/eventlog/tpm1.c:276:2: note: Value stored to 'len' is never read len += get_event_name(eventname, event, event_entry); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Suppressed 7 warnings (5 in non-user code, 2 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. Suppressed 5 warnings (4 in non-user code, 1 with check filters). Use -header-filter=.* to display errors from all non-system headers. Use -system-headers to display errors from system headers as well. 5 warnings generated. vim +206 block/blk-cranges.c 5bede30cfe0b7d Damien Le Moal 2021-07-23 200 5bede30cfe0b7d Damien Le Moal 2021-07-23 201 static bool blk_cranges_changed(struct gendisk *disk, struct blk_cranges *new) 5bede30cfe0b7d Damien Le Moal 2021-07-23 202 { 5bede30cfe0b7d Damien Le Moal 2021-07-23 203 struct blk_cranges *old = disk->queue->cranges; 5bede30cfe0b7d Damien Le Moal 2021-07-23 204 int i; 5bede30cfe0b7d Damien Le Moal 2021-07-23 205 5bede30cfe0b7d Damien Le Moal 2021-07-23 @206 if (!old || old->nr_ranges != new->nr_ranges) 5bede30cfe0b7d Damien Le Moal 2021-07-23 207 return true; 5bede30cfe0b7d Damien Le Moal 2021-07-23 208 5bede30cfe0b7d Damien Le Moal 2021-07-23 209 for (i = 0; i < new->nr_ranges; i++) { 5bede30cfe0b7d Damien Le Moal 2021-07-23 210 if (old->ranges[i].sector != new->ranges[i].sector || 5bede30cfe0b7d Damien Le Moal 2021-07-23 211 old->ranges[i].nr_sectors != new->ranges[i].nr_sectors) 5bede30cfe0b7d Damien Le Moal 2021-07-23 212 return true; 5bede30cfe0b7d Damien Le Moal 2021-07-23 213 } 5bede30cfe0b7d Damien Le Moal 2021-07-23 214 5bede30cfe0b7d Damien Le Moal 2021-07-23 215 return false; 5bede30cfe0b7d Damien Le Moal 2021-07-23 216 } 5bede30cfe0b7d Damien Le Moal 2021-07-23 217 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 35526 bytes --] [-- Attachment #3: Attached Message Part --] [-- Type: text/plain, Size: 150 bytes --] _______________________________________________ kbuild mailing list -- kbuild@lists.01.org To unsubscribe send an email to kbuild-leave@lists.01.org ^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH v2 0/4] Initial support for multi-actuator HDDs @ 2021-07-23 1:21 Damien Le Moal 2021-07-23 1:21 ` [PATCH v2 1/4] block: Add concurrent positioning ranges support Damien Le Moal 0 siblings, 1 reply; 2+ messages in thread From: Damien Le Moal @ 2021-07-23 1:21 UTC (permalink / raw) To: linux-block, Jens Axboe, linux-scsi, Martin K . Petersen, linux-ide Cc: Hannes Reinecke Single LUN multi-actuator hard-disks are cappable to seek and execute multiple commands in parallel. This capability is exposed to the host using the Concurrent Positioning Ranges VPD page (SCSI) and Log (ATA). Each positioning range describes the contiguous set of LBAs that an actuator serves. This series adds support the scsi disk driver to retreive this information and advertize it to user space through sysfs. libata is also modified to handle ATA drives. The first patch adds the block layer plumbing to expose concurrent sector ranges of the device through sysfs as a sub-directory of the device sysfs queue directory. Patch 2 and 3 add support to sd and libata. Finally patch 4 documents the sysfs queue attributed changes. This series does not attempt in any way to optimize accesses to multi-actuator devices (e.g. block IO scheduler or filesystems). This initial support only exposes the actuators information to user space through sysfs. Changes from v1: * Moved libata-scsi hunk from patch 1 to patch 3 where it belongs * Fixed unintialized variable in patch 2 Reported-by: kernel test robot <lkp@intel.com> Reported-by: Dan Carpenter <dan.carpenter@oracle.com * Changed patch 3 adding struct ata_cpr_log to contain both the number of concurrent ranges and the array of concurrent ranges. * Added a note in the documentation (patch 4) about the unit used for the concurrent ranges attributes. Damien Le Moal (4): block: Add concurrent positioning ranges support scsi: sd: add concurrent positioning ranges support libata: support concurrent positioning ranges log doc: document sysfs queue/cranges attributes Documentation/block/queue-sysfs.rst | 30 ++- block/Makefile | 2 +- block/blk-cranges.c | 286 ++++++++++++++++++++++++++++ block/blk-sysfs.c | 13 ++ block/blk.h | 3 + drivers/ata/libata-core.c | 52 +++++ drivers/ata/libata-scsi.c | 48 ++++- drivers/scsi/sd.c | 81 ++++++++ drivers/scsi/sd.h | 1 + include/linux/ata.h | 1 + include/linux/blkdev.h | 29 +++ include/linux/libata.h | 15 ++ 12 files changed, 550 insertions(+), 11 deletions(-) create mode 100644 block/blk-cranges.c -- 2.31.1 ^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH v2 1/4] block: Add concurrent positioning ranges support 2021-07-23 1:21 [PATCH v2 0/4] Initial support for multi-actuator HDDs Damien Le Moal @ 2021-07-23 1:21 ` Damien Le Moal 0 siblings, 0 replies; 2+ messages in thread From: Damien Le Moal @ 2021-07-23 1:21 UTC (permalink / raw) To: linux-block, Jens Axboe, linux-scsi, Martin K . Petersen, linux-ide Cc: Hannes Reinecke The Concurrent Positioning Ranges VPD page (for SCSI) and Log (for ATA) contain parameters describing the number of sets of contiguous LBAs that can be served independently by a single LUN multi-actuator disk. This patch provides the blk_queue_set_cranges() function allowing a device driver to signal to the block layer that a disk has multiple actuators, each one serving a contiguous range of sectors. To describe the set of sector ranges representing the different actuators of a device, the data type struct blk_cranges is introduced. For a device with multiple actuators, a struct blk_cranges is attached to the device request queue by the blk_queue_set_cranges() function. The function blk_alloc_cranges() is provided for drivers to allocate this structure. The blk_cranges structure contains kobjects (struct kobject) to register with sysfs the set of sector ranges defined by a device. On initial device scan, this registration is done from blk_register_queue() using the block layer internal function blk_register_cranges(). If a driver calls blk_queue_set_cranges() for a registered queue, e.g. when a device is revalidated, blk_queue_set_cranges() will execute blk_register_cranges() to update the queue sysfs attribute files. The sysfs file structure created starts from the cranges sub-directory and contains the start sector and number of sectors served by an actuator, with the information for each actuator grouped in one directory per actuator. E.g. for a dual actuator drive, we have: $ tree /sys/block/sdk/queue/cranges/ /sys/block/sdk/queue/cranges/ |-- 0 | |-- nr_sectors | `-- sector `-- 1 |-- nr_sectors `-- sector For a regular single actuator device, the cranges directory does not exist. Device revalidation may lead to changes to this structure and to the attribute values. When manipulated, the queue sysfs_lock and sysfs_dir_lock are held for atomicity, similarly to how the blk-mq and elevator sysfs queue sub-directories are protected. The code related to the management of cranges is added in the new file block/blk-cranges.c. Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> --- block/Makefile | 2 +- block/blk-cranges.c | 286 +++++++++++++++++++++++++++++++++++++++++ block/blk-sysfs.c | 13 ++ block/blk.h | 3 + include/linux/blkdev.h | 29 +++++ 5 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 block/blk-cranges.c diff --git a/block/Makefile b/block/Makefile index bfbe4e13ca1e..e477e6ca9ea6 100644 --- a/block/Makefile +++ b/block/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-sysfs.o \ blk-lib.o blk-mq.o blk-mq-tag.o blk-stat.o \ blk-mq-sysfs.o blk-mq-cpumap.o blk-mq-sched.o ioctl.o \ genhd.o ioprio.o badblocks.o partitions/ blk-rq-qos.o \ - disk-events.o + disk-events.o blk-cranges.o obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BLK_SCSI_REQUEST) += scsi_ioctl.o diff --git a/block/blk-cranges.c b/block/blk-cranges.c new file mode 100644 index 000000000000..e8ff0d40a5c9 --- /dev/null +++ b/block/blk-cranges.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Block device concurrent positioning ranges. + * + * Copyright (C) 2021 Western Digital Corporation or its Affiliates. + */ +#include <linux/kernel.h> +#include <linux/blkdev.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include "blk.h" + +static ssize_t blk_crange_sector_show(struct blk_crange *cr, char *page) +{ + return sprintf(page, "%llu\n", cr->sector); +} + +static ssize_t blk_crange_nr_sectors_show(struct blk_crange *cr, char *page) +{ + return sprintf(page, "%llu\n", cr->nr_sectors); +} + +struct blk_crange_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct blk_crange *cr, char *page); +}; + +static struct blk_crange_sysfs_entry blk_crange_sector_entry = { + .attr = { .name = "sector", .mode = 0444 }, + .show = blk_crange_sector_show, +}; + +static struct blk_crange_sysfs_entry blk_crange_nr_sectors_entry = { + .attr = { .name = "nr_sectors", .mode = 0444 }, + .show = blk_crange_nr_sectors_show, +}; + +static struct attribute *blk_crange_attrs[] = { + &blk_crange_sector_entry.attr, + &blk_crange_nr_sectors_entry.attr, + NULL, +}; +ATTRIBUTE_GROUPS(blk_crange); + +static ssize_t blk_crange_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *page) +{ + struct blk_crange_sysfs_entry *entry; + struct blk_crange *cr; + struct request_queue *q; + ssize_t ret; + + entry = container_of(attr, struct blk_crange_sysfs_entry, attr); + cr = container_of(kobj, struct blk_crange, kobj); + q = cr->queue; + + mutex_lock(&q->sysfs_lock); + ret = entry->show(cr, page); + mutex_unlock(&q->sysfs_lock); + + return ret; +} + +static const struct sysfs_ops blk_crange_sysfs_ops = { + .show = blk_crange_sysfs_show, +}; + +/* + * Dummy release function to make kobj happy. + */ +static void blk_cranges_sysfs_nop_release(struct kobject *kobj) +{ +} + +static struct kobj_type blk_crange_ktype = { + .sysfs_ops = &blk_crange_sysfs_ops, + .default_groups = blk_crange_groups, + .release = blk_cranges_sysfs_nop_release, +}; + +static struct kobj_type blk_cranges_ktype = { + .release = blk_cranges_sysfs_nop_release, +}; + +/** + * blk_register_cranges - register with sysfs a set of concurrent ranges + * @disk: Target disk + * @new_cranges: New set of concurrent ranges + * + * Register with sysfs a set of concurrent ranges for @disk. If @new_cranges + * is not NULL, this set of concurrent ranges is registered and the + * old set specified by q->cranges is unregistered. Otherwise, q->cranges + * is registered if it is not already. + */ +int blk_register_cranges(struct gendisk *disk, struct blk_cranges *new_cranges) +{ + struct request_queue *q = disk->queue; + struct blk_cranges *cranges; + int i, ret; + + lockdep_assert_held(&q->sysfs_dir_lock); + lockdep_assert_held(&q->sysfs_lock); + + /* If a new range set is specified, unregister the old one */ + if (new_cranges) { + if (q->cranges) + blk_unregister_cranges(disk); + q->cranges = new_cranges; + } + + cranges = q->cranges; + if (!cranges) + return 0; + + /* + * At this point, q->cranges is the new set of sector ranges that needs + * to be registered with sysfs. + */ + WARN_ON(cranges->sysfs_registered); + ret = kobject_init_and_add(&cranges->kobj, &blk_cranges_ktype, + &q->kobj, "%s", "cranges"); + if (ret) + goto free; + + for (i = 0; i < cranges->nr_ranges; i++) { + cranges->ranges[i].queue = q; + ret = kobject_init_and_add(&cranges->ranges[i].kobj, + &blk_crange_ktype, &cranges->kobj, + "%d", i); + if (ret) + goto delete_obj; + } + + cranges->sysfs_registered = true; + + return 0; + +delete_obj: + while (--i >= 0) + kobject_del(&cranges->ranges[i].kobj); + kobject_del(&cranges->kobj); +free: + kfree(cranges); + q->cranges = NULL; + return ret; +} + +void blk_unregister_cranges(struct gendisk *disk) +{ + struct request_queue *q = disk->queue; + struct blk_cranges *cranges = q->cranges; + int i; + + lockdep_assert_held(&q->sysfs_dir_lock); + lockdep_assert_held(&q->sysfs_lock); + + if (!cranges) + return; + + if (cranges->sysfs_registered) { + for (i = 0; i < cranges->nr_ranges; i++) + kobject_del(&cranges->ranges[i].kobj); + kobject_del(&cranges->kobj); + } + + kfree(cranges); + q->cranges = NULL; +} + +static bool blk_check_ranges(struct gendisk *disk, struct blk_cranges *cr) +{ + sector_t capacity = get_capacity(disk); + sector_t min_sector = (sector_t)-1; + sector_t max_sector = 0; + int i; + + /* + * Sector ranges may overlap but should overall contain all sectors + * within the disk capacity. + */ + for (i = 0; i < cr->nr_ranges; i++) { + min_sector = min(min_sector, cr->ranges[i].sector); + max_sector = max(max_sector, cr->ranges[i].sector + + cr->ranges[i].nr_sectors); + } + + if (min_sector != 0 || max_sector < capacity) { + pr_warn("Invalid concurrent ranges: missing sectors\n"); + return false; + } + + if (max_sector > capacity) { + pr_warn("Invalid concurrent ranges: beyond capacity\n"); + return false; + } + + return true; +} + +static bool blk_cranges_changed(struct gendisk *disk, struct blk_cranges *new) +{ + struct blk_cranges *old = disk->queue->cranges; + int i; + + if (!old || old->nr_ranges != new->nr_ranges) + return true; + + for (i = 0; i < new->nr_ranges; i++) { + if (old->ranges[i].sector != new->ranges[i].sector || + old->ranges[i].nr_sectors != new->ranges[i].nr_sectors) + return true; + } + + return false; +} + +/** + * blk_alloc_cranges - Allocate a concurrent positioning range structure + * @disk: target disk + * @nr_ranges: Number of concurrent ranges + * + * Allocate a struct blk_cranges structure with @nr_ranges range descriptors. + */ +struct blk_cranges *blk_alloc_cranges(struct gendisk *disk, int nr_ranges) +{ + struct blk_cranges *cr; + + cr = kzalloc_node(struct_size(cr, ranges, nr_ranges), GFP_KERNEL, + disk->queue->node); + if (cr) + cr->nr_ranges = nr_ranges; + return cr; +} +EXPORT_SYMBOL_GPL(blk_alloc_cranges); + +/** + * blk_queue_set_cranges - Set a disk concurrent positioning ranges + * @disk: target disk + * @cr: concurrent ranges structure + * + * Set the concurrant positioning ranges information of the request queue + * of @disk to @cr. If @cr is NULL and the concurrent ranges structure + * already set, if any, is cleared. If there are no differences between + * @cr and the concurrent ranges structure already set, @cr is freed. + */ +void blk_queue_set_cranges(struct gendisk *disk, struct blk_cranges *cr) +{ + struct request_queue *q = disk->queue; + + if (WARN_ON_ONCE(cr && !cr->nr_ranges)) { + kfree(cr); + cr = NULL; + } + + mutex_lock(&q->sysfs_dir_lock); + mutex_lock(&q->sysfs_lock); + + if (cr && !blk_check_ranges(disk, cr)) { + kfree(cr); + cr = NULL; + } + + if (!blk_cranges_changed(disk, cr)) { + kfree(cr); + goto unlock; + } + + /* + * This may be called for a registered queue. E.g. during a device + * revalidation. If that is the case, we need to register the new set + * of concurrent ranges. If the queue is not already registered, the + * device request queue registration will register the ranges. + */ + if (blk_queue_registered(q)) { + blk_register_cranges(disk, cr); + } else { + swap(q->cranges, cr); + kfree(cr); + } + +unlock: + mutex_unlock(&q->sysfs_lock); + mutex_unlock(&q->sysfs_dir_lock); +} +EXPORT_SYMBOL_GPL(blk_queue_set_cranges); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 370d83c18057..aeac98ecc5a0 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -899,9 +899,21 @@ int blk_register_queue(struct gendisk *disk) } mutex_lock(&q->sysfs_lock); + + ret = blk_register_cranges(disk, NULL); + if (ret) { + mutex_unlock(&q->sysfs_lock); + mutex_unlock(&q->sysfs_dir_lock); + kobject_del(&q->kobj); + blk_trace_remove_sysfs(dev); + kobject_put(&dev->kobj); + return ret; + } + if (q->elevator) { ret = elv_register_queue(q, false); if (ret) { + blk_unregister_cranges(disk); mutex_unlock(&q->sysfs_lock); mutex_unlock(&q->sysfs_dir_lock); kobject_del(&q->kobj); @@ -985,6 +997,7 @@ void blk_unregister_queue(struct gendisk *disk) mutex_lock(&q->sysfs_lock); if (q->elevator) elv_unregister_queue(q); + blk_unregister_cranges(disk); mutex_unlock(&q->sysfs_lock); mutex_unlock(&q->sysfs_dir_lock); diff --git a/block/blk.h b/block/blk.h index 4b885c0f6708..650c0d87987c 100644 --- a/block/blk.h +++ b/block/blk.h @@ -368,4 +368,7 @@ extern struct device_attribute dev_attr_events; extern struct device_attribute dev_attr_events_async; extern struct device_attribute dev_attr_events_poll_msecs; +int blk_register_cranges(struct gendisk *disk, struct blk_cranges *new_cranges); +void blk_unregister_cranges(struct gendisk *disk); + #endif /* BLK_INTERNAL_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3177181c4326..cd58aa1090a3 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -378,6 +378,29 @@ static inline int blkdev_zone_mgmt_ioctl(struct block_device *bdev, #endif /* CONFIG_BLK_DEV_ZONED */ +/* + * Concurrent sector ranges: struct blk_crange describes range of + * contiguous sectors that can be served by independent resources on the + * device. The set of ranges defined in struct blk_cranges must overall + * include all sectors within the device capacity. + * For a device with multiple ranges, e.g. a single LUN multi-actuator HDD, + * requests targeting sectors in different ranges can be executed in parallel. + * A request can straddle a range boundary. + */ +struct blk_crange { + struct kobject kobj; + struct request_queue *queue; + sector_t sector; + sector_t nr_sectors; +}; + +struct blk_cranges { + struct kobject kobj; + bool sysfs_registered; + unsigned int nr_ranges; + struct blk_crange ranges[]; +}; + struct request_queue { struct request *last_merge; struct elevator_queue *elevator; @@ -570,6 +593,9 @@ struct request_queue { #define BLK_MAX_WRITE_HINTS 5 u64 write_hints[BLK_MAX_WRITE_HINTS]; + + /* Concurrent sector ranges */ + struct blk_cranges *cranges; }; /* Keep blk_queue_flag_name[] in sync with the definitions below */ @@ -1163,6 +1189,9 @@ extern void blk_queue_required_elevator_features(struct request_queue *q, extern bool blk_queue_can_use_dma_map_merging(struct request_queue *q, struct device *dev); +struct blk_cranges *blk_alloc_cranges(struct gendisk *disk, int nr_ranges); +void blk_queue_set_cranges(struct gendisk *disk, struct blk_cranges *cr); + /* * Number of physical segments as sent to the device. * -- 2.31.1 ^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2021-07-24 9:36 UTC | newest] Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <202107241107.Xndcu52J-lkp@intel.com> 2021-07-24 9:36 ` [PATCH v2 1/4] block: Add concurrent positioning ranges support kernel test robot 2021-07-23 1:21 [PATCH v2 0/4] Initial support for multi-actuator HDDs Damien Le Moal 2021-07-23 1:21 ` [PATCH v2 1/4] block: Add concurrent positioning ranges support Damien Le Moal
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).