From fb1ba406c451045f1063ace70086b4645d4e9d54 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:18 +0200 Subject: scsi: scsi_ioctl: Remove scsi_cmd_blk_ioctl() Open code scsi_cmd_blk_ioctl() in its two callers. Link: https://lore.kernel.org/r/20210724072033.1284840-10-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- include/linux/blkdev.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3177181c4326..19aa3d5429c0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -889,8 +889,6 @@ extern blk_status_t blk_insert_cloned_request(struct request_queue *q, int blk_rq_append_bio(struct request *rq, struct bio *bio); extern void blk_queue_split(struct bio **); extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int); -extern int scsi_cmd_blk_ioctl(struct block_device *, fmode_t, - unsigned int, void __user *); extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, unsigned int, void __user *); extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, -- cgit v1.2.3-71-gd317 From 4f07bfc56157ebc689ef54879e90c48a47294083 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:19 +0200 Subject: scsi: scsi_ioctl: Remove scsi_verify_blk_ioctl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manually verify that the device is not a partition and the caller has admin privŃ–leges at the beginning of the sr ioctl method and open code the trivial check for sd as well. Link: https://lore.kernel.org/r/20210724072033.1284840-11-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/scsi_ioctl.c | 12 ------------ drivers/scsi/sd.c | 8 ++------ drivers/scsi/sr.c | 6 +++--- include/linux/blkdev.h | 1 - 4 files changed, 5 insertions(+), 22 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index f8138438c56f..ca7b84452d9d 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -842,18 +842,6 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod } EXPORT_SYMBOL(scsi_cmd_ioctl); -int scsi_verify_blk_ioctl(struct block_device *bd, unsigned int cmd) -{ - if (bd && !bdev_is_partition(bd)) - return 0; - - if (capable(CAP_SYS_RAWIO)) - return 0; - - return -ENOIOCTLCMD; -} -EXPORT_SYMBOL(scsi_verify_blk_ioctl); - /** * scsi_req_init - initialize certain fields of a scsi_request structure * @req: Pointer to a scsi_request structure. diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d65bfe505e08..bcc4b1339e21 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1555,9 +1555,8 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, SCSI_LOG_IOCTL(1, sd_printk(KERN_INFO, sdkp, "sd_ioctl: disk=%s, " "cmd=0x%x\n", disk->disk_name, cmd)); - error = scsi_verify_blk_ioctl(bdev, cmd); - if (error < 0) - return error; + if (bdev_is_partition(bdev) && !capable(CAP_SYS_RAWIO)) + return -ENOIOCTLCMD; /* * If we are in the middle of error recovery, don't let anyone @@ -1583,9 +1582,6 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, case SCSI_IOCTL_GET_BUS_NUMBER: break; default: - error = scsi_verify_blk_ioctl(bdev, cmd); - if (error < 0) - return error; error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p); if (error != -ENOTTY) return error; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index b903e54c57fd..e6eadba4d638 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -562,6 +562,9 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, void __user *argp = (void __user *)arg; int ret; + if (bdev_is_partition(bdev) && !capable(CAP_SYS_RAWIO)) + return -ENOIOCTLCMD; + mutex_lock(&cd->lock); ret = scsi_ioctl_block_when_processing_errors(sdev, cmd, @@ -580,9 +583,6 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, case SCSI_IOCTL_GET_BUS_NUMBER: break; default: - ret = scsi_verify_blk_ioctl(bdev, cmd); - if (ret < 0) - goto put; ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, argp); if (ret != -ENOTTY) goto put; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 19aa3d5429c0..e2b972a85012 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -888,7 +888,6 @@ extern blk_status_t blk_insert_cloned_request(struct request_queue *q, struct request *rq); int blk_rq_append_bio(struct request *rq, struct bio *bio); extern void blk_queue_split(struct bio **); -extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int); extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, unsigned int, void __user *); extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, -- cgit v1.2.3-71-gd317 From 547e2f7093b19a993d76c249b4c3ec8af8127d09 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:21 +0200 Subject: scsi: block: Add a queue_max_bytes() helper Return the max_sectors value in bytes. Lifted from scsi_ioctl.c. Link: https://lore.kernel.org/r/20210724072033.1284840-13-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/scsi_ioctl.c | 13 ++----------- include/linux/blkdev.h | 5 +++++ 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index ca7b84452d9d..c3871529e283 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -68,18 +68,9 @@ static int sg_set_timeout(struct request_queue *q, int __user *p) return err; } -static int max_sectors_bytes(struct request_queue *q) -{ - unsigned int max_sectors = queue_max_sectors(q); - - max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9); - - return max_sectors << 9; -} - static int sg_get_reserved_size(struct request_queue *q, int __user *p) { - int val = min_t(int, q->sg_reserved_size, max_sectors_bytes(q)); + int val = min(q->sg_reserved_size, queue_max_bytes(q)); return put_user(val, p); } @@ -94,7 +85,7 @@ static int sg_set_reserved_size(struct request_queue *q, int __user *p) if (size < 0) return -EINVAL; - q->sg_reserved_size = min(size, max_sectors_bytes(q)); + q->sg_reserved_size = min_t(unsigned int, size, queue_max_bytes(q)); return 0; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e2b972a85012..9971796819ef 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1373,6 +1373,11 @@ static inline unsigned int queue_max_sectors(const struct request_queue *q) return q->limits.max_sectors; } +static inline unsigned int queue_max_bytes(struct request_queue *q) +{ + return min_t(unsigned int, queue_max_sectors(q), INT_MAX >> 9) << 9; +} + static inline unsigned int queue_max_hw_sectors(const struct request_queue *q) { return q->limits.max_hw_sectors; -- cgit v1.2.3-71-gd317 From 78011042684dfbb50f7060f4623793f7a5c74a01 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:23 +0200 Subject: scsi: bsg: Move bsg_scsi_ops to drivers/scsi/ Move the SCSI-specific bsg code in the SCSI midlayer instead of in the common bsg code. This just keeps the common bsg code block/ and also allows building it as a module. Link: https://lore.kernel.org/r/20210724072033.1284840-15-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/Kconfig | 23 ++---------- block/Makefile | 2 +- block/bsg.c | 95 +---------------------------------------------- drivers/scsi/Kconfig | 13 +++++++ drivers/scsi/Makefile | 1 + drivers/scsi/scsi_bsg.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_priv.h | 10 +++++ drivers/scsi/scsi_sysfs.c | 2 +- include/linux/blkdev.h | 2 +- include/linux/bsg.h | 11 ++---- 10 files changed, 129 insertions(+), 125 deletions(-) create mode 100644 drivers/scsi/scsi_bsg.c (limited to 'include/linux/blkdev.h') diff --git a/block/Kconfig b/block/Kconfig index fd732aede922..88aa88241795 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -35,29 +35,12 @@ config BLK_SCSI_REQUEST config BLK_CGROUP_RWSTAT bool -config BLK_DEV_BSG - bool "Block layer SG support v4" - default y - select BLK_SCSI_REQUEST - help - Saying Y here will enable generic SG (SCSI generic) v4 support - for any block device. - - Unlike SG v3 (aka block/scsi_ioctl.c drivers/scsi/sg.c), SG v4 - can handle complicated SCSI commands: tagged variable length cdbs - with bidirectional data transfers and generic request/response - protocols (e.g. Task Management Functions and SMP in Serial - Attached SCSI). - - This option is required by recent UDEV versions to properly - access device serial numbers, etc. - - If unsure, say Y. +config BLK_DEV_BSG_COMMON + tristate config BLK_DEV_BSGLIB bool "Block layer SG support v4 helper lib" - select BLK_DEV_BSG - select BLK_SCSI_REQUEST + select BLK_DEV_BSG_COMMON help Subsystems will normally enable this if needed. Users will not normally need to manually enable this. diff --git a/block/Makefile b/block/Makefile index bfbe4e13ca1e..f37d532c8da5 100644 --- a/block/Makefile +++ b/block/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-sysfs.o \ obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BLK_SCSI_REQUEST) += scsi_ioctl.o -obj-$(CONFIG_BLK_DEV_BSG) += bsg.o +obj-$(CONFIG_BLK_DEV_BSG_COMMON) += bsg.o obj-$(CONFIG_BLK_DEV_BSGLIB) += bsg-lib.o obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o obj-$(CONFIG_BLK_CGROUP_RWSTAT) += blk-cgroup-rwstat.o diff --git a/block/bsg.c b/block/bsg.c index df21df106d3b..3dbfd2c6aef3 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -15,9 +15,6 @@ #include #include -#include -#include -#include #include #define BSG_DESCRIPTION "Block layer SCSI generic (bsg) driver" @@ -54,86 +51,6 @@ static inline struct hlist_head *bsg_dev_idx_hash(int index) #define uptr64(val) ((void __user *)(uintptr_t)(val)) -static int bsg_scsi_check_proto(struct sg_io_v4 *hdr) -{ - if (hdr->protocol != BSG_PROTOCOL_SCSI || - hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD) - return -EINVAL; - return 0; -} - -static int bsg_scsi_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, - fmode_t mode) -{ - struct scsi_request *sreq = scsi_req(rq); - - if (hdr->dout_xfer_len && hdr->din_xfer_len) { - pr_warn_once("BIDI support in bsg has been removed.\n"); - return -EOPNOTSUPP; - } - - sreq->cmd_len = hdr->request_len; - if (sreq->cmd_len > BLK_MAX_CDB) { - sreq->cmd = kzalloc(sreq->cmd_len, GFP_KERNEL); - if (!sreq->cmd) - return -ENOMEM; - } - - if (copy_from_user(sreq->cmd, uptr64(hdr->request), sreq->cmd_len)) - return -EFAULT; - if (blk_verify_command(sreq->cmd, mode)) - return -EPERM; - return 0; -} - -static int bsg_scsi_complete_rq(struct request *rq, struct sg_io_v4 *hdr) -{ - struct scsi_request *sreq = scsi_req(rq); - int ret = 0; - - /* - * fill in all the output members - */ - hdr->device_status = sreq->result & 0xff; - hdr->transport_status = host_byte(sreq->result); - hdr->driver_status = 0; - if (scsi_status_is_check_condition(sreq->result)) - hdr->driver_status = DRIVER_SENSE; - hdr->info = 0; - if (hdr->device_status || hdr->transport_status || hdr->driver_status) - hdr->info |= SG_INFO_CHECK; - hdr->response_len = 0; - - if (sreq->sense_len && hdr->response) { - int len = min_t(unsigned int, hdr->max_response_len, - sreq->sense_len); - - if (copy_to_user(uptr64(hdr->response), sreq->sense, len)) - ret = -EFAULT; - else - hdr->response_len = len; - } - - if (rq_data_dir(rq) == READ) - hdr->din_resid = sreq->resid_len; - else - hdr->dout_resid = sreq->resid_len; - - return ret; -} - -static void bsg_scsi_free_rq(struct request *rq) -{ - scsi_req_free_cmd(scsi_req(rq)); -} - -static const struct bsg_ops bsg_scsi_ops = { - .check_proto = bsg_scsi_check_proto, - .fill_hdr = bsg_scsi_fill_hdr, - .complete_rq = bsg_scsi_complete_rq, - .free_rq = bsg_scsi_free_rq, -}; - static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) { struct request *rq; @@ -487,17 +404,7 @@ unlock: mutex_unlock(&bsg_mutex); return ret; } - -int bsg_scsi_register_queue(struct request_queue *q, struct device *parent) -{ - if (!blk_queue_scsi_passthrough(q)) { - WARN_ONCE(true, "Attempt to register a non-SCSI queue\n"); - return -EINVAL; - } - - return bsg_register_queue(q, parent, dev_name(parent), &bsg_scsi_ops); -} -EXPORT_SYMBOL_GPL(bsg_scsi_register_queue); +EXPORT_SYMBOL_GPL(bsg_register_queue); static struct cdev bsg_cdev; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 8f44d433e06e..86ecab196dfd 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -20,6 +20,7 @@ config SCSI select SCSI_DMA if HAS_DMA select SG_POOL select BLK_SCSI_REQUEST + select BLK_DEV_BSG_COMMON if BLK_DEV_BSG help If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or any other SCSI device under Linux, say Y and make sure that you know @@ -140,6 +141,18 @@ config CHR_DEV_SG If unsure, say N. +config BLK_DEV_BSG + bool "/dev/bsg support (SG v4)" + depends on SCSI + default y + help + Saying Y here will enable generic SG (SCSI generic) v4 support + for any SCSI device. + + This option is required by UDEV to access device serial numbers, etc. + + If unsure, say Y. + config CHR_DEV_SCH tristate "SCSI media changer support" depends on SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 1748d1ec1338..240b831b5a11 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -168,6 +168,7 @@ scsi_mod-$(CONFIG_BLK_DEBUG_FS) += scsi_debugfs.o scsi_mod-y += scsi_trace.o scsi_logging.o scsi_mod-$(CONFIG_PM) += scsi_pm.o scsi_mod-$(CONFIG_SCSI_DH) += scsi_dh.o +scsi_mod-$(CONFIG_BLK_DEV_BSG) += scsi_bsg.o hv_storvsc-y := storvsc_drv.o diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c new file mode 100644 index 000000000000..3bdb28940460 --- /dev/null +++ b/drivers/scsi/scsi_bsg.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include "scsi_priv.h" + +#define uptr64(val) ((void __user *)(uintptr_t)(val)) + +static int scsi_bsg_check_proto(struct sg_io_v4 *hdr) +{ + if (hdr->protocol != BSG_PROTOCOL_SCSI || + hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD) + return -EINVAL; + return 0; +} + +static int scsi_bsg_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, + fmode_t mode) +{ + struct scsi_request *sreq = scsi_req(rq); + + if (hdr->dout_xfer_len && hdr->din_xfer_len) { + pr_warn_once("BIDI support in bsg has been removed.\n"); + return -EOPNOTSUPP; + } + + sreq->cmd_len = hdr->request_len; + if (sreq->cmd_len > BLK_MAX_CDB) { + sreq->cmd = kzalloc(sreq->cmd_len, GFP_KERNEL); + if (!sreq->cmd) + return -ENOMEM; + } + + if (copy_from_user(sreq->cmd, uptr64(hdr->request), sreq->cmd_len)) + return -EFAULT; + if (blk_verify_command(sreq->cmd, mode)) + return -EPERM; + return 0; +} + +static int scsi_bsg_complete_rq(struct request *rq, struct sg_io_v4 *hdr) +{ + struct scsi_request *sreq = scsi_req(rq); + int ret = 0; + + /* + * fill in all the output members + */ + hdr->device_status = sreq->result & 0xff; + hdr->transport_status = host_byte(sreq->result); + hdr->driver_status = 0; + if (scsi_status_is_check_condition(sreq->result)) + hdr->driver_status = DRIVER_SENSE; + hdr->info = 0; + if (hdr->device_status || hdr->transport_status || hdr->driver_status) + hdr->info |= SG_INFO_CHECK; + hdr->response_len = 0; + + if (sreq->sense_len && hdr->response) { + int len = min_t(unsigned int, hdr->max_response_len, + sreq->sense_len); + + if (copy_to_user(uptr64(hdr->response), sreq->sense, len)) + ret = -EFAULT; + else + hdr->response_len = len; + } + + if (rq_data_dir(rq) == READ) + hdr->din_resid = sreq->resid_len; + else + hdr->dout_resid = sreq->resid_len; + + return ret; +} + +static void scsi_bsg_free_rq(struct request *rq) +{ + scsi_req_free_cmd(scsi_req(rq)); +} + +static const struct bsg_ops scsi_bsg_ops = { + .check_proto = scsi_bsg_check_proto, + .fill_hdr = scsi_bsg_fill_hdr, + .complete_rq = scsi_bsg_complete_rq, + .free_rq = scsi_bsg_free_rq, +}; + +int scsi_bsg_register_queue(struct request_queue *q, struct device *parent) +{ + return bsg_register_queue(q, parent, dev_name(parent), &scsi_bsg_ops); +} diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index eae2235f79b5..0a0db35bab04 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -180,6 +180,16 @@ static inline void scsi_dh_add_device(struct scsi_device *sdev) { } static inline void scsi_dh_release_device(struct scsi_device *sdev) { } #endif +#ifdef CONFIG_BLK_DEV_BSG +int scsi_bsg_register_queue(struct request_queue *q, struct device *parent); +#else +static inline int scsi_bsg_register_queue(struct request_queue *q, + struct device *parent) +{ + return 0; +} +#endif + extern int scsi_device_max_queue_depth(struct scsi_device *sdev); /* diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 32489d25158f..4ff9ac3296d8 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1366,7 +1366,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) transport_add_device(&sdev->sdev_gendev); sdev->is_visible = 1; - error = bsg_scsi_register_queue(rq, &sdev->sdev_gendev); + error = scsi_bsg_register_queue(rq, &sdev->sdev_gendev); if (error) /* we're treating error on bsg register as non-fatal, * so pretend nothing went wrong */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9971796819ef..d36b67bd7267 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -537,7 +537,7 @@ struct request_queue { int mq_freeze_depth; -#if defined(CONFIG_BLK_DEV_BSG) +#if IS_ENABLED(CONFIG_BLK_DEV_BSG_COMMON) struct bsg_class_device bsg_dev; #endif diff --git a/include/linux/bsg.h b/include/linux/bsg.h index dac37b6e00ec..b887da20bd41 100644 --- a/include/linux/bsg.h +++ b/include/linux/bsg.h @@ -5,8 +5,9 @@ #include struct request; +struct request_queue; -#ifdef CONFIG_BLK_DEV_BSG +#ifdef CONFIG_BLK_DEV_BSG_COMMON struct bsg_ops { int (*check_proto)(struct sg_io_v4 *hdr); int (*fill_hdr)(struct request *rq, struct sg_io_v4 *hdr, @@ -24,16 +25,10 @@ struct bsg_class_device { int bsg_register_queue(struct request_queue *q, struct device *parent, const char *name, const struct bsg_ops *ops); -int bsg_scsi_register_queue(struct request_queue *q, struct device *parent); void bsg_unregister_queue(struct request_queue *q); #else -static inline int bsg_scsi_register_queue(struct request_queue *q, - struct device *parent) -{ - return 0; -} static inline void bsg_unregister_queue(struct request_queue *q) { } -#endif /* CONFIG_BLK_DEV_BSG */ +#endif /* CONFIG_BLK_DEV_BSG_COMMON */ #endif /* _LINUX_BSG_H */ -- cgit v1.2.3-71-gd317 From 7353dc06c9a8e37c80da7ff986e6ef5123bec8ce Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:26 +0200 Subject: scsi: scsi_ioctl: Simplify SCSI passthrough permission checking Remove the separate command filter structure and just use a switch statement (which also cought two duplicate commands), return a bool and give the function a sensible name. Link: https://lore.kernel.org/r/20210724072033.1284840-18-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/scsi_ioctl.c | 219 ++++++++++++++++++++++-------------------------- drivers/scsi/scsi_bsg.c | 2 +- drivers/scsi/sg.c | 5 +- include/linux/blkdev.h | 2 +- 4 files changed, 104 insertions(+), 124 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 4d023f2f43f0..3642e145108a 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -22,13 +22,6 @@ #include #include -struct blk_cmd_filter { - unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; - unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; -}; - -static struct blk_cmd_filter blk_default_cmd_filter; - static int sg_get_version(int __user *p) { static const int sg_version_num = 30527; @@ -80,115 +73,108 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) return put_user(1, p); } -static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) -{ - /* Basic read-only commands */ - __set_bit(TEST_UNIT_READY, filter->read_ok); - __set_bit(REQUEST_SENSE, filter->read_ok); - __set_bit(READ_6, filter->read_ok); - __set_bit(READ_10, filter->read_ok); - __set_bit(READ_12, filter->read_ok); - __set_bit(READ_16, filter->read_ok); - __set_bit(READ_BUFFER, filter->read_ok); - __set_bit(READ_DEFECT_DATA, filter->read_ok); - __set_bit(READ_CAPACITY, filter->read_ok); - __set_bit(READ_LONG, filter->read_ok); - __set_bit(INQUIRY, filter->read_ok); - __set_bit(MODE_SENSE, filter->read_ok); - __set_bit(MODE_SENSE_10, filter->read_ok); - __set_bit(LOG_SENSE, filter->read_ok); - __set_bit(START_STOP, filter->read_ok); - __set_bit(GPCMD_VERIFY_10, filter->read_ok); - __set_bit(VERIFY_16, filter->read_ok); - __set_bit(REPORT_LUNS, filter->read_ok); - __set_bit(SERVICE_ACTION_IN_16, filter->read_ok); - __set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok); - __set_bit(MAINTENANCE_IN, filter->read_ok); - __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); - - /* Audio CD commands */ - __set_bit(GPCMD_PLAY_CD, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); - __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); - __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); - - /* CD/DVD data reading */ - __set_bit(GPCMD_READ_CD, filter->read_ok); - __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); - __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); - __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); - __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); - __set_bit(GPCMD_READ_HEADER, filter->read_ok); - __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); - __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); - __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); - __set_bit(GPCMD_REPORT_KEY, filter->read_ok); - __set_bit(GPCMD_SCAN, filter->read_ok); - __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); - __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); - __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); - __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); - __set_bit(GPCMD_SEEK, filter->read_ok); - __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); - - /* Basic writing commands */ - __set_bit(WRITE_6, filter->write_ok); - __set_bit(WRITE_10, filter->write_ok); - __set_bit(WRITE_VERIFY, filter->write_ok); - __set_bit(WRITE_12, filter->write_ok); - __set_bit(WRITE_VERIFY_12, filter->write_ok); - __set_bit(WRITE_16, filter->write_ok); - __set_bit(WRITE_LONG, filter->write_ok); - __set_bit(WRITE_LONG_2, filter->write_ok); - __set_bit(WRITE_SAME, filter->write_ok); - __set_bit(WRITE_SAME_16, filter->write_ok); - __set_bit(WRITE_SAME_32, filter->write_ok); - __set_bit(ERASE, filter->write_ok); - __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); - __set_bit(MODE_SELECT, filter->write_ok); - __set_bit(LOG_SELECT, filter->write_ok); - __set_bit(GPCMD_BLANK, filter->write_ok); - __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); - __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); - __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); - __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); - __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); - __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); - __set_bit(GPCMD_SEND_EVENT, filter->write_ok); - __set_bit(GPCMD_SEND_KEY, filter->write_ok); - __set_bit(GPCMD_SEND_OPC, filter->write_ok); - __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); - __set_bit(GPCMD_SET_SPEED, filter->write_ok); - __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); - __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); - __set_bit(GPCMD_SET_STREAMING, filter->write_ok); - __set_bit(GPCMD_SET_READ_AHEAD, filter->write_ok); - - /* ZBC Commands */ - __set_bit(ZBC_OUT, filter->write_ok); - __set_bit(ZBC_IN, filter->read_ok); -} - -int blk_verify_command(unsigned char *cmd, fmode_t mode) +/* + * Check if the given command is allowed. + * + * For unprivileged users only a small set of whitelisted command is allowed so + * that they can't format the drive or update the firmware. + */ +bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode) { - struct blk_cmd_filter *filter = &blk_default_cmd_filter; - /* root can do any command. */ if (capable(CAP_SYS_RAWIO)) - return 0; + return true; /* Anybody who can open the device can do a read-safe command */ - if (test_bit(cmd[0], filter->read_ok)) - return 0; - - /* Write-safe commands require a writable open */ - if (test_bit(cmd[0], filter->write_ok) && (mode & FMODE_WRITE)) - return 0; - - return -EPERM; + switch (cmd[0]) { + /* Basic read-only commands */ + case TEST_UNIT_READY: + case REQUEST_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case READ_BUFFER: + case READ_DEFECT_DATA: + case READ_CAPACITY: /* also GPCMD_READ_CDVD_CAPACITY */ + case READ_LONG: + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case LOG_SENSE: + case START_STOP: + case GPCMD_VERIFY_10: + case VERIFY_16: + case REPORT_LUNS: + case SERVICE_ACTION_IN_16: + case RECEIVE_DIAGNOSTIC: + case MAINTENANCE_IN: /* also GPCMD_SEND_KEY, which is a write command */ + case GPCMD_READ_BUFFER_CAPACITY: + /* Audio CD commands */ + case GPCMD_PLAY_CD: + case GPCMD_PLAY_AUDIO_10: + case GPCMD_PLAY_AUDIO_MSF: + case GPCMD_PLAY_AUDIO_TI: + case GPCMD_PAUSE_RESUME: + /* CD/DVD data reading */ + case GPCMD_READ_CD: + case GPCMD_READ_CD_MSF: + case GPCMD_READ_DISC_INFO: + case GPCMD_READ_DVD_STRUCTURE: + case GPCMD_READ_HEADER: + case GPCMD_READ_TRACK_RZONE_INFO: + case GPCMD_READ_SUBCHANNEL: + case GPCMD_READ_TOC_PMA_ATIP: + case GPCMD_REPORT_KEY: + case GPCMD_SCAN: + case GPCMD_GET_CONFIGURATION: + case GPCMD_READ_FORMAT_CAPACITIES: + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + case GPCMD_GET_PERFORMANCE: + case GPCMD_SEEK: + case GPCMD_STOP_PLAY_SCAN: + /* ZBC */ + case ZBC_IN: + return true; + /* Basic writing commands */ + case WRITE_6: + case WRITE_10: + case WRITE_VERIFY: + case WRITE_12: + case WRITE_VERIFY_12: + case WRITE_16: + case WRITE_LONG: + case WRITE_LONG_2: + case WRITE_SAME: + case WRITE_SAME_16: + case WRITE_SAME_32: + case ERASE: + case GPCMD_MODE_SELECT_10: + case MODE_SELECT: + case LOG_SELECT: + case GPCMD_BLANK: + case GPCMD_CLOSE_TRACK: + case GPCMD_FLUSH_CACHE: + case GPCMD_FORMAT_UNIT: + case GPCMD_REPAIR_RZONE_TRACK: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_SEND_DVD_STRUCTURE: + case GPCMD_SEND_EVENT: + case GPCMD_SEND_OPC: + case GPCMD_SEND_CUE_SHEET: + case GPCMD_SET_SPEED: + case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + case GPCMD_LOAD_UNLOAD: + case GPCMD_SET_STREAMING: + case GPCMD_SET_READ_AHEAD: + /* ZBC */ + case ZBC_OUT: + return (mode & FMODE_WRITE); + default: + return false; + } } -EXPORT_SYMBOL(blk_verify_command); +EXPORT_SYMBOL(scsi_cmd_allowed); static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, struct sg_io_hdr *hdr, fmode_t mode) @@ -197,7 +183,7 @@ static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, if (copy_from_user(req->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; - if (blk_verify_command(req->cmd, mode)) + if (!scsi_cmd_allowed(req->cmd, mode)) return -EPERM; /* @@ -428,8 +414,8 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error; - err = blk_verify_command(req->cmd, mode); - if (err) + err = -EPERM; + if (!scsi_cmd_allowed(req->cmd, mode)) goto error; /* default. possible overriden later */ @@ -808,10 +794,3 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod return err; } EXPORT_SYMBOL(scsi_cmd_ioctl); - -static int __init blk_scsi_ioctl_init(void) -{ - blk_set_cmd_filter_defaults(&blk_default_cmd_filter); - return 0; -} -fs_initcall(blk_scsi_ioctl_init); diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index 3bdb28940460..68f60316adf1 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -36,7 +36,7 @@ static int scsi_bsg_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, if (copy_from_user(sreq->cmd, uptr64(hdr->request), sreq->cmd_len)) return -EFAULT; - if (blk_verify_command(sreq->cmd, mode)) + if (!scsi_cmd_allowed(sreq->cmd, mode)) return -EPERM; return 0; } diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6cb1e4b6eac2..c86fa4476334 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -238,8 +238,9 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) if (sfp->parentdp->device->type == TYPE_SCANNER) return 0; - - return blk_verify_command(cmd, filp->f_mode); + if (!scsi_cmd_allowed(cmd, filp->f_mode)) + return -EPERM; + return 0; } static int diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d36b67bd7267..e28679e63373 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1343,7 +1343,7 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask, 0); } -extern int blk_verify_command(unsigned char *cmd, fmode_t mode); +bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode); static inline bool bdev_is_partition(struct block_device *bdev) { -- cgit v1.2.3-71-gd317 From f2542a3be3277a65c766fa6e86b930d3d839f79e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 24 Jul 2021 09:20:27 +0200 Subject: scsi: scsi_ioctl: Move the "block layer" SCSI ioctl handling to drivers/scsi Merge the ioctl handling in block/scsi_ioctl.c into its only caller in drivers/scsi/scsi_ioctl.c. Link: https://lore.kernel.org/r/20210724072033.1284840-19-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/Makefile | 1 - block/scsi_ioctl.c | 796 ---------------------------------------------- drivers/scsi/scsi_ioctl.c | 740 +++++++++++++++++++++++++++++++++++++++++- include/linux/blkdev.h | 11 - include/scsi/scsi_ioctl.h | 6 + 5 files changed, 741 insertions(+), 813 deletions(-) delete mode 100644 block/scsi_ioctl.c (limited to 'include/linux/blkdev.h') diff --git a/block/Makefile b/block/Makefile index f37d532c8da5..640afba070fd 100644 --- a/block/Makefile +++ b/block/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-sysfs.o \ disk-events.o obj-$(CONFIG_BOUNCE) += bounce.o -obj-$(CONFIG_BLK_SCSI_REQUEST) += scsi_ioctl.o obj-$(CONFIG_BLK_DEV_BSG_COMMON) += bsg.o obj-$(CONFIG_BLK_DEV_BSGLIB) += bsg-lib.o obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c deleted file mode 100644 index 3642e145108a..000000000000 --- a/block/scsi_ioctl.c +++ /dev/null @@ -1,796 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2001 Jens Axboe - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int sg_get_version(int __user *p) -{ - static const int sg_version_num = 30527; - return put_user(sg_version_num, p); -} - -static int sg_get_timeout(struct request_queue *q) -{ - return jiffies_to_clock_t(q->sg_timeout); -} - -static int sg_set_timeout(struct request_queue *q, int __user *p) -{ - int timeout, err = get_user(timeout, p); - - if (!err) - q->sg_timeout = clock_t_to_jiffies(timeout); - - return err; -} - -static int sg_get_reserved_size(struct request_queue *q, int __user *p) -{ - int val = min(q->sg_reserved_size, queue_max_bytes(q)); - - return put_user(val, p); -} - -static int sg_set_reserved_size(struct request_queue *q, int __user *p) -{ - int size, err = get_user(size, p); - - if (err) - return err; - - if (size < 0) - return -EINVAL; - - q->sg_reserved_size = min_t(unsigned int, size, queue_max_bytes(q)); - return 0; -} - -/* - * will always return that we are ATAPI even for a real SCSI drive, I'm not - * so sure this is worth doing anything about (why would you care??) - */ -static int sg_emulated_host(struct request_queue *q, int __user *p) -{ - return put_user(1, p); -} - -/* - * Check if the given command is allowed. - * - * For unprivileged users only a small set of whitelisted command is allowed so - * that they can't format the drive or update the firmware. - */ -bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode) -{ - /* root can do any command. */ - if (capable(CAP_SYS_RAWIO)) - return true; - - /* Anybody who can open the device can do a read-safe command */ - switch (cmd[0]) { - /* Basic read-only commands */ - case TEST_UNIT_READY: - case REQUEST_SENSE: - case READ_6: - case READ_10: - case READ_12: - case READ_16: - case READ_BUFFER: - case READ_DEFECT_DATA: - case READ_CAPACITY: /* also GPCMD_READ_CDVD_CAPACITY */ - case READ_LONG: - case INQUIRY: - case MODE_SENSE: - case MODE_SENSE_10: - case LOG_SENSE: - case START_STOP: - case GPCMD_VERIFY_10: - case VERIFY_16: - case REPORT_LUNS: - case SERVICE_ACTION_IN_16: - case RECEIVE_DIAGNOSTIC: - case MAINTENANCE_IN: /* also GPCMD_SEND_KEY, which is a write command */ - case GPCMD_READ_BUFFER_CAPACITY: - /* Audio CD commands */ - case GPCMD_PLAY_CD: - case GPCMD_PLAY_AUDIO_10: - case GPCMD_PLAY_AUDIO_MSF: - case GPCMD_PLAY_AUDIO_TI: - case GPCMD_PAUSE_RESUME: - /* CD/DVD data reading */ - case GPCMD_READ_CD: - case GPCMD_READ_CD_MSF: - case GPCMD_READ_DISC_INFO: - case GPCMD_READ_DVD_STRUCTURE: - case GPCMD_READ_HEADER: - case GPCMD_READ_TRACK_RZONE_INFO: - case GPCMD_READ_SUBCHANNEL: - case GPCMD_READ_TOC_PMA_ATIP: - case GPCMD_REPORT_KEY: - case GPCMD_SCAN: - case GPCMD_GET_CONFIGURATION: - case GPCMD_READ_FORMAT_CAPACITIES: - case GPCMD_GET_EVENT_STATUS_NOTIFICATION: - case GPCMD_GET_PERFORMANCE: - case GPCMD_SEEK: - case GPCMD_STOP_PLAY_SCAN: - /* ZBC */ - case ZBC_IN: - return true; - /* Basic writing commands */ - case WRITE_6: - case WRITE_10: - case WRITE_VERIFY: - case WRITE_12: - case WRITE_VERIFY_12: - case WRITE_16: - case WRITE_LONG: - case WRITE_LONG_2: - case WRITE_SAME: - case WRITE_SAME_16: - case WRITE_SAME_32: - case ERASE: - case GPCMD_MODE_SELECT_10: - case MODE_SELECT: - case LOG_SELECT: - case GPCMD_BLANK: - case GPCMD_CLOSE_TRACK: - case GPCMD_FLUSH_CACHE: - case GPCMD_FORMAT_UNIT: - case GPCMD_REPAIR_RZONE_TRACK: - case GPCMD_RESERVE_RZONE_TRACK: - case GPCMD_SEND_DVD_STRUCTURE: - case GPCMD_SEND_EVENT: - case GPCMD_SEND_OPC: - case GPCMD_SEND_CUE_SHEET: - case GPCMD_SET_SPEED: - case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - case GPCMD_LOAD_UNLOAD: - case GPCMD_SET_STREAMING: - case GPCMD_SET_READ_AHEAD: - /* ZBC */ - case ZBC_OUT: - return (mode & FMODE_WRITE); - default: - return false; - } -} -EXPORT_SYMBOL(scsi_cmd_allowed); - -static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_hdr *hdr, fmode_t mode) -{ - struct scsi_request *req = scsi_req(rq); - - if (copy_from_user(req->cmd, hdr->cmdp, hdr->cmd_len)) - return -EFAULT; - if (!scsi_cmd_allowed(req->cmd, mode)) - return -EPERM; - - /* - * fill in request structure - */ - req->cmd_len = hdr->cmd_len; - - rq->timeout = msecs_to_jiffies(hdr->timeout); - if (!rq->timeout) - rq->timeout = q->sg_timeout; - if (!rq->timeout) - rq->timeout = BLK_DEFAULT_SG_TIMEOUT; - if (rq->timeout < BLK_MIN_SG_TIMEOUT) - rq->timeout = BLK_MIN_SG_TIMEOUT; - - return 0; -} - -static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, - struct bio *bio) -{ - struct scsi_request *req = scsi_req(rq); - int r, ret = 0; - - /* - * fill in all the output members - */ - hdr->status = req->result & 0xff; - hdr->masked_status = status_byte(req->result); - hdr->msg_status = COMMAND_COMPLETE; - hdr->host_status = host_byte(req->result); - hdr->driver_status = 0; - if (scsi_status_is_check_condition(hdr->status)) - hdr->driver_status = DRIVER_SENSE; - hdr->info = 0; - if (hdr->masked_status || hdr->host_status || hdr->driver_status) - hdr->info |= SG_INFO_CHECK; - hdr->resid = req->resid_len; - hdr->sb_len_wr = 0; - - if (req->sense_len && hdr->sbp) { - int len = min((unsigned int) hdr->mx_sb_len, req->sense_len); - - if (!copy_to_user(hdr->sbp, req->sense, len)) - hdr->sb_len_wr = len; - else - ret = -EFAULT; - } - - r = blk_rq_unmap_user(bio); - if (!ret) - ret = r; - - return ret; -} - -static int sg_io(struct request_queue *q, struct gendisk *bd_disk, - struct sg_io_hdr *hdr, fmode_t mode) -{ - unsigned long start_time; - ssize_t ret = 0; - int writing = 0; - int at_head = 0; - struct request *rq; - struct scsi_request *req; - struct bio *bio; - - if (hdr->interface_id != 'S') - return -EINVAL; - - if (hdr->dxfer_len > (queue_max_hw_sectors(q) << 9)) - return -EIO; - - if (hdr->dxfer_len) - switch (hdr->dxfer_direction) { - default: - return -EINVAL; - case SG_DXFER_TO_DEV: - writing = 1; - break; - case SG_DXFER_TO_FROM_DEV: - case SG_DXFER_FROM_DEV: - break; - } - if (hdr->flags & SG_FLAG_Q_AT_HEAD) - at_head = 1; - - ret = -ENOMEM; - rq = blk_get_request(q, writing ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); - if (IS_ERR(rq)) - return PTR_ERR(rq); - req = scsi_req(rq); - - if (hdr->cmd_len > BLK_MAX_CDB) { - req->cmd = kzalloc(hdr->cmd_len, GFP_KERNEL); - if (!req->cmd) - goto out_put_request; - } - - ret = blk_fill_sghdr_rq(q, rq, hdr, mode); - if (ret < 0) - goto out_free_cdb; - - ret = 0; - if (hdr->iovec_count) { - struct iov_iter i; - struct iovec *iov = NULL; - - ret = import_iovec(rq_data_dir(rq), hdr->dxferp, - hdr->iovec_count, 0, &iov, &i); - if (ret < 0) - goto out_free_cdb; - - /* SG_IO howto says that the shorter of the two wins */ - iov_iter_truncate(&i, hdr->dxfer_len); - - ret = blk_rq_map_user_iov(q, rq, NULL, &i, GFP_KERNEL); - kfree(iov); - } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, - GFP_KERNEL); - - if (ret) - goto out_free_cdb; - - bio = rq->bio; - req->retries = 0; - - start_time = jiffies; - - blk_execute_rq(bd_disk, rq, at_head); - - hdr->duration = jiffies_to_msecs(jiffies - start_time); - - ret = blk_complete_sghdr_rq(rq, hdr, bio); - -out_free_cdb: - scsi_req_free_cmd(req); -out_put_request: - blk_put_request(rq); - return ret; -} - -/** - * sg_scsi_ioctl -- handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl - * @q: request queue to send scsi commands down - * @disk: gendisk to operate on (option) - * @mode: mode used to open the file through which the ioctl has been - * submitted - * @sic: userspace structure describing the command to perform - * - * Send down the scsi command described by @sic to the device below - * the request queue @q. If @file is non-NULL it's used to perform - * fine-grained permission checks that allow users to send down - * non-destructive SCSI commands. If the caller has a struct gendisk - * available it should be passed in as @disk to allow the low level - * driver to use the information contained in it. A non-NULL @disk - * is only allowed if the caller knows that the low level driver doesn't - * need it (e.g. in the scsi subsystem). - * - * Notes: - * - This interface is deprecated - users should use the SG_IO - * interface instead, as this is a more flexible approach to - * performing SCSI commands on a device. - * - The SCSI command length is determined by examining the 1st byte - * of the given command. There is no way to override this. - * - Data transfers are limited to PAGE_SIZE - * - The length (x + y) must be at least OMAX_SB_LEN bytes long to - * accommodate the sense buffer when an error occurs. - * The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that - * old code will not be surprised. - * - If a Unix error occurs (e.g. ENOMEM) then the user will receive - * a negative return and the Unix error code in 'errno'. - * If the SCSI command succeeds then 0 is returned. - * Positive numbers returned are the compacted SCSI error codes (4 - * bytes in one int) where the lowest byte is the SCSI status. - */ -int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, - struct scsi_ioctl_command __user *sic) -{ - enum { OMAX_SB_LEN = 16 }; /* For backward compatibility */ - struct request *rq; - struct scsi_request *req; - int err; - unsigned int in_len, out_len, bytes, opcode, cmdlen; - char *buffer = NULL; - - if (!sic) - return -EINVAL; - - /* - * get in an out lengths, verify they don't exceed a page worth of data - */ - if (get_user(in_len, &sic->inlen)) - return -EFAULT; - if (get_user(out_len, &sic->outlen)) - return -EFAULT; - if (in_len > PAGE_SIZE || out_len > PAGE_SIZE) - return -EINVAL; - if (get_user(opcode, sic->data)) - return -EFAULT; - - bytes = max(in_len, out_len); - if (bytes) { - buffer = kzalloc(bytes, GFP_NOIO | GFP_USER | __GFP_NOWARN); - if (!buffer) - return -ENOMEM; - - } - - rq = blk_get_request(q, in_len ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); - if (IS_ERR(rq)) { - err = PTR_ERR(rq); - goto error_free_buffer; - } - req = scsi_req(rq); - - cmdlen = COMMAND_SIZE(opcode); - - /* - * get command and data to send to device, if any - */ - err = -EFAULT; - req->cmd_len = cmdlen; - if (copy_from_user(req->cmd, sic->data, cmdlen)) - goto error; - - if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) - goto error; - - err = -EPERM; - if (!scsi_cmd_allowed(req->cmd, mode)) - goto error; - - /* default. possible overriden later */ - req->retries = 5; - - switch (opcode) { - case SEND_DIAGNOSTIC: - case FORMAT_UNIT: - rq->timeout = FORMAT_UNIT_TIMEOUT; - req->retries = 1; - break; - case START_STOP: - rq->timeout = START_STOP_TIMEOUT; - break; - case MOVE_MEDIUM: - rq->timeout = MOVE_MEDIUM_TIMEOUT; - break; - case READ_ELEMENT_STATUS: - rq->timeout = READ_ELEMENT_STATUS_TIMEOUT; - break; - case READ_DEFECT_DATA: - rq->timeout = READ_DEFECT_DATA_TIMEOUT; - req->retries = 1; - break; - default: - rq->timeout = BLK_DEFAULT_SG_TIMEOUT; - break; - } - - if (bytes) { - err = blk_rq_map_kern(q, rq, buffer, bytes, GFP_NOIO); - if (err) - goto error; - } - - blk_execute_rq(disk, rq, 0); - - err = req->result & 0xff; /* only 8 bit SCSI status */ - if (err) { - if (req->sense_len && req->sense) { - bytes = (OMAX_SB_LEN > req->sense_len) ? - req->sense_len : OMAX_SB_LEN; - if (copy_to_user(sic->data, req->sense, bytes)) - err = -EFAULT; - } - } else { - if (copy_to_user(sic->data, buffer, out_len)) - err = -EFAULT; - } - -error: - blk_put_request(rq); - -error_free_buffer: - kfree(buffer); - - return err; -} -EXPORT_SYMBOL_GPL(sg_scsi_ioctl); - -/* Send basic block requests */ -static int __blk_send_generic(struct request_queue *q, struct gendisk *bd_disk, - int cmd, int data) -{ - struct request *rq; - int err; - - rq = blk_get_request(q, REQ_OP_DRV_OUT, 0); - if (IS_ERR(rq)) - return PTR_ERR(rq); - rq->timeout = BLK_DEFAULT_SG_TIMEOUT; - scsi_req(rq)->cmd[0] = cmd; - scsi_req(rq)->cmd[4] = data; - scsi_req(rq)->cmd_len = 6; - blk_execute_rq(bd_disk, rq, 0); - err = scsi_req(rq)->result ? -EIO : 0; - blk_put_request(rq); - - return err; -} - -static inline int blk_send_start_stop(struct request_queue *q, - struct gendisk *bd_disk, int data) -{ - return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data); -} - -int put_sg_io_hdr(const struct sg_io_hdr *hdr, void __user *argp) -{ -#ifdef CONFIG_COMPAT - if (in_compat_syscall()) { - struct compat_sg_io_hdr hdr32 = { - .interface_id = hdr->interface_id, - .dxfer_direction = hdr->dxfer_direction, - .cmd_len = hdr->cmd_len, - .mx_sb_len = hdr->mx_sb_len, - .iovec_count = hdr->iovec_count, - .dxfer_len = hdr->dxfer_len, - .dxferp = (uintptr_t)hdr->dxferp, - .cmdp = (uintptr_t)hdr->cmdp, - .sbp = (uintptr_t)hdr->sbp, - .timeout = hdr->timeout, - .flags = hdr->flags, - .pack_id = hdr->pack_id, - .usr_ptr = (uintptr_t)hdr->usr_ptr, - .status = hdr->status, - .masked_status = hdr->masked_status, - .msg_status = hdr->msg_status, - .sb_len_wr = hdr->sb_len_wr, - .host_status = hdr->host_status, - .driver_status = hdr->driver_status, - .resid = hdr->resid, - .duration = hdr->duration, - .info = hdr->info, - }; - - if (copy_to_user(argp, &hdr32, sizeof(hdr32))) - return -EFAULT; - - return 0; - } -#endif - - if (copy_to_user(argp, hdr, sizeof(*hdr))) - return -EFAULT; - - return 0; -} -EXPORT_SYMBOL(put_sg_io_hdr); - -int get_sg_io_hdr(struct sg_io_hdr *hdr, const void __user *argp) -{ -#ifdef CONFIG_COMPAT - struct compat_sg_io_hdr hdr32; - - if (in_compat_syscall()) { - if (copy_from_user(&hdr32, argp, sizeof(hdr32))) - return -EFAULT; - - *hdr = (struct sg_io_hdr) { - .interface_id = hdr32.interface_id, - .dxfer_direction = hdr32.dxfer_direction, - .cmd_len = hdr32.cmd_len, - .mx_sb_len = hdr32.mx_sb_len, - .iovec_count = hdr32.iovec_count, - .dxfer_len = hdr32.dxfer_len, - .dxferp = compat_ptr(hdr32.dxferp), - .cmdp = compat_ptr(hdr32.cmdp), - .sbp = compat_ptr(hdr32.sbp), - .timeout = hdr32.timeout, - .flags = hdr32.flags, - .pack_id = hdr32.pack_id, - .usr_ptr = compat_ptr(hdr32.usr_ptr), - .status = hdr32.status, - .masked_status = hdr32.masked_status, - .msg_status = hdr32.msg_status, - .sb_len_wr = hdr32.sb_len_wr, - .host_status = hdr32.host_status, - .driver_status = hdr32.driver_status, - .resid = hdr32.resid, - .duration = hdr32.duration, - .info = hdr32.info, - }; - - return 0; - } -#endif - - if (copy_from_user(hdr, argp, sizeof(*hdr))) - return -EFAULT; - - return 0; -} -EXPORT_SYMBOL(get_sg_io_hdr); - -#ifdef CONFIG_COMPAT -struct compat_cdrom_generic_command { - unsigned char cmd[CDROM_PACKET_SIZE]; - compat_caddr_t buffer; - compat_uint_t buflen; - compat_int_t stat; - compat_caddr_t sense; - unsigned char data_direction; - unsigned char pad[3]; - compat_int_t quiet; - compat_int_t timeout; - compat_caddr_t unused; -}; -#endif - -static int scsi_get_cdrom_generic_arg(struct cdrom_generic_command *cgc, - const void __user *arg) -{ -#ifdef CONFIG_COMPAT - if (in_compat_syscall()) { - struct compat_cdrom_generic_command cgc32; - - if (copy_from_user(&cgc32, arg, sizeof(cgc32))) - return -EFAULT; - - *cgc = (struct cdrom_generic_command) { - .buffer = compat_ptr(cgc32.buffer), - .buflen = cgc32.buflen, - .stat = cgc32.stat, - .sense = compat_ptr(cgc32.sense), - .data_direction = cgc32.data_direction, - .quiet = cgc32.quiet, - .timeout = cgc32.timeout, - .unused = compat_ptr(cgc32.unused), - }; - memcpy(&cgc->cmd, &cgc32.cmd, CDROM_PACKET_SIZE); - return 0; - } -#endif - if (copy_from_user(cgc, arg, sizeof(*cgc))) - return -EFAULT; - - return 0; -} - -static int scsi_put_cdrom_generic_arg(const struct cdrom_generic_command *cgc, - void __user *arg) -{ -#ifdef CONFIG_COMPAT - if (in_compat_syscall()) { - struct compat_cdrom_generic_command cgc32 = { - .buffer = (uintptr_t)(cgc->buffer), - .buflen = cgc->buflen, - .stat = cgc->stat, - .sense = (uintptr_t)(cgc->sense), - .data_direction = cgc->data_direction, - .quiet = cgc->quiet, - .timeout = cgc->timeout, - .unused = (uintptr_t)(cgc->unused), - }; - memcpy(&cgc32.cmd, &cgc->cmd, CDROM_PACKET_SIZE); - - if (copy_to_user(arg, &cgc32, sizeof(cgc32))) - return -EFAULT; - - return 0; - } -#endif - if (copy_to_user(arg, cgc, sizeof(*cgc))) - return -EFAULT; - - return 0; -} - -static int scsi_cdrom_send_packet(struct request_queue *q, - struct gendisk *bd_disk, - fmode_t mode, void __user *arg) -{ - struct cdrom_generic_command cgc; - struct sg_io_hdr hdr; - int err; - - err = scsi_get_cdrom_generic_arg(&cgc, arg); - if (err) - return err; - - cgc.timeout = clock_t_to_jiffies(cgc.timeout); - memset(&hdr, 0, sizeof(hdr)); - hdr.interface_id = 'S'; - hdr.cmd_len = sizeof(cgc.cmd); - hdr.dxfer_len = cgc.buflen; - switch (cgc.data_direction) { - case CGC_DATA_UNKNOWN: - hdr.dxfer_direction = SG_DXFER_UNKNOWN; - break; - case CGC_DATA_WRITE: - hdr.dxfer_direction = SG_DXFER_TO_DEV; - break; - case CGC_DATA_READ: - hdr.dxfer_direction = SG_DXFER_FROM_DEV; - break; - case CGC_DATA_NONE: - hdr.dxfer_direction = SG_DXFER_NONE; - break; - default: - return -EINVAL; - } - - hdr.dxferp = cgc.buffer; - hdr.sbp = cgc.sense; - if (hdr.sbp) - hdr.mx_sb_len = sizeof(struct request_sense); - hdr.timeout = jiffies_to_msecs(cgc.timeout); - hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd; - hdr.cmd_len = sizeof(cgc.cmd); - - err = sg_io(q, bd_disk, &hdr, mode); - if (err == -EFAULT) - return -EFAULT; - - if (hdr.status) - return -EIO; - - cgc.stat = err; - cgc.buflen = hdr.resid; - if (scsi_put_cdrom_generic_arg(&cgc, arg)) - return -EFAULT; - - return err; -} - -int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mode, - unsigned int cmd, void __user *arg) -{ - int err; - - if (!q) - return -ENXIO; - - switch (cmd) { - /* - * new sgv3 interface - */ - case SG_GET_VERSION_NUM: - err = sg_get_version(arg); - break; - case SG_SET_TIMEOUT: - err = sg_set_timeout(q, arg); - break; - case SG_GET_TIMEOUT: - err = sg_get_timeout(q); - break; - case SG_GET_RESERVED_SIZE: - err = sg_get_reserved_size(q, arg); - break; - case SG_SET_RESERVED_SIZE: - err = sg_set_reserved_size(q, arg); - break; - case SG_EMULATED_HOST: - err = sg_emulated_host(q, arg); - break; - case SG_IO: { - struct sg_io_hdr hdr; - - err = get_sg_io_hdr(&hdr, arg); - if (err) - break; - err = sg_io(q, bd_disk, &hdr, mode); - if (err == -EFAULT) - break; - - if (put_sg_io_hdr(&hdr, arg)) - err = -EFAULT; - break; - } - case CDROM_SEND_PACKET: - err = scsi_cdrom_send_packet(q, bd_disk, mode, arg); - break; - - /* - * old junk scsi send command ioctl - */ - case SCSI_IOCTL_SEND_COMMAND: - printk(KERN_WARNING "program %s is using a deprecated SCSI ioctl, please convert it to SG_IO\n", current->comm); - err = -EINVAL; - if (!arg) - break; - - err = sg_scsi_ioctl(q, bd_disk, mode, arg); - break; - case CDROMCLOSETRAY: - err = blk_send_start_stop(q, bd_disk, 0x03); - break; - case CDROMEJECT: - err = blk_send_start_stop(q, bd_disk, 0x02); - break; - default: - err = -ENOTTY; - } - - return err; -} -EXPORT_SYMBOL(scsi_cmd_ioctl); diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 7739575b5229..2c4cdd0fc26e 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -189,6 +190,706 @@ static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg) ? -EFAULT: 0; } +static int sg_get_version(int __user *p) +{ + static const int sg_version_num = 30527; + return put_user(sg_version_num, p); +} + +static int sg_get_timeout(struct request_queue *q) +{ + return jiffies_to_clock_t(q->sg_timeout); +} + +static int sg_set_timeout(struct request_queue *q, int __user *p) +{ + int timeout, err = get_user(timeout, p); + + if (!err) + q->sg_timeout = clock_t_to_jiffies(timeout); + + return err; +} + +static int sg_get_reserved_size(struct request_queue *q, int __user *p) +{ + int val = min(q->sg_reserved_size, queue_max_bytes(q)); + + return put_user(val, p); +} + +static int sg_set_reserved_size(struct request_queue *q, int __user *p) +{ + int size, err = get_user(size, p); + + if (err) + return err; + + if (size < 0) + return -EINVAL; + + q->sg_reserved_size = min_t(unsigned int, size, queue_max_bytes(q)); + return 0; +} + +/* + * will always return that we are ATAPI even for a real SCSI drive, I'm not + * so sure this is worth doing anything about (why would you care??) + */ +static int sg_emulated_host(struct request_queue *q, int __user *p) +{ + return put_user(1, p); +} + +/* Send basic block requests */ +static int __blk_send_generic(struct request_queue *q, struct gendisk *bd_disk, + int cmd, int data) +{ + struct request *rq; + int err; + + rq = blk_get_request(q, REQ_OP_DRV_OUT, 0); + if (IS_ERR(rq)) + return PTR_ERR(rq); + rq->timeout = BLK_DEFAULT_SG_TIMEOUT; + scsi_req(rq)->cmd[0] = cmd; + scsi_req(rq)->cmd[4] = data; + scsi_req(rq)->cmd_len = 6; + blk_execute_rq(bd_disk, rq, 0); + err = scsi_req(rq)->result ? -EIO : 0; + blk_put_request(rq); + + return err; +} + +static inline int blk_send_start_stop(struct request_queue *q, + struct gendisk *bd_disk, int data) +{ + return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data); +} + +/* + * Check if the given command is allowed. + * + * Only a subset of commands are allowed for unprivileged users. Commands used + * to format the media, update the firmware, etc. are not permitted. + */ +bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode) +{ + /* root can do any command. */ + if (capable(CAP_SYS_RAWIO)) + return true; + + /* Anybody who can open the device can do a read-safe command */ + switch (cmd[0]) { + /* Basic read-only commands */ + case TEST_UNIT_READY: + case REQUEST_SENSE: + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case READ_BUFFER: + case READ_DEFECT_DATA: + case READ_CAPACITY: /* also GPCMD_READ_CDVD_CAPACITY */ + case READ_LONG: + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case LOG_SENSE: + case START_STOP: + case GPCMD_VERIFY_10: + case VERIFY_16: + case REPORT_LUNS: + case SERVICE_ACTION_IN_16: + case RECEIVE_DIAGNOSTIC: + case MAINTENANCE_IN: /* also GPCMD_SEND_KEY, which is a write command */ + case GPCMD_READ_BUFFER_CAPACITY: + /* Audio CD commands */ + case GPCMD_PLAY_CD: + case GPCMD_PLAY_AUDIO_10: + case GPCMD_PLAY_AUDIO_MSF: + case GPCMD_PLAY_AUDIO_TI: + case GPCMD_PAUSE_RESUME: + /* CD/DVD data reading */ + case GPCMD_READ_CD: + case GPCMD_READ_CD_MSF: + case GPCMD_READ_DISC_INFO: + case GPCMD_READ_DVD_STRUCTURE: + case GPCMD_READ_HEADER: + case GPCMD_READ_TRACK_RZONE_INFO: + case GPCMD_READ_SUBCHANNEL: + case GPCMD_READ_TOC_PMA_ATIP: + case GPCMD_REPORT_KEY: + case GPCMD_SCAN: + case GPCMD_GET_CONFIGURATION: + case GPCMD_READ_FORMAT_CAPACITIES: + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + case GPCMD_GET_PERFORMANCE: + case GPCMD_SEEK: + case GPCMD_STOP_PLAY_SCAN: + /* ZBC */ + case ZBC_IN: + return true; + /* Basic writing commands */ + case WRITE_6: + case WRITE_10: + case WRITE_VERIFY: + case WRITE_12: + case WRITE_VERIFY_12: + case WRITE_16: + case WRITE_LONG: + case WRITE_LONG_2: + case WRITE_SAME: + case WRITE_SAME_16: + case WRITE_SAME_32: + case ERASE: + case GPCMD_MODE_SELECT_10: + case MODE_SELECT: + case LOG_SELECT: + case GPCMD_BLANK: + case GPCMD_CLOSE_TRACK: + case GPCMD_FLUSH_CACHE: + case GPCMD_FORMAT_UNIT: + case GPCMD_REPAIR_RZONE_TRACK: + case GPCMD_RESERVE_RZONE_TRACK: + case GPCMD_SEND_DVD_STRUCTURE: + case GPCMD_SEND_EVENT: + case GPCMD_SEND_OPC: + case GPCMD_SEND_CUE_SHEET: + case GPCMD_SET_SPEED: + case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + case GPCMD_LOAD_UNLOAD: + case GPCMD_SET_STREAMING: + case GPCMD_SET_READ_AHEAD: + /* ZBC */ + case ZBC_OUT: + return (mode & FMODE_WRITE); + default: + return false; + } +} +EXPORT_SYMBOL(scsi_cmd_allowed); + +static int scsi_fill_sghdr_rq(struct request_queue *q, struct request *rq, + struct sg_io_hdr *hdr, fmode_t mode) +{ + struct scsi_request *req = scsi_req(rq); + + if (copy_from_user(req->cmd, hdr->cmdp, hdr->cmd_len)) + return -EFAULT; + if (!scsi_cmd_allowed(req->cmd, mode)) + return -EPERM; + + /* + * fill in request structure + */ + req->cmd_len = hdr->cmd_len; + + rq->timeout = msecs_to_jiffies(hdr->timeout); + if (!rq->timeout) + rq->timeout = q->sg_timeout; + if (!rq->timeout) + rq->timeout = BLK_DEFAULT_SG_TIMEOUT; + if (rq->timeout < BLK_MIN_SG_TIMEOUT) + rq->timeout = BLK_MIN_SG_TIMEOUT; + + return 0; +} + +static int scsi_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, + struct bio *bio) +{ + struct scsi_request *req = scsi_req(rq); + int r, ret = 0; + + /* + * fill in all the output members + */ + hdr->status = req->result & 0xff; + hdr->masked_status = status_byte(req->result); + hdr->msg_status = COMMAND_COMPLETE; + hdr->host_status = host_byte(req->result); + hdr->driver_status = 0; + if (scsi_status_is_check_condition(hdr->status)) + hdr->driver_status = DRIVER_SENSE; + hdr->info = 0; + if (hdr->masked_status || hdr->host_status || hdr->driver_status) + hdr->info |= SG_INFO_CHECK; + hdr->resid = req->resid_len; + hdr->sb_len_wr = 0; + + if (req->sense_len && hdr->sbp) { + int len = min((unsigned int) hdr->mx_sb_len, req->sense_len); + + if (!copy_to_user(hdr->sbp, req->sense, len)) + hdr->sb_len_wr = len; + else + ret = -EFAULT; + } + + r = blk_rq_unmap_user(bio); + if (!ret) + ret = r; + + return ret; +} + +static int sg_io(struct request_queue *q, struct gendisk *bd_disk, + struct sg_io_hdr *hdr, fmode_t mode) +{ + unsigned long start_time; + ssize_t ret = 0; + int writing = 0; + int at_head = 0; + struct request *rq; + struct scsi_request *req; + struct bio *bio; + + if (hdr->interface_id != 'S') + return -EINVAL; + + if (hdr->dxfer_len > (queue_max_hw_sectors(q) << 9)) + return -EIO; + + if (hdr->dxfer_len) + switch (hdr->dxfer_direction) { + default: + return -EINVAL; + case SG_DXFER_TO_DEV: + writing = 1; + break; + case SG_DXFER_TO_FROM_DEV: + case SG_DXFER_FROM_DEV: + break; + } + if (hdr->flags & SG_FLAG_Q_AT_HEAD) + at_head = 1; + + ret = -ENOMEM; + rq = blk_get_request(q, writing ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + if (IS_ERR(rq)) + return PTR_ERR(rq); + req = scsi_req(rq); + + if (hdr->cmd_len > BLK_MAX_CDB) { + req->cmd = kzalloc(hdr->cmd_len, GFP_KERNEL); + if (!req->cmd) + goto out_put_request; + } + + ret = scsi_fill_sghdr_rq(q, rq, hdr, mode); + if (ret < 0) + goto out_free_cdb; + + ret = 0; + if (hdr->iovec_count) { + struct iov_iter i; + struct iovec *iov = NULL; + + ret = import_iovec(rq_data_dir(rq), hdr->dxferp, + hdr->iovec_count, 0, &iov, &i); + if (ret < 0) + goto out_free_cdb; + + /* SG_IO howto says that the shorter of the two wins */ + iov_iter_truncate(&i, hdr->dxfer_len); + + ret = blk_rq_map_user_iov(q, rq, NULL, &i, GFP_KERNEL); + kfree(iov); + } else if (hdr->dxfer_len) + ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, + GFP_KERNEL); + + if (ret) + goto out_free_cdb; + + bio = rq->bio; + req->retries = 0; + + start_time = jiffies; + + blk_execute_rq(bd_disk, rq, at_head); + + hdr->duration = jiffies_to_msecs(jiffies - start_time); + + ret = scsi_complete_sghdr_rq(rq, hdr, bio); + +out_free_cdb: + scsi_req_free_cmd(req); +out_put_request: + blk_put_request(rq); + return ret; +} + +/** + * sg_scsi_ioctl -- handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl + * @q: request queue to send scsi commands down + * @disk: gendisk to operate on (option) + * @mode: mode used to open the file through which the ioctl has been + * submitted + * @sic: userspace structure describing the command to perform + * + * Send down the scsi command described by @sic to the device below + * the request queue @q. If @file is non-NULL it's used to perform + * fine-grained permission checks that allow users to send down + * non-destructive SCSI commands. If the caller has a struct gendisk + * available it should be passed in as @disk to allow the low level + * driver to use the information contained in it. A non-NULL @disk + * is only allowed if the caller knows that the low level driver doesn't + * need it (e.g. in the scsi subsystem). + * + * Notes: + * - This interface is deprecated - users should use the SG_IO + * interface instead, as this is a more flexible approach to + * performing SCSI commands on a device. + * - The SCSI command length is determined by examining the 1st byte + * of the given command. There is no way to override this. + * - Data transfers are limited to PAGE_SIZE + * - The length (x + y) must be at least OMAX_SB_LEN bytes long to + * accommodate the sense buffer when an error occurs. + * The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that + * old code will not be surprised. + * - If a Unix error occurs (e.g. ENOMEM) then the user will receive + * a negative return and the Unix error code in 'errno'. + * If the SCSI command succeeds then 0 is returned. + * Positive numbers returned are the compacted SCSI error codes (4 + * bytes in one int) where the lowest byte is the SCSI status. + */ +int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, + struct scsi_ioctl_command __user *sic) +{ + enum { OMAX_SB_LEN = 16 }; /* For backward compatibility */ + struct request *rq; + struct scsi_request *req; + int err; + unsigned int in_len, out_len, bytes, opcode, cmdlen; + char *buffer = NULL; + + if (!sic) + return -EINVAL; + + /* + * get in an out lengths, verify they don't exceed a page worth of data + */ + if (get_user(in_len, &sic->inlen)) + return -EFAULT; + if (get_user(out_len, &sic->outlen)) + return -EFAULT; + if (in_len > PAGE_SIZE || out_len > PAGE_SIZE) + return -EINVAL; + if (get_user(opcode, sic->data)) + return -EFAULT; + + bytes = max(in_len, out_len); + if (bytes) { + buffer = kzalloc(bytes, GFP_NOIO | GFP_USER | __GFP_NOWARN); + if (!buffer) + return -ENOMEM; + + } + + rq = blk_get_request(q, in_len ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto error_free_buffer; + } + req = scsi_req(rq); + + cmdlen = COMMAND_SIZE(opcode); + + /* + * get command and data to send to device, if any + */ + err = -EFAULT; + req->cmd_len = cmdlen; + if (copy_from_user(req->cmd, sic->data, cmdlen)) + goto error; + + if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) + goto error; + + err = -EPERM; + if (!scsi_cmd_allowed(req->cmd, mode)) + goto error; + + /* default. possible overridden later */ + req->retries = 5; + + switch (opcode) { + case SEND_DIAGNOSTIC: + case FORMAT_UNIT: + rq->timeout = FORMAT_UNIT_TIMEOUT; + req->retries = 1; + break; + case START_STOP: + rq->timeout = START_STOP_TIMEOUT; + break; + case MOVE_MEDIUM: + rq->timeout = MOVE_MEDIUM_TIMEOUT; + break; + case READ_ELEMENT_STATUS: + rq->timeout = READ_ELEMENT_STATUS_TIMEOUT; + break; + case READ_DEFECT_DATA: + rq->timeout = READ_DEFECT_DATA_TIMEOUT; + req->retries = 1; + break; + default: + rq->timeout = BLK_DEFAULT_SG_TIMEOUT; + break; + } + + if (bytes) { + err = blk_rq_map_kern(q, rq, buffer, bytes, GFP_NOIO); + if (err) + goto error; + } + + blk_execute_rq(disk, rq, 0); + + err = req->result & 0xff; /* only 8 bit SCSI status */ + if (err) { + if (req->sense_len && req->sense) { + bytes = (OMAX_SB_LEN > req->sense_len) ? + req->sense_len : OMAX_SB_LEN; + if (copy_to_user(sic->data, req->sense, bytes)) + err = -EFAULT; + } + } else { + if (copy_to_user(sic->data, buffer, out_len)) + err = -EFAULT; + } + +error: + blk_put_request(rq); + +error_free_buffer: + kfree(buffer); + + return err; +} +EXPORT_SYMBOL_GPL(sg_scsi_ioctl); + +int put_sg_io_hdr(const struct sg_io_hdr *hdr, void __user *argp) +{ +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) { + struct compat_sg_io_hdr hdr32 = { + .interface_id = hdr->interface_id, + .dxfer_direction = hdr->dxfer_direction, + .cmd_len = hdr->cmd_len, + .mx_sb_len = hdr->mx_sb_len, + .iovec_count = hdr->iovec_count, + .dxfer_len = hdr->dxfer_len, + .dxferp = (uintptr_t)hdr->dxferp, + .cmdp = (uintptr_t)hdr->cmdp, + .sbp = (uintptr_t)hdr->sbp, + .timeout = hdr->timeout, + .flags = hdr->flags, + .pack_id = hdr->pack_id, + .usr_ptr = (uintptr_t)hdr->usr_ptr, + .status = hdr->status, + .masked_status = hdr->masked_status, + .msg_status = hdr->msg_status, + .sb_len_wr = hdr->sb_len_wr, + .host_status = hdr->host_status, + .driver_status = hdr->driver_status, + .resid = hdr->resid, + .duration = hdr->duration, + .info = hdr->info, + }; + + if (copy_to_user(argp, &hdr32, sizeof(hdr32))) + return -EFAULT; + + return 0; + } +#endif + + if (copy_to_user(argp, hdr, sizeof(*hdr))) + return -EFAULT; + + return 0; +} +EXPORT_SYMBOL(put_sg_io_hdr); + +int get_sg_io_hdr(struct sg_io_hdr *hdr, const void __user *argp) +{ +#ifdef CONFIG_COMPAT + struct compat_sg_io_hdr hdr32; + + if (in_compat_syscall()) { + if (copy_from_user(&hdr32, argp, sizeof(hdr32))) + return -EFAULT; + + *hdr = (struct sg_io_hdr) { + .interface_id = hdr32.interface_id, + .dxfer_direction = hdr32.dxfer_direction, + .cmd_len = hdr32.cmd_len, + .mx_sb_len = hdr32.mx_sb_len, + .iovec_count = hdr32.iovec_count, + .dxfer_len = hdr32.dxfer_len, + .dxferp = compat_ptr(hdr32.dxferp), + .cmdp = compat_ptr(hdr32.cmdp), + .sbp = compat_ptr(hdr32.sbp), + .timeout = hdr32.timeout, + .flags = hdr32.flags, + .pack_id = hdr32.pack_id, + .usr_ptr = compat_ptr(hdr32.usr_ptr), + .status = hdr32.status, + .masked_status = hdr32.masked_status, + .msg_status = hdr32.msg_status, + .sb_len_wr = hdr32.sb_len_wr, + .host_status = hdr32.host_status, + .driver_status = hdr32.driver_status, + .resid = hdr32.resid, + .duration = hdr32.duration, + .info = hdr32.info, + }; + + return 0; + } +#endif + + if (copy_from_user(hdr, argp, sizeof(*hdr))) + return -EFAULT; + + return 0; +} +EXPORT_SYMBOL(get_sg_io_hdr); + +#ifdef CONFIG_COMPAT +struct compat_cdrom_generic_command { + unsigned char cmd[CDROM_PACKET_SIZE]; + compat_caddr_t buffer; + compat_uint_t buflen; + compat_int_t stat; + compat_caddr_t sense; + unsigned char data_direction; + unsigned char pad[3]; + compat_int_t quiet; + compat_int_t timeout; + compat_caddr_t unused; +}; +#endif + +static int scsi_get_cdrom_generic_arg(struct cdrom_generic_command *cgc, + const void __user *arg) +{ +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) { + struct compat_cdrom_generic_command cgc32; + + if (copy_from_user(&cgc32, arg, sizeof(cgc32))) + return -EFAULT; + + *cgc = (struct cdrom_generic_command) { + .buffer = compat_ptr(cgc32.buffer), + .buflen = cgc32.buflen, + .stat = cgc32.stat, + .sense = compat_ptr(cgc32.sense), + .data_direction = cgc32.data_direction, + .quiet = cgc32.quiet, + .timeout = cgc32.timeout, + .unused = compat_ptr(cgc32.unused), + }; + memcpy(&cgc->cmd, &cgc32.cmd, CDROM_PACKET_SIZE); + return 0; + } +#endif + if (copy_from_user(cgc, arg, sizeof(*cgc))) + return -EFAULT; + + return 0; +} + +static int scsi_put_cdrom_generic_arg(const struct cdrom_generic_command *cgc, + void __user *arg) +{ +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) { + struct compat_cdrom_generic_command cgc32 = { + .buffer = (uintptr_t)(cgc->buffer), + .buflen = cgc->buflen, + .stat = cgc->stat, + .sense = (uintptr_t)(cgc->sense), + .data_direction = cgc->data_direction, + .quiet = cgc->quiet, + .timeout = cgc->timeout, + .unused = (uintptr_t)(cgc->unused), + }; + memcpy(&cgc32.cmd, &cgc->cmd, CDROM_PACKET_SIZE); + + if (copy_to_user(arg, &cgc32, sizeof(cgc32))) + return -EFAULT; + + return 0; + } +#endif + if (copy_to_user(arg, cgc, sizeof(*cgc))) + return -EFAULT; + + return 0; +} + +static int scsi_cdrom_send_packet(struct request_queue *q, + struct gendisk *bd_disk, + fmode_t mode, void __user *arg) +{ + struct cdrom_generic_command cgc; + struct sg_io_hdr hdr; + int err; + + err = scsi_get_cdrom_generic_arg(&cgc, arg); + if (err) + return err; + + cgc.timeout = clock_t_to_jiffies(cgc.timeout); + memset(&hdr, 0, sizeof(hdr)); + hdr.interface_id = 'S'; + hdr.cmd_len = sizeof(cgc.cmd); + hdr.dxfer_len = cgc.buflen; + switch (cgc.data_direction) { + case CGC_DATA_UNKNOWN: + hdr.dxfer_direction = SG_DXFER_UNKNOWN; + break; + case CGC_DATA_WRITE: + hdr.dxfer_direction = SG_DXFER_TO_DEV; + break; + case CGC_DATA_READ: + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case CGC_DATA_NONE: + hdr.dxfer_direction = SG_DXFER_NONE; + break; + default: + return -EINVAL; + } + + hdr.dxferp = cgc.buffer; + hdr.sbp = cgc.sense; + if (hdr.sbp) + hdr.mx_sb_len = sizeof(struct request_sense); + hdr.timeout = jiffies_to_msecs(cgc.timeout); + hdr.cmdp = ((struct cdrom_generic_command __user *) arg)->cmd; + hdr.cmd_len = sizeof(cgc.cmd); + + err = sg_io(q, bd_disk, &hdr, mode); + if (err == -EFAULT) + return -EFAULT; + + if (hdr.status) + return -EIO; + + cgc.stat = err; + cgc.buflen = hdr.resid; + if (scsi_put_cdrom_generic_arg(&cgc, arg)) + return -EFAULT; + + return err; +} + /** * scsi_ioctl - Dispatch ioctl to scsi device * @sdev: scsi device receiving ioctl @@ -225,13 +926,42 @@ int scsi_ioctl(struct scsi_device *sdev, struct gendisk *disk, fmode_t mode, break; } - if (cmd != SCSI_IOCTL_GET_IDLUN && cmd != SCSI_IOCTL_GET_BUS_NUMBER) { - error = scsi_cmd_ioctl(q, disk, mode, cmd, arg); - if (error != -ENOTTY) + switch (cmd) { + case SG_GET_VERSION_NUM: + return sg_get_version(arg); + case SG_SET_TIMEOUT: + return sg_set_timeout(q, arg); + case SG_GET_TIMEOUT: + return sg_get_timeout(q); + case SG_GET_RESERVED_SIZE: + return sg_get_reserved_size(q, arg); + case SG_SET_RESERVED_SIZE: + return sg_set_reserved_size(q, arg); + case SG_EMULATED_HOST: + return sg_emulated_host(q, arg); + case SG_IO: { + struct sg_io_hdr hdr; + + error = get_sg_io_hdr(&hdr, arg); + if (error) return error; - } - switch (cmd) { + error = sg_io(q, disk, &hdr, mode); + if (error == -EFAULT) + return error; + + if (put_sg_io_hdr(&hdr, arg)) + return -EFAULT; + return 0; + } + case SCSI_IOCTL_SEND_COMMAND: + return sg_scsi_ioctl(q, disk, mode, arg); + case CDROM_SEND_PACKET: + return scsi_cdrom_send_packet(q, disk, mode, arg); + case CDROMCLOSETRAY: + return blk_send_start_stop(q, disk, 0x03); + case CDROMEJECT: + return blk_send_start_stop(q, disk, 0x02); case SCSI_IOCTL_GET_IDLUN: { struct scsi_idlun v = { .dev_id = (sdev->id & 0xff) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e28679e63373..8c617a5a5d61 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -28,8 +28,6 @@ #include struct module; -struct scsi_ioctl_command; - struct request_queue; struct elevator_queue; struct blk_trace; @@ -888,13 +886,6 @@ extern blk_status_t blk_insert_cloned_request(struct request_queue *q, struct request *rq); int blk_rq_append_bio(struct request *rq, struct bio *bio); extern void blk_queue_split(struct bio **); -extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, - unsigned int, void __user *); -extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, - struct scsi_ioctl_command __user *); -extern int get_sg_io_hdr(struct sg_io_hdr *hdr, const void __user *argp); -extern int put_sg_io_hdr(const struct sg_io_hdr *hdr, void __user *argp); - extern int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags); extern void blk_queue_exit(struct request_queue *q); extern void blk_sync_queue(struct request_queue *q); @@ -1343,8 +1334,6 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask, 0); } -bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode); - static inline bool bdev_is_partition(struct block_device *bdev) { return bdev->bd_partno; diff --git a/include/scsi/scsi_ioctl.h b/include/scsi/scsi_ioctl.h index defbe8084eb8..b3918fded464 100644 --- a/include/scsi/scsi_ioctl.h +++ b/include/scsi/scsi_ioctl.h @@ -20,6 +20,7 @@ struct gendisk; struct scsi_device; +struct sg_io_hdr; /* * Structures used for scsi_ioctl et al. @@ -46,6 +47,11 @@ int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd, bool ndelay); int scsi_ioctl(struct scsi_device *sdev, struct gendisk *disk, fmode_t mode, int cmd, void __user *arg); +int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, + struct scsi_ioctl_command __user *argp); +int get_sg_io_hdr(struct sg_io_hdr *hdr, const void __user *argp); +int put_sg_io_hdr(const struct sg_io_hdr *hdr, void __user *argp); +bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode); #endif /* __KERNEL__ */ #endif /* _SCSI_IOCTL_H */ -- cgit v1.2.3-71-gd317 From ead09dd3aed5cc6a6c6288a87a5bfa9bbc8d5ecf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 29 Jul 2021 08:48:42 +0200 Subject: scsi: bsg: Simplify device registration Use the per-device cdev_device_interface to store the bsg data in the char device inode, and thus remove the need to embedd the bsg_class_device structure in the request_queue. Link: https://lore.kernel.org/r/20210729064845.1044147-2-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/bsg-lib.c | 11 +- block/bsg.c | 304 +++++++++++---------------------------------- drivers/scsi/scsi_bsg.c | 5 +- drivers/scsi/scsi_priv.h | 11 +- drivers/scsi/scsi_sysfs.c | 24 ++-- include/linux/blkdev.h | 6 - include/linux/bsg-lib.h | 1 + include/linux/bsg.h | 21 +--- include/scsi/scsi_device.h | 2 + 9 files changed, 108 insertions(+), 277 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/bsg-lib.c b/block/bsg-lib.c index a89d80102304..fe43f5fda6e5 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -6,6 +6,7 @@ * Copyright (C) 2011 Red Hat, Inc. All rights reserved. * Copyright (C) 2011 Mike Christie */ +#include #include #include #include @@ -19,6 +20,7 @@ struct bsg_set { struct blk_mq_tag_set tag_set; + struct bsg_device *bd; bsg_job_fn *job_fn; bsg_timeout_fn *timeout_fn; }; @@ -327,7 +329,7 @@ void bsg_remove_queue(struct request_queue *q) struct bsg_set *bset = container_of(q->tag_set, struct bsg_set, tag_set); - bsg_unregister_queue(q); + bsg_unregister_queue(bset->bd); blk_cleanup_queue(q); blk_mq_free_tag_set(&bset->tag_set); kfree(bset); @@ -396,10 +398,9 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, q->queuedata = dev; blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); - ret = bsg_register_queue(q, dev, name, &bsg_transport_ops); - if (ret) { - printk(KERN_ERR "%s: bsg interface failed to " - "initialize - register queue\n", dev->kobj.name); + bset->bd = bsg_register_queue(q, dev, name, &bsg_transport_ops); + if (IS_ERR(bset->bd)) { + ret = PTR_ERR(bset->bd); goto out_cleanup_queue; } diff --git a/block/bsg.c b/block/bsg.c index 3dbfd2c6aef3..83a095185d33 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -20,38 +20,29 @@ #define BSG_DESCRIPTION "Block layer SCSI generic (bsg) driver" #define BSG_VERSION "0.4" -#define bsg_dbg(bd, fmt, ...) \ - pr_debug("%s: " fmt, (bd)->name, ##__VA_ARGS__) - struct bsg_device { struct request_queue *queue; - spinlock_t lock; - struct hlist_node dev_list; - refcount_t ref_count; - char name[20]; + const struct bsg_ops *ops; + struct device device; + struct cdev cdev; int max_queue; }; +static inline struct bsg_device *to_bsg_device(struct inode *inode) +{ + return container_of(inode->i_cdev, struct bsg_device, cdev); +} + #define BSG_DEFAULT_CMDS 64 #define BSG_MAX_DEVS 32768 -static DEFINE_MUTEX(bsg_mutex); -static DEFINE_IDR(bsg_minor_idr); - -#define BSG_LIST_ARRAY_SIZE 8 -static struct hlist_head bsg_device_list[BSG_LIST_ARRAY_SIZE]; - +static DEFINE_IDA(bsg_minor_ida); static struct class *bsg_class; static int bsg_major; -static inline struct hlist_head *bsg_dev_idx_hash(int index) -{ - return &bsg_device_list[index & (BSG_LIST_ARRAY_SIZE - 1)]; -} - #define uptr64(val) ((void __user *)(uintptr_t)(val)) -static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) +static int bsg_sg_io(struct bsg_device *bd, fmode_t mode, void __user *uarg) { struct request *rq; struct bio *bio; @@ -61,21 +52,18 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) if (copy_from_user(&hdr, uarg, sizeof(hdr))) return -EFAULT; - if (!q->bsg_dev.class_dev) - return -ENXIO; - if (hdr.guard != 'Q') return -EINVAL; - ret = q->bsg_dev.ops->check_proto(&hdr); + ret = bd->ops->check_proto(&hdr); if (ret) return ret; - rq = blk_get_request(q, hdr.dout_xfer_len ? + rq = blk_get_request(bd->queue, hdr.dout_xfer_len ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); if (IS_ERR(rq)) return PTR_ERR(rq); - ret = q->bsg_dev.ops->fill_hdr(rq, &hdr, mode); + ret = bd->ops->fill_hdr(rq, &hdr, mode); if (ret) { blk_put_request(rq); return ret; @@ -83,17 +71,17 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) rq->timeout = msecs_to_jiffies(hdr.timeout); if (!rq->timeout) - rq->timeout = q->sg_timeout; + rq->timeout = rq->q->sg_timeout; if (!rq->timeout) rq->timeout = BLK_DEFAULT_SG_TIMEOUT; if (rq->timeout < BLK_MIN_SG_TIMEOUT) rq->timeout = BLK_MIN_SG_TIMEOUT; if (hdr.dout_xfer_len) { - ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.dout_xferp), + ret = blk_rq_map_user(rq->q, rq, NULL, uptr64(hdr.dout_xferp), hdr.dout_xfer_len, GFP_KERNEL); } else if (hdr.din_xfer_len) { - ret = blk_rq_map_user(q, rq, NULL, uptr64(hdr.din_xferp), + ret = blk_rq_map_user(rq->q, rq, NULL, uptr64(hdr.din_xferp), hdr.din_xfer_len, GFP_KERNEL); } @@ -103,171 +91,50 @@ static int bsg_sg_io(struct request_queue *q, fmode_t mode, void __user *uarg) bio = rq->bio; blk_execute_rq(NULL, rq, !(hdr.flags & BSG_FLAG_Q_AT_TAIL)); - ret = rq->q->bsg_dev.ops->complete_rq(rq, &hdr); + ret = bd->ops->complete_rq(rq, &hdr); blk_rq_unmap_user(bio); out_free_rq: - rq->q->bsg_dev.ops->free_rq(rq); + bd->ops->free_rq(rq); blk_put_request(rq); if (!ret && copy_to_user(uarg, &hdr, sizeof(hdr))) return -EFAULT; return ret; } -static struct bsg_device *bsg_alloc_device(void) -{ - struct bsg_device *bd; - - bd = kzalloc(sizeof(struct bsg_device), GFP_KERNEL); - if (unlikely(!bd)) - return NULL; - - spin_lock_init(&bd->lock); - bd->max_queue = BSG_DEFAULT_CMDS; - INIT_HLIST_NODE(&bd->dev_list); - return bd; -} - -static int bsg_put_device(struct bsg_device *bd) -{ - struct request_queue *q = bd->queue; - - mutex_lock(&bsg_mutex); - - if (!refcount_dec_and_test(&bd->ref_count)) { - mutex_unlock(&bsg_mutex); - return 0; - } - - hlist_del(&bd->dev_list); - mutex_unlock(&bsg_mutex); - - bsg_dbg(bd, "tearing down\n"); - - /* - * close can always block - */ - kfree(bd); - blk_put_queue(q); - return 0; -} - -static struct bsg_device *bsg_add_device(struct inode *inode, - struct request_queue *rq, - struct file *file) -{ - struct bsg_device *bd; - unsigned char buf[32]; - - lockdep_assert_held(&bsg_mutex); - - if (!blk_get_queue(rq)) - return ERR_PTR(-ENXIO); - - bd = bsg_alloc_device(); - if (!bd) { - blk_put_queue(rq); - return ERR_PTR(-ENOMEM); - } - - bd->queue = rq; - - refcount_set(&bd->ref_count, 1); - hlist_add_head(&bd->dev_list, bsg_dev_idx_hash(iminor(inode))); - - strncpy(bd->name, dev_name(rq->bsg_dev.class_dev), sizeof(bd->name) - 1); - bsg_dbg(bd, "bound to <%s>, max queue %d\n", - format_dev_t(buf, inode->i_rdev), bd->max_queue); - - return bd; -} - -static struct bsg_device *__bsg_get_device(int minor, struct request_queue *q) -{ - struct bsg_device *bd; - - lockdep_assert_held(&bsg_mutex); - - hlist_for_each_entry(bd, bsg_dev_idx_hash(minor), dev_list) { - if (bd->queue == q) { - refcount_inc(&bd->ref_count); - goto found; - } - } - bd = NULL; -found: - return bd; -} - -static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) -{ - struct bsg_device *bd; - struct bsg_class_device *bcd; - - /* - * find the class device - */ - mutex_lock(&bsg_mutex); - bcd = idr_find(&bsg_minor_idr, iminor(inode)); - - if (!bcd) { - bd = ERR_PTR(-ENODEV); - goto out_unlock; - } - - bd = __bsg_get_device(iminor(inode), bcd->queue); - if (!bd) - bd = bsg_add_device(inode, bcd->queue, file); - -out_unlock: - mutex_unlock(&bsg_mutex); - return bd; -} - static int bsg_open(struct inode *inode, struct file *file) { - struct bsg_device *bd; - - bd = bsg_get_device(inode, file); - - if (IS_ERR(bd)) - return PTR_ERR(bd); - - file->private_data = bd; + if (!blk_get_queue(to_bsg_device(inode)->queue)) + return -ENXIO; return 0; } static int bsg_release(struct inode *inode, struct file *file) { - struct bsg_device *bd = file->private_data; - - file->private_data = NULL; - return bsg_put_device(bd); + blk_put_queue(to_bsg_device(inode)->queue); + return 0; } static int bsg_get_command_q(struct bsg_device *bd, int __user *uarg) { - return put_user(bd->max_queue, uarg); + return put_user(READ_ONCE(bd->max_queue), uarg); } static int bsg_set_command_q(struct bsg_device *bd, int __user *uarg) { - int queue; + int max_queue; - if (get_user(queue, uarg)) + if (get_user(max_queue, uarg)) return -EFAULT; - if (queue < 1) + if (max_queue < 1) return -EINVAL; - - spin_lock_irq(&bd->lock); - bd->max_queue = queue; - spin_unlock_irq(&bd->lock); + WRITE_ONCE(bd->max_queue, max_queue); return 0; } static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct bsg_device *bd = file->private_data; + struct bsg_device *bd = to_bsg_device(file_inode(file)); struct request_queue *q = bd->queue; void __user *uarg = (void __user *) arg; int __user *intp = uarg; @@ -312,7 +179,7 @@ static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case SG_EMULATED_HOST: return put_user(1, intp); case SG_IO: - return bsg_sg_io(q, file->f_mode, uarg); + return bsg_sg_io(bd, file->f_mode, uarg); case SCSI_IOCTL_SEND_COMMAND: pr_warn_ratelimited("%s: calling unsupported SCSI_IOCTL_SEND_COMMAND\n", current->comm); @@ -331,83 +198,66 @@ static const struct file_operations bsg_fops = { .llseek = default_llseek, }; -void bsg_unregister_queue(struct request_queue *q) +void bsg_unregister_queue(struct bsg_device *bd) { - struct bsg_class_device *bcd = &q->bsg_dev; - - if (!bcd->class_dev) - return; - - mutex_lock(&bsg_mutex); - idr_remove(&bsg_minor_idr, bcd->minor); - if (q->kobj.sd) - sysfs_remove_link(&q->kobj, "bsg"); - device_unregister(bcd->class_dev); - bcd->class_dev = NULL; - mutex_unlock(&bsg_mutex); + if (bd->queue->kobj.sd) + sysfs_remove_link(&bd->queue->kobj, "bsg"); + cdev_device_del(&bd->cdev, &bd->device); + ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt)); + kfree(bd); } EXPORT_SYMBOL_GPL(bsg_unregister_queue); -int bsg_register_queue(struct request_queue *q, struct device *parent, - const char *name, const struct bsg_ops *ops) +struct bsg_device *bsg_register_queue(struct request_queue *q, + struct device *parent, const char *name, + const struct bsg_ops *ops) { - struct bsg_class_device *bcd; - dev_t dev; + struct bsg_device *bd; int ret; - struct device *class_dev = NULL; - - /* - * we need a proper transport to send commands, not a stacked device - */ - if (!queue_is_mq(q)) - return 0; - bcd = &q->bsg_dev; - memset(bcd, 0, sizeof(*bcd)); - - mutex_lock(&bsg_mutex); + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return ERR_PTR(-ENOMEM); + bd->max_queue = BSG_DEFAULT_CMDS; + bd->queue = q; + bd->ops = ops; - ret = idr_alloc(&bsg_minor_idr, bcd, 0, BSG_MAX_DEVS, GFP_KERNEL); + ret = ida_simple_get(&bsg_minor_ida, 0, BSG_MAX_DEVS, GFP_KERNEL); if (ret < 0) { - if (ret == -ENOSPC) { - printk(KERN_ERR "bsg: too many bsg devices\n"); - ret = -EINVAL; - } - goto unlock; - } - - bcd->minor = ret; - bcd->queue = q; - bcd->ops = ops; - dev = MKDEV(bsg_major, bcd->minor); - class_dev = device_create(bsg_class, parent, dev, NULL, "%s", name); - if (IS_ERR(class_dev)) { - ret = PTR_ERR(class_dev); - goto idr_remove; + if (ret == -ENOSPC) + dev_err(parent, "bsg: too many bsg devices\n"); + goto out_kfree; } - bcd->class_dev = class_dev; + bd->device.devt = MKDEV(bsg_major, ret); + bd->device.class = bsg_class; + bd->device.parent = parent; + dev_set_name(&bd->device, "%s", name); + device_initialize(&bd->device); + + cdev_init(&bd->cdev, &bsg_fops); + bd->cdev.owner = THIS_MODULE; + ret = cdev_device_add(&bd->cdev, &bd->device); + if (ret) + goto out_ida_remove; if (q->kobj.sd) { - ret = sysfs_create_link(&q->kobj, &bcd->class_dev->kobj, "bsg"); + ret = sysfs_create_link(&q->kobj, &bd->device.kobj, "bsg"); if (ret) - goto unregister_class_dev; + goto out_device_del; } - mutex_unlock(&bsg_mutex); - return 0; + return bd; -unregister_class_dev: - device_unregister(class_dev); -idr_remove: - idr_remove(&bsg_minor_idr, bcd->minor); -unlock: - mutex_unlock(&bsg_mutex); - return ret; +out_device_del: + cdev_device_del(&bd->cdev, &bd->device); +out_ida_remove: + ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt)); +out_kfree: + kfree(bd); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(bsg_register_queue); -static struct cdev bsg_cdev; - static char *bsg_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "bsg/%s", dev_name(dev)); @@ -415,11 +265,8 @@ static char *bsg_devnode(struct device *dev, umode_t *mode) static int __init bsg_init(void) { - int ret, i; dev_t devid; - - for (i = 0; i < BSG_LIST_ARRAY_SIZE; i++) - INIT_HLIST_HEAD(&bsg_device_list[i]); + int ret; bsg_class = class_create(THIS_MODULE, "bsg"); if (IS_ERR(bsg_class)) @@ -429,19 +276,12 @@ static int __init bsg_init(void) ret = alloc_chrdev_region(&devid, 0, BSG_MAX_DEVS, "bsg"); if (ret) goto destroy_bsg_class; - bsg_major = MAJOR(devid); - cdev_init(&bsg_cdev, &bsg_fops); - ret = cdev_add(&bsg_cdev, MKDEV(bsg_major, 0), BSG_MAX_DEVS); - if (ret) - goto unregister_chrdev; - printk(KERN_INFO BSG_DESCRIPTION " version " BSG_VERSION " loaded (major %d)\n", bsg_major); return 0; -unregister_chrdev: - unregister_chrdev_region(MKDEV(bsg_major, 0), BSG_MAX_DEVS); + destroy_bsg_class: class_destroy(bsg_class); return ret; diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index 68f60316adf1..c0d41c45c2be 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -89,7 +89,8 @@ static const struct bsg_ops scsi_bsg_ops = { .free_rq = scsi_bsg_free_rq, }; -int scsi_bsg_register_queue(struct request_queue *q, struct device *parent) +struct bsg_device *scsi_bsg_register_queue(struct scsi_device *sdev) { - return bsg_register_queue(q, parent, dev_name(parent), &scsi_bsg_ops); + return bsg_register_queue(sdev->request_queue, &sdev->sdev_gendev, + dev_name(&sdev->sdev_gendev), &scsi_bsg_ops); } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 0a0db35bab04..6d9152031a40 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -7,6 +7,7 @@ #include #include +struct bsg_device; struct request_queue; struct request; struct scsi_cmnd; @@ -180,15 +181,7 @@ static inline void scsi_dh_add_device(struct scsi_device *sdev) { } static inline void scsi_dh_release_device(struct scsi_device *sdev) { } #endif -#ifdef CONFIG_BLK_DEV_BSG -int scsi_bsg_register_queue(struct request_queue *q, struct device *parent); -#else -static inline int scsi_bsg_register_queue(struct request_queue *q, - struct device *parent) -{ - return 0; -} -#endif +struct bsg_device *scsi_bsg_register_queue(struct scsi_device *sdev); extern int scsi_device_max_queue_depth(struct scsi_device *sdev); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 4ff9ac3296d8..07cee8dc4100 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -1327,7 +1328,6 @@ static int scsi_target_add(struct scsi_target *starget) int scsi_sysfs_add_sdev(struct scsi_device *sdev) { int error, i; - struct request_queue *rq = sdev->request_queue; struct scsi_target *starget = sdev->sdev_target; error = scsi_target_add(starget); @@ -1366,12 +1366,19 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) transport_add_device(&sdev->sdev_gendev); sdev->is_visible = 1; - error = scsi_bsg_register_queue(rq, &sdev->sdev_gendev); - if (error) - /* we're treating error on bsg register as non-fatal, - * so pretend nothing went wrong */ - sdev_printk(KERN_INFO, sdev, - "Failed to register bsg queue, errno=%d\n", error); + if (IS_ENABLED(CONFIG_BLK_DEV_BSG)) { + sdev->bsg_dev = scsi_bsg_register_queue(sdev); + if (IS_ERR(sdev->bsg_dev)) { + /* + * We're treating error on bsg register as non-fatal, so + * pretend nothing went wrong. + */ + sdev_printk(KERN_INFO, sdev, + "Failed to register bsg queue, errno=%d\n", + error); + sdev->bsg_dev = NULL; + } + } /* add additional host specific attributes */ if (sdev->host->hostt->sdev_attrs) { @@ -1433,7 +1440,8 @@ void __scsi_remove_device(struct scsi_device *sdev) sysfs_remove_groups(&sdev->sdev_gendev.kobj, sdev->host->hostt->sdev_groups); - bsg_unregister_queue(sdev->request_queue); + if (IS_ENABLED(CONFIG_BLK_DEV_BSG) && sdev->bsg_dev) + bsg_unregister_queue(sdev->bsg_dev); device_unregister(&sdev->sdev_dev); transport_remove_device(dev); device_del(dev); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 8c617a5a5d61..28957ccdd9c2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -33,7 +32,6 @@ struct elevator_queue; struct blk_trace; struct request; struct sg_io_hdr; -struct bsg_job; struct blkcg_gq; struct blk_flush_queue; struct pr_ops; @@ -535,10 +533,6 @@ struct request_queue { int mq_freeze_depth; -#if IS_ENABLED(CONFIG_BLK_DEV_BSG_COMMON) - struct bsg_class_device bsg_dev; -#endif - #ifdef CONFIG_BLK_DEV_THROTTLING /* Throttle data */ struct throtl_data *td; diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index 960988d42f77..6b211323a489 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h @@ -12,6 +12,7 @@ #include #include +struct bsg_job; struct request; struct device; struct scatterlist; diff --git a/include/linux/bsg.h b/include/linux/bsg.h index b887da20bd41..fa21f79beda2 100644 --- a/include/linux/bsg.h +++ b/include/linux/bsg.h @@ -4,10 +4,11 @@ #include +struct bsg_device; +struct device; struct request; struct request_queue; -#ifdef CONFIG_BLK_DEV_BSG_COMMON struct bsg_ops { int (*check_proto)(struct sg_io_v4 *hdr); int (*fill_hdr)(struct request *rq, struct sg_io_v4 *hdr, @@ -16,19 +17,9 @@ struct bsg_ops { void (*free_rq)(struct request *rq); }; -struct bsg_class_device { - struct device *class_dev; - int minor; - struct request_queue *queue; - const struct bsg_ops *ops; -}; +struct bsg_device *bsg_register_queue(struct request_queue *q, + struct device *parent, const char *name, + const struct bsg_ops *ops); +void bsg_unregister_queue(struct bsg_device *bcd); -int bsg_register_queue(struct request_queue *q, struct device *parent, - const char *name, const struct bsg_ops *ops); -void bsg_unregister_queue(struct request_queue *q); -#else -static inline void bsg_unregister_queue(struct request_queue *q) -{ -} -#endif /* CONFIG_BLK_DEV_BSG_COMMON */ #endif /* _LINUX_BSG_H */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index d1de21f799f4..99082da1b951 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -10,6 +10,7 @@ #include #include +struct bsg_device; struct device; struct request_queue; struct scsi_cmnd; @@ -235,6 +236,7 @@ struct scsi_device { size_t dma_drain_len; void *dma_drain_buf; + struct bsg_device *bsg_dev; unsigned char access_state; struct mutex state_mutex; enum scsi_device_state sdev_state; -- cgit v1.2.3-71-gd317 From cf93a27446fe1a6e0acb9bbedf5fce1e98e4fc5b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 29 Jul 2021 08:48:43 +0200 Subject: scsi: block: Remove BLK_SCSI_MAX_CMDS This was used for the table based SCSI passthough permission checking that is gone now. Link: https://lore.kernel.org/r/20210729064845.1044147-3-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- include/linux/blkdev.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 28957ccdd9c2..e0bb14acb708 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -271,9 +271,6 @@ enum blk_queue_state { #define BLK_TAG_ALLOC_FIFO 0 /* allocate starting from 0 */ #define BLK_TAG_ALLOC_RR 1 /* allocate starting from last allocated tag */ -#define BLK_SCSI_MAX_CMDS (256) -#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) - /* * Zoned block device models (zoned limit). * -- cgit v1.2.3-71-gd317 From 1e61c1a804d2a2a3c46add01cac3a6e9eca01080 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 29 Jul 2021 08:48:44 +0200 Subject: scsi: block: Remove the remaining SG_IO-related fields from struct request_queue Move the sg_timeout and sg_reserved_size fields into the bsg_device and scsi_device structures as they have nothing to do with generic block I/O. Note that these values are now separate for bsg vs. SCSI device node access, but that just matches how /dev/sg vs the other nodes has always behaved. Link: https://lore.kernel.org/r/20210729064845.1044147-4-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- block/blk-mq.c | 2 -- block/bsg.c | 13 ++++++---- drivers/scsi/scsi_ioctl.c | 63 ++++++++++++++++++++++------------------------ drivers/scsi/scsi_scan.c | 2 ++ include/linux/blkdev.h | 5 ---- include/scsi/scsi_device.h | 3 +++ 6 files changed, 43 insertions(+), 45 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/blk-mq.c b/block/blk-mq.c index 2c4ac51e54eb..495f508c6300 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -3298,8 +3298,6 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, set->map[HCTX_TYPE_POLL].nr_queues) blk_queue_flag_set(QUEUE_FLAG_POLL, q); - q->sg_reserved_size = INT_MAX; - INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work); INIT_LIST_HEAD(&q->requeue_list); spin_lock_init(&q->requeue_lock); diff --git a/block/bsg.c b/block/bsg.c index 83a095185d33..3ba74eec4ba2 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -26,6 +26,8 @@ struct bsg_device { struct device device; struct cdev cdev; int max_queue; + unsigned int timeout; + unsigned int reserved_size; }; static inline struct bsg_device *to_bsg_device(struct inode *inode) @@ -71,7 +73,7 @@ static int bsg_sg_io(struct bsg_device *bd, fmode_t mode, void __user *uarg) rq->timeout = msecs_to_jiffies(hdr.timeout); if (!rq->timeout) - rq->timeout = rq->q->sg_timeout; + rq->timeout = bd->timeout; if (!rq->timeout) rq->timeout = BLK_DEFAULT_SG_TIMEOUT; if (rq->timeout < BLK_MIN_SG_TIMEOUT) @@ -161,19 +163,19 @@ static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case SG_SET_TIMEOUT: if (get_user(val, intp)) return -EFAULT; - q->sg_timeout = clock_t_to_jiffies(val); + bd->timeout = clock_t_to_jiffies(val); return 0; case SG_GET_TIMEOUT: - return jiffies_to_clock_t(q->sg_timeout); + return jiffies_to_clock_t(bd->timeout); case SG_GET_RESERVED_SIZE: - return put_user(min(q->sg_reserved_size, queue_max_bytes(q)), + return put_user(min(bd->reserved_size, queue_max_bytes(q)), intp); case SG_SET_RESERVED_SIZE: if (get_user(val, intp)) return -EFAULT; if (val < 0) return -EINVAL; - q->sg_reserved_size = + bd->reserved_size = min_t(unsigned int, val, queue_max_bytes(q)); return 0; case SG_EMULATED_HOST: @@ -219,6 +221,7 @@ struct bsg_device *bsg_register_queue(struct request_queue *q, if (!bd) return ERR_PTR(-ENOMEM); bd->max_queue = BSG_DEFAULT_CMDS; + bd->reserved_size = INT_MAX; bd->queue = q; bd->ops = ops; diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 633f016c2bfe..7b2b0a1581f4 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -173,29 +173,25 @@ static int sg_get_version(int __user *p) return put_user(sg_version_num, p); } -static int sg_get_timeout(struct request_queue *q) -{ - return jiffies_to_clock_t(q->sg_timeout); -} - -static int sg_set_timeout(struct request_queue *q, int __user *p) +static int sg_set_timeout(struct scsi_device *sdev, int __user *p) { int timeout, err = get_user(timeout, p); if (!err) - q->sg_timeout = clock_t_to_jiffies(timeout); + sdev->sg_timeout = clock_t_to_jiffies(timeout); return err; } -static int sg_get_reserved_size(struct request_queue *q, int __user *p) +static int sg_get_reserved_size(struct scsi_device *sdev, int __user *p) { - int val = min(q->sg_reserved_size, queue_max_bytes(q)); + int val = min(sdev->sg_reserved_size, + queue_max_bytes(sdev->request_queue)); return put_user(val, p); } -static int sg_set_reserved_size(struct request_queue *q, int __user *p) +static int sg_set_reserved_size(struct scsi_device *sdev, int __user *p) { int size, err = get_user(size, p); @@ -205,7 +201,8 @@ static int sg_set_reserved_size(struct request_queue *q, int __user *p) if (size < 0) return -EINVAL; - q->sg_reserved_size = min_t(unsigned int, size, queue_max_bytes(q)); + sdev->sg_reserved_size = min_t(unsigned int, size, + queue_max_bytes(sdev->request_queue)); return 0; } @@ -345,7 +342,7 @@ bool scsi_cmd_allowed(unsigned char *cmd, fmode_t mode) } EXPORT_SYMBOL(scsi_cmd_allowed); -static int scsi_fill_sghdr_rq(struct request_queue *q, struct request *rq, +static int scsi_fill_sghdr_rq(struct scsi_device *sdev, struct request *rq, struct sg_io_hdr *hdr, fmode_t mode) { struct scsi_request *req = scsi_req(rq); @@ -362,7 +359,7 @@ static int scsi_fill_sghdr_rq(struct request_queue *q, struct request *rq, rq->timeout = msecs_to_jiffies(hdr->timeout); if (!rq->timeout) - rq->timeout = q->sg_timeout; + rq->timeout = sdev->sg_timeout; if (!rq->timeout) rq->timeout = BLK_DEFAULT_SG_TIMEOUT; if (rq->timeout < BLK_MIN_SG_TIMEOUT) @@ -409,7 +406,7 @@ static int scsi_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, return ret; } -static int sg_io(struct request_queue *q, struct gendisk *bd_disk, +static int sg_io(struct scsi_device *sdev, struct gendisk *disk, struct sg_io_hdr *hdr, fmode_t mode) { unsigned long start_time; @@ -423,7 +420,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, if (hdr->interface_id != 'S') return -EINVAL; - if (hdr->dxfer_len > (queue_max_hw_sectors(q) << 9)) + if (hdr->dxfer_len > (queue_max_hw_sectors(sdev->request_queue) << 9)) return -EIO; if (hdr->dxfer_len) @@ -441,7 +438,8 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, at_head = 1; ret = -ENOMEM; - rq = blk_get_request(q, writing ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + rq = blk_get_request(sdev->request_queue, writing ? + REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); if (IS_ERR(rq)) return PTR_ERR(rq); req = scsi_req(rq); @@ -452,7 +450,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, goto out_put_request; } - ret = scsi_fill_sghdr_rq(q, rq, hdr, mode); + ret = scsi_fill_sghdr_rq(sdev, rq, hdr, mode); if (ret < 0) goto out_free_cdb; @@ -469,11 +467,11 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, /* SG_IO howto says that the shorter of the two wins */ iov_iter_truncate(&i, hdr->dxfer_len); - ret = blk_rq_map_user_iov(q, rq, NULL, &i, GFP_KERNEL); + ret = blk_rq_map_user_iov(rq->q, rq, NULL, &i, GFP_KERNEL); kfree(iov); } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, - GFP_KERNEL); + ret = blk_rq_map_user(rq->q, rq, NULL, hdr->dxferp, + hdr->dxfer_len, GFP_KERNEL); if (ret) goto out_free_cdb; @@ -483,7 +481,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, start_time = jiffies; - blk_execute_rq(bd_disk, rq, at_head); + blk_execute_rq(disk, rq, at_head); hdr->duration = jiffies_to_msecs(jiffies - start_time); @@ -806,9 +804,8 @@ static int scsi_put_cdrom_generic_arg(const struct cdrom_generic_command *cgc, return 0; } -static int scsi_cdrom_send_packet(struct request_queue *q, - struct gendisk *bd_disk, - fmode_t mode, void __user *arg) +static int scsi_cdrom_send_packet(struct scsi_device *sdev, struct gendisk *disk, + fmode_t mode, void __user *arg) { struct cdrom_generic_command cgc; struct sg_io_hdr hdr; @@ -848,7 +845,7 @@ static int scsi_cdrom_send_packet(struct request_queue *q, hdr.cmdp = ((struct cdrom_generic_command __user *) arg)->cmd; hdr.cmd_len = sizeof(cgc.cmd); - err = sg_io(q, bd_disk, &hdr, mode); + err = sg_io(sdev, disk, &hdr, mode); if (err == -EFAULT) return -EFAULT; @@ -863,7 +860,7 @@ static int scsi_cdrom_send_packet(struct request_queue *q, return err; } -static int scsi_ioctl_sg_io(struct request_queue *q, struct gendisk *disk, +static int scsi_ioctl_sg_io(struct scsi_device *sdev, struct gendisk *disk, fmode_t mode, void __user *argp) { struct sg_io_hdr hdr; @@ -872,7 +869,7 @@ static int scsi_ioctl_sg_io(struct request_queue *q, struct gendisk *disk, error = get_sg_io_hdr(&hdr, argp); if (error) return error; - error = sg_io(q, disk, &hdr, mode); + error = sg_io(sdev, disk, &hdr, mode); if (error == -EFAULT) return error; if (put_sg_io_hdr(&hdr, argp)) @@ -918,21 +915,21 @@ int scsi_ioctl(struct scsi_device *sdev, struct gendisk *disk, fmode_t mode, case SG_GET_VERSION_NUM: return sg_get_version(arg); case SG_SET_TIMEOUT: - return sg_set_timeout(q, arg); + return sg_set_timeout(sdev, arg); case SG_GET_TIMEOUT: - return sg_get_timeout(q); + return jiffies_to_clock_t(sdev->sg_timeout); case SG_GET_RESERVED_SIZE: - return sg_get_reserved_size(q, arg); + return sg_get_reserved_size(sdev, arg); case SG_SET_RESERVED_SIZE: - return sg_set_reserved_size(q, arg); + return sg_set_reserved_size(sdev, arg); case SG_EMULATED_HOST: return sg_emulated_host(q, arg); case SG_IO: - return scsi_ioctl_sg_io(q, disk, mode, arg); + return scsi_ioctl_sg_io(sdev, disk, mode, arg); case SCSI_IOCTL_SEND_COMMAND: return sg_scsi_ioctl(q, disk, mode, arg); case CDROM_SEND_PACKET: - return scsi_cdrom_send_packet(q, disk, mode, arg); + return scsi_cdrom_send_packet(sdev, disk, mode, arg); case CDROMCLOSETRAY: return scsi_send_start_stop(sdev, 3); case CDROMEJECT: diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 3faedf4970ec..e06a2602fca4 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -267,6 +267,8 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, */ sdev->borken = 1; + sdev->sg_reserved_size = INT_MAX; + q = blk_mq_init_queue(&sdev->host->tag_set); if (IS_ERR(q)) { /* release fn is set up in scsi_sysfs_device_initialise, so diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e0bb14acb708..987f15089eeb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -499,11 +499,6 @@ struct request_queue { unsigned int max_active_zones; #endif /* CONFIG_BLK_DEV_ZONED */ - /* - * sg stuff - */ - unsigned int sg_timeout; - unsigned int sg_reserved_size; int node; struct mutex debugfs_mutex; #ifdef CONFIG_BLK_DEV_IO_TRACE diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 99082da1b951..7137e7924913 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -236,6 +236,9 @@ struct scsi_device { size_t dma_drain_len; void *dma_drain_buf; + unsigned int sg_timeout; + unsigned int sg_reserved_size; + struct bsg_device *bsg_dev; unsigned char access_state; struct mutex state_mutex; -- cgit v1.2.3-71-gd317 From 14cf1dbb55bb07427babee425fd2a8a9300737cc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 22 Jul 2021 09:54:01 +0200 Subject: block: remove bdgrab All callers are gone, and no one should grab a pure inode reference to a block device anymore. Signed-off-by: Christoph Hellwig Reviewed-by: Josef Bacik Link: https://lore.kernel.org/r/20210722075402.983367-9-hch@lst.de Signed-off-by: Jens Axboe --- fs/block_dev.c | 15 --------------- include/linux/blkdev.h | 1 - 2 files changed, 16 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/fs/block_dev.c b/fs/block_dev.c index 4a6c8c0a3bc9..4f2c4e9e84f5 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -921,21 +921,6 @@ void bdev_add(struct block_device *bdev, dev_t dev) insert_inode_hash(bdev->bd_inode); } -/** - * bdgrab -- Grab a reference to an already referenced block device - * @bdev: Block device to grab a reference to. - * - * Returns the block_device with an additional reference when successful, - * or NULL if the inode is already beeing freed. - */ -struct block_device *bdgrab(struct block_device *bdev) -{ - if (!igrab(bdev->bd_inode)) - return NULL; - return bdev; -} -EXPORT_SYMBOL(bdgrab); - long nr_blockdev_pages(void) { struct inode *inode; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d3afea47ade6..eb1289a58917 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1984,7 +1984,6 @@ void blkdev_put_no_open(struct block_device *bdev); struct block_device *bdev_alloc(struct gendisk *disk, u8 partno); void bdev_add(struct block_device *bdev, dev_t dev); struct block_device *I_BDEV(struct inode *inode); -struct block_device *bdgrab(struct block_device *bdev); void bdput(struct block_device *); int truncate_bdev_range(struct block_device *bdev, fmode_t mode, loff_t lstart, loff_t lend); -- cgit v1.2.3-71-gd317 From 2f4731dcd0bb73379fbb9e3eb07ae7324125caef Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 22 Jul 2021 09:54:02 +0200 Subject: block: remove bdput Now that we've stopped using inode references for anything meaninful in the block layer get rid of the helper to put it and just open code the call to iput on the block_device inode. Signed-off-by: Christoph Hellwig Reviewed-by: Josef Bacik Reviewed-by: Chaitanya Kulkarni Link: https://lore.kernel.org/r/20210722075402.983367-10-hch@lst.de Signed-off-by: Jens Axboe --- block/genhd.c | 4 ++-- block/partitions/core.c | 2 +- fs/block_dev.c | 6 ------ include/linux/blkdev.h | 1 - 4 files changed, 3 insertions(+), 10 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/genhd.c b/block/genhd.c index 716f5ca479ad..5dbb99b57b33 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1076,7 +1076,7 @@ static void disk_release(struct device *dev) xa_destroy(&disk->part_tbl); if (test_bit(GD_QUEUE_REF, &disk->state) && disk->queue) blk_put_queue(disk->queue); - bdput(disk->part0); /* frees the disk */ + iput(disk->part0->bd_inode); /* frees the disk */ } struct class block_class = { .name = "block", @@ -1261,7 +1261,7 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) out_destroy_part_tbl: xa_destroy(&disk->part_tbl); - bdput(disk->part0); + iput(disk->part0->bd_inode); out_free_disk: kfree(disk); return NULL; diff --git a/block/partitions/core.c b/block/partitions/core.c index 4f7a1a9cd544..2415bffc2771 100644 --- a/block/partitions/core.c +++ b/block/partitions/core.c @@ -262,7 +262,7 @@ static void part_release(struct device *dev) if (MAJOR(dev->devt) == BLOCK_EXT_MAJOR) blk_free_ext_minor(MINOR(dev->devt)); put_disk(dev_to_bdev(dev)->bd_disk); - bdput(dev_to_bdev(dev)); + iput(dev_to_bdev(dev)->bd_inode); } static int part_uevent(struct device *dev, struct kobj_uevent_env *env) diff --git a/fs/block_dev.c b/fs/block_dev.c index 4f2c4e9e84f5..6658f40ae492 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -934,12 +934,6 @@ long nr_blockdev_pages(void) return ret; } -void bdput(struct block_device *bdev) -{ - iput(bdev->bd_inode); -} -EXPORT_SYMBOL(bdput); - /** * bd_may_claim - test whether a block device can be claimed * @bdev: block device of interest diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index eb1289a58917..b5c033cf5f26 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1984,7 +1984,6 @@ void blkdev_put_no_open(struct block_device *bdev); struct block_device *bdev_alloc(struct gendisk *disk, u8 partno); void bdev_add(struct block_device *bdev, dev_t dev); struct block_device *I_BDEV(struct inode *inode); -void bdput(struct block_device *); int truncate_bdev_range(struct block_device *bdev, fmode_t mode, loff_t lstart, loff_t lend); -- cgit v1.2.3-71-gd317 From 471aa704db4904f7af5a50019ca3b5b018c0cf62 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 9 Aug 2021 16:17:41 +0200 Subject: block: pass a gendisk to blk_queue_update_readahead .. and rename the function to disk_update_readahead. This is in preparation for moving the BDI from the request_queue to the gendisk. Signed-off-by: Christoph Hellwig Reviewed-by: Jan Kara Reviewed-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20210809141744.1203023-3-hch@lst.de Signed-off-by: Jens Axboe --- block/blk-settings.c | 8 +++++--- block/blk-sysfs.c | 2 +- drivers/block/drbd/drbd_nl.c | 2 +- drivers/md/dm-table.c | 2 +- drivers/nvme/host/core.c | 2 +- include/linux/blkdev.h | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/blk-settings.c b/block/blk-settings.c index 109012719aa0..44aaef9bf736 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -380,8 +380,10 @@ void blk_queue_alignment_offset(struct request_queue *q, unsigned int offset) } EXPORT_SYMBOL(blk_queue_alignment_offset); -void blk_queue_update_readahead(struct request_queue *q) +void disk_update_readahead(struct gendisk *disk) { + struct request_queue *q = disk->queue; + /* * For read-ahead of large files to be effective, we need to read ahead * at least twice the optimal I/O size. @@ -391,7 +393,7 @@ void blk_queue_update_readahead(struct request_queue *q) q->backing_dev_info->io_pages = queue_max_sectors(q) >> (PAGE_SHIFT - 9); } -EXPORT_SYMBOL_GPL(blk_queue_update_readahead); +EXPORT_SYMBOL_GPL(disk_update_readahead); /** * blk_limits_io_min - set minimum request size for a device @@ -665,7 +667,7 @@ void disk_stack_limits(struct gendisk *disk, struct block_device *bdev, pr_notice("%s: Warning: Device %pg is misaligned\n", disk->disk_name, bdev); - blk_queue_update_readahead(disk->queue); + disk_update_readahead(disk); } EXPORT_SYMBOL(disk_stack_limits); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 370d83c18057..3af2ab7d5086 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -866,7 +866,7 @@ int blk_register_queue(struct gendisk *disk) "%s is registering an already registered queue\n", kobject_name(&dev->kobj)); - blk_queue_update_readahead(q); + disk_update_readahead(disk); ret = blk_trace_init_sysfs(dev); if (ret) diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index e7d0e637e632..44ccf8b4f4b2 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1364,7 +1364,7 @@ static void drbd_setup_queue_param(struct drbd_device *device, struct drbd_backi if (b) { blk_stack_limits(&q->limits, &b->limits, 0); - blk_queue_update_readahead(q); + disk_update_readahead(device->vdisk); } fixup_discard_if_not_supported(q); fixup_write_zeroes(device, q); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 0543cdf89e92..b03eabc1ed7c 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -2076,7 +2076,7 @@ int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, } dm_update_keyslot_manager(q, t); - blk_queue_update_readahead(q); + disk_update_readahead(t->md->disk); return 0; } diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index dfd9dec0c1f6..f6c0a59c4b53 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1890,7 +1890,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id) nvme_update_disk_info(ns->head->disk, ns, id); blk_stack_limits(&ns->head->disk->queue->limits, &ns->queue->limits, 0); - blk_queue_update_readahead(ns->head->disk->queue); + disk_update_readahead(ns->head->disk); blk_mq_unfreeze_queue(ns->head->disk->queue); } return 0; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b5c033cf5f26..ac3642c88a4d 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1139,7 +1139,7 @@ void blk_queue_zone_write_granularity(struct request_queue *q, unsigned int size); extern void blk_queue_alignment_offset(struct request_queue *q, unsigned int alignment); -void blk_queue_update_readahead(struct request_queue *q); +void disk_update_readahead(struct gendisk *disk); extern void blk_limits_io_min(struct queue_limits *limits, unsigned int min); extern void blk_queue_io_min(struct request_queue *q, unsigned int min); extern void blk_limits_io_opt(struct queue_limits *limits, unsigned int opt); -- cgit v1.2.3-71-gd317 From 1008162b2782a3624d12b0aee8da58bc75d12e19 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 9 Aug 2021 16:17:42 +0200 Subject: block: add a queue_has_disk helper Add a helper to check if a gendisk is associated with a request_queue. Signed-off-by: Christoph Hellwig Reviewed-by: Jan Kara Reviewed-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20210809141744.1203023-4-hch@lst.de Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/blkdev.h') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ac3642c88a4d..96f3d9617cd8 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -664,6 +664,7 @@ extern void blk_clear_pm_only(struct request_queue *q); dma_map_page_attrs(dev, (bv)->bv_page, (bv)->bv_offset, (bv)->bv_len, \ (dir), (attrs)) +#define queue_has_disk(q) ((q)->kobj.parent != NULL) #define queue_to_disk(q) (dev_to_disk(kobj_to_dev((q)->kobj.parent))) static inline bool queue_is_mq(struct request_queue *q) -- cgit v1.2.3-71-gd317 From edb0872f44ec9976ea6d052cb4b93cd2d23ac2ba Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 9 Aug 2021 16:17:43 +0200 Subject: block: move the bdi from the request_queue to the gendisk The backing device information only makes sense for file system I/O, and thus belongs into the gendisk and not the lower level request_queue structure. Move it there. Signed-off-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Link: https://lore.kernel.org/r/20210809141744.1203023-5-hch@lst.de Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 4 ++-- block/blk-cgroup.c | 7 +++---- block/blk-core.c | 13 +++---------- block/blk-mq.c | 2 +- block/blk-settings.c | 14 +++++++++----- block/blk-sysfs.c | 26 ++++++++++++-------------- block/blk-wbt.c | 10 +++++----- block/genhd.c | 23 ++++++++++++++--------- drivers/block/drbd/drbd_req.c | 5 ++--- drivers/block/pktcdvd.c | 8 +++----- fs/block_dev.c | 4 ++-- fs/fat/fatent.c | 1 + include/linux/blkdev.h | 3 --- include/linux/genhd.h | 1 + 14 files changed, 58 insertions(+), 63 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 727955918563..1576e858d3a5 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -5266,8 +5266,8 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) switch (ioprio_class) { default: pr_err("bdi %s: bfq: bad prio class %d\n", - bdi_dev_name(bfqq->bfqd->queue->backing_dev_info), - ioprio_class); + bdi_dev_name(queue_to_disk(bfqq->bfqd->queue)->bdi), + ioprio_class); fallthrough; case IOPRIO_CLASS_NONE: /* diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 575d7a2e7203..db034e35ae20 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -489,10 +489,9 @@ static int blkcg_reset_stats(struct cgroup_subsys_state *css, const char *blkg_dev_name(struct blkcg_gq *blkg) { - /* some drivers (floppy) instantiate a queue w/o disk registered */ - if (blkg->q->backing_dev_info->dev) - return bdi_dev_name(blkg->q->backing_dev_info); - return NULL; + if (!queue_has_disk(blkg->q) || !queue_to_disk(blkg->q)->bdi->dev) + return NULL; + return bdi_dev_name(queue_to_disk(blkg->q)->bdi); } /** diff --git a/block/blk-core.c b/block/blk-core.c index 5897bc37467d..0874bc2fcdb4 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -14,7 +14,6 @@ */ #include #include -#include #include #include #include @@ -531,13 +530,9 @@ struct request_queue *blk_alloc_queue(int node_id) if (ret) goto fail_id; - q->backing_dev_info = bdi_alloc(node_id); - if (!q->backing_dev_info) - goto fail_split; - q->stats = blk_alloc_queue_stats(); if (!q->stats) - goto fail_stats; + goto fail_split; q->node = node_id; @@ -567,7 +562,7 @@ struct request_queue *blk_alloc_queue(int node_id) if (percpu_ref_init(&q->q_usage_counter, blk_queue_usage_counter_release, PERCPU_REF_INIT_ATOMIC, GFP_KERNEL)) - goto fail_bdi; + goto fail_stats; if (blkcg_init_queue(q)) goto fail_ref; @@ -580,10 +575,8 @@ struct request_queue *blk_alloc_queue(int node_id) fail_ref: percpu_ref_exit(&q->q_usage_counter); -fail_bdi: - blk_free_queue_stats(q->stats); fail_stats: - bdi_put(q->backing_dev_info); + blk_free_queue_stats(q->stats); fail_split: bioset_exit(&q->bio_split); fail_id: diff --git a/block/blk-mq.c b/block/blk-mq.c index 2c4ac51e54eb..d2725f94491d 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -525,7 +525,7 @@ void blk_mq_free_request(struct request *rq) __blk_mq_dec_active_requests(hctx); if (unlikely(laptop_mode && !blk_rq_is_passthrough(rq))) - laptop_io_completion(q->backing_dev_info); + laptop_io_completion(queue_to_disk(q)->bdi); rq_qos_done(q, rq); diff --git a/block/blk-settings.c b/block/blk-settings.c index 44aaef9bf736..3613d2cc0688 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -140,7 +141,9 @@ void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_hw_secto limits->logical_block_size >> SECTOR_SHIFT); limits->max_sectors = max_sectors; - q->backing_dev_info->io_pages = max_sectors >> (PAGE_SHIFT - 9); + if (!queue_has_disk(q)) + return; + queue_to_disk(q)->bdi->io_pages = max_sectors >> (PAGE_SHIFT - 9); } EXPORT_SYMBOL(blk_queue_max_hw_sectors); @@ -388,10 +391,9 @@ void disk_update_readahead(struct gendisk *disk) * For read-ahead of large files to be effective, we need to read ahead * at least twice the optimal I/O size. */ - q->backing_dev_info->ra_pages = + disk->bdi->ra_pages = max(queue_io_opt(q) * 2 / PAGE_SIZE, VM_READAHEAD_PAGES); - q->backing_dev_info->io_pages = - queue_max_sectors(q) >> (PAGE_SHIFT - 9); + disk->bdi->io_pages = queue_max_sectors(q) >> (PAGE_SHIFT - 9); } EXPORT_SYMBOL_GPL(disk_update_readahead); @@ -473,7 +475,9 @@ EXPORT_SYMBOL(blk_limits_io_opt); void blk_queue_io_opt(struct request_queue *q, unsigned int opt) { blk_limits_io_opt(&q->limits, opt); - q->backing_dev_info->ra_pages = + if (!queue_has_disk(q)) + return; + queue_to_disk(q)->bdi->ra_pages = max(queue_io_opt(q) * 2 / PAGE_SIZE, VM_READAHEAD_PAGES); } EXPORT_SYMBOL(blk_queue_io_opt); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 3af2ab7d5086..1832587dce3a 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -88,9 +88,11 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) static ssize_t queue_ra_show(struct request_queue *q, char *page) { - unsigned long ra_kb = q->backing_dev_info->ra_pages << - (PAGE_SHIFT - 10); + unsigned long ra_kb; + if (!queue_has_disk(q)) + return -EINVAL; + ra_kb = queue_to_disk(q)->bdi->ra_pages << (PAGE_SHIFT - 10); return queue_var_show(ra_kb, page); } @@ -98,13 +100,14 @@ static ssize_t queue_ra_store(struct request_queue *q, const char *page, size_t count) { unsigned long ra_kb; - ssize_t ret = queue_var_store(&ra_kb, page, count); + ssize_t ret; + if (!queue_has_disk(q)) + return -EINVAL; + ret = queue_var_store(&ra_kb, page, count); if (ret < 0) return ret; - - q->backing_dev_info->ra_pages = ra_kb >> (PAGE_SHIFT - 10); - + queue_to_disk(q)->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10); return ret; } @@ -251,7 +254,9 @@ queue_max_sectors_store(struct request_queue *q, const char *page, size_t count) spin_lock_irq(&q->queue_lock); q->limits.max_sectors = max_sectors_kb << 1; - q->backing_dev_info->io_pages = max_sectors_kb >> (PAGE_SHIFT - 10); + if (queue_has_disk(q)) + queue_to_disk(q)->bdi->io_pages = + max_sectors_kb >> (PAGE_SHIFT - 10); spin_unlock_irq(&q->queue_lock); return ret; @@ -766,13 +771,6 @@ static void blk_exit_queue(struct request_queue *q) * e.g. blkcg_print_blkgs() to crash. */ blkcg_exit_queue(q); - - /* - * Since the cgroup code may dereference the @q->backing_dev_info - * pointer, only decrease its reference count after having removed the - * association with the block cgroup controller. - */ - bdi_put(q->backing_dev_info); } /** diff --git a/block/blk-wbt.c b/block/blk-wbt.c index 3ed71b8da887..31086afaad9c 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -97,7 +97,7 @@ static void wb_timestamp(struct rq_wb *rwb, unsigned long *var) */ static bool wb_recent_wait(struct rq_wb *rwb) { - struct bdi_writeback *wb = &rwb->rqos.q->backing_dev_info->wb; + struct bdi_writeback *wb = &queue_to_disk(rwb->rqos.q)->bdi->wb; return time_before(jiffies, wb->dirty_sleep + HZ); } @@ -234,7 +234,7 @@ enum { static int latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat) { - struct backing_dev_info *bdi = rwb->rqos.q->backing_dev_info; + struct backing_dev_info *bdi = queue_to_disk(rwb->rqos.q)->bdi; struct rq_depth *rqd = &rwb->rq_depth; u64 thislat; @@ -287,7 +287,7 @@ static int latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat) static void rwb_trace_step(struct rq_wb *rwb, const char *msg) { - struct backing_dev_info *bdi = rwb->rqos.q->backing_dev_info; + struct backing_dev_info *bdi = queue_to_disk(rwb->rqos.q)->bdi; struct rq_depth *rqd = &rwb->rq_depth; trace_wbt_step(bdi, msg, rqd->scale_step, rwb->cur_win_nsec, @@ -359,8 +359,8 @@ static void wb_timer_fn(struct blk_stat_callback *cb) status = latency_exceeded(rwb, cb->stat); - trace_wbt_timer(rwb->rqos.q->backing_dev_info, status, rqd->scale_step, - inflight); + trace_wbt_timer(queue_to_disk(rwb->rqos.q)->bdi, status, + rqd->scale_step, inflight); /* * If we exceeded the latency target, step down. If we did not, diff --git a/block/genhd.c b/block/genhd.c index b0b6e0caa389..f8def1129501 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -466,10 +466,9 @@ static void register_disk(struct device *parent, struct gendisk *disk, dev_set_uevent_suppress(ddev, 0); disk_uevent(disk, KOBJ_ADD); - if (disk->queue->backing_dev_info->dev) { - err = sysfs_create_link(&ddev->kobj, - &disk->queue->backing_dev_info->dev->kobj, - "bdi"); + if (disk->bdi->dev) { + err = sysfs_create_link(&ddev->kobj, &disk->bdi->dev->kobj, + "bdi"); WARN_ON(err); } } @@ -540,15 +539,14 @@ void device_add_disk(struct device *parent, struct gendisk *disk, disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO; disk->flags |= GENHD_FL_NO_PART_SCAN; } else { - struct backing_dev_info *bdi = disk->queue->backing_dev_info; struct device *dev = disk_to_dev(disk); /* Register BDI before referencing it from bdev */ dev->devt = MKDEV(disk->major, disk->first_minor); - ret = bdi_register(bdi, "%u:%u", + ret = bdi_register(disk->bdi, "%u:%u", disk->major, disk->first_minor); WARN_ON(ret); - bdi_set_owner(bdi, dev); + bdi_set_owner(disk->bdi, dev); bdev_add(disk->part0, dev->devt); } register_disk(parent, disk, groups); @@ -615,7 +613,7 @@ void del_gendisk(struct gendisk *disk) * Unregister bdi before releasing device numbers (as they can * get reused and we'd get clashes in sysfs). */ - bdi_unregister(disk->queue->backing_dev_info); + bdi_unregister(disk->bdi); } blk_unregister_queue(disk); @@ -1088,6 +1086,7 @@ static void disk_release(struct device *dev) might_sleep(); + bdi_put(disk->bdi); if (MAJOR(dev->devt) == BLOCK_EXT_MAJOR) blk_free_ext_minor(MINOR(dev->devt)); disk_release_events(disk); @@ -1268,9 +1267,13 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) if (!disk) return NULL; + disk->bdi = bdi_alloc(node_id); + if (!disk->bdi) + goto out_free_disk; + disk->part0 = bdev_alloc(disk, 0); if (!disk->part0) - goto out_free_disk; + goto out_free_bdi; disk->node_id = node_id; mutex_init(&disk->open_mutex); @@ -1292,6 +1295,8 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) out_destroy_part_tbl: xa_destroy(&disk->part_tbl); iput(disk->part0->bd_inode); +out_free_bdi: + bdi_put(disk->bdi); out_free_disk: kfree(disk); return NULL; diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 13beb98a7c5a..5ca233644d70 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -905,13 +905,12 @@ static bool drbd_may_do_local_read(struct drbd_device *device, sector_t sector, static bool remote_due_to_read_balancing(struct drbd_device *device, sector_t sector, enum drbd_read_balancing rbm) { - struct backing_dev_info *bdi; int stripe_shift; switch (rbm) { case RB_CONGESTED_REMOTE: - bdi = device->ldev->backing_bdev->bd_disk->queue->backing_dev_info; - return bdi_read_congested(bdi); + return bdi_read_congested( + device->ldev->backing_bdev->bd_disk->bdi); case RB_LEAST_PENDING: return atomic_read(&device->local_cnt) > atomic_read(&device->ap_pending_cnt) + atomic_read(&device->rs_pending_cnt); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 538446b652de..0f26b2510a75 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -1183,10 +1183,8 @@ try_next_bio: wakeup = (pd->write_congestion_on > 0 && pd->bio_queue_size <= pd->write_congestion_off); spin_unlock(&pd->lock); - if (wakeup) { - clear_bdi_congested(pd->disk->queue->backing_dev_info, - BLK_RW_ASYNC); - } + if (wakeup) + clear_bdi_congested(pd->disk->bdi, BLK_RW_ASYNC); pkt->sleep_time = max(PACKET_WAIT_TIME, 1); pkt_set_state(pkt, PACKET_WAITING_STATE); @@ -2366,7 +2364,7 @@ static void pkt_make_request_write(struct request_queue *q, struct bio *bio) spin_lock(&pd->lock); if (pd->write_congestion_on > 0 && pd->bio_queue_size >= pd->write_congestion_on) { - set_bdi_congested(q->backing_dev_info, BLK_RW_ASYNC); + set_bdi_congested(bio->bi_bdev->bd_disk->bdi, BLK_RW_ASYNC); do { spin_unlock(&pd->lock); congestion_wait(BLK_RW_ASYNC, HZ); diff --git a/fs/block_dev.c b/fs/block_dev.c index cc801767a377..43be5463a4c4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1087,7 +1087,7 @@ static int blkdev_get_whole(struct block_device *bdev, fmode_t mode) if (!bdev->bd_openers) { set_init_blocksize(bdev); if (bdev->bd_bdi == &noop_backing_dev_info) - bdev->bd_bdi = bdi_get(disk->queue->backing_dev_info); + bdev->bd_bdi = bdi_get(disk->bdi); } if (test_bit(GD_NEED_PART_SCAN, &disk->state)) bdev_disk_changed(disk, false); @@ -1122,7 +1122,7 @@ static int blkdev_get_part(struct block_device *part, fmode_t mode) disk->open_partitions++; set_init_blocksize(part); if (part->bd_bdi == &noop_backing_dev_info) - part->bd_bdi = bdi_get(disk->queue->backing_dev_info); + part->bd_bdi = bdi_get(disk->bdi); done: part->bd_openers++; return 0; diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 860e884e56e8..978ac6751aeb 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -5,6 +5,7 @@ #include #include +#include #include "fat.h" struct fatent_operations { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 96f3d9617cd8..23e1253a8d88 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -398,8 +397,6 @@ struct request_queue { struct blk_mq_hw_ctx **queue_hw_ctx; unsigned int nr_hw_queues; - struct backing_dev_info *backing_dev_info; - /* * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 473d93c6ebda..b3bab578f03a 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -158,6 +158,7 @@ struct gendisk { struct mutex open_mutex; /* open/close mutex */ unsigned open_partitions; /* number of open partitions */ + struct backing_dev_info *bdi; struct kobject *slave_dir; #ifdef CONFIG_BLOCK_HOLDER_DEPRECATED struct list_head slave_bdevs; -- cgit v1.2.3-71-gd317 From 866663b7b52d2da267b28e12eed89ee781b8fed1 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 29 Jul 2021 11:42:26 +0800 Subject: block: return ELEVATOR_DISCARD_MERGE if possible When merging one bio to request, if they are discard IO and the queue supports multi-range discard, we need to return ELEVATOR_DISCARD_MERGE because both block core and related drivers(nvme, virtio-blk) doesn't handle mixed discard io merge(traditional IO merge together with discard merge) well. Fix the issue by returning ELEVATOR_DISCARD_MERGE in this situation, so both blk-mq and drivers just need to handle multi-range discard. Reported-by: Oleksandr Natalenko Signed-off-by: Ming Lei Tested-by: Oleksandr Natalenko Fixes: 2705dfb20947 ("block: fix discard request merge") Link: https://lore.kernel.org/r/20210729034226.1591070-1-ming.lei@redhat.com Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 3 +++ block/blk-merge.c | 16 ---------------- block/elevator.c | 3 +++ block/mq-deadline-main.c | 2 ++ include/linux/blkdev.h | 16 ++++++++++++++++ 5 files changed, 24 insertions(+), 16 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 1576e858d3a5..e4a61eda2d0f 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -2361,6 +2361,9 @@ static int bfq_request_merge(struct request_queue *q, struct request **req, __rq = bfq_find_rq_fmerge(bfqd, bio, q); if (__rq && elv_bio_merge_ok(__rq, bio)) { *req = __rq; + + if (blk_discard_mergable(__rq)) + return ELEVATOR_DISCARD_MERGE; return ELEVATOR_FRONT_MERGE; } diff --git a/block/blk-merge.c b/block/blk-merge.c index a11b3b53717e..f8707ff7e2fc 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -705,22 +705,6 @@ static void blk_account_io_merge_request(struct request *req) } } -/* - * Two cases of handling DISCARD merge: - * If max_discard_segments > 1, the driver takes every bio - * as a range and send them to controller together. The ranges - * needn't to be contiguous. - * Otherwise, the bios/requests will be handled as same as - * others which should be contiguous. - */ -static inline bool blk_discard_mergable(struct request *req) -{ - if (req_op(req) == REQ_OP_DISCARD && - queue_max_discard_segments(req->q) > 1) - return true; - return false; -} - static enum elv_merge blk_try_req_merge(struct request *req, struct request *next) { diff --git a/block/elevator.c b/block/elevator.c index 9beaafd238e0..ff45d8388f48 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -336,6 +336,9 @@ enum elv_merge elv_merge(struct request_queue *q, struct request **req, __rq = elv_rqhash_find(q, bio->bi_iter.bi_sector); if (__rq && elv_bio_merge_ok(__rq, bio)) { *req = __rq; + + if (blk_discard_mergable(__rq)) + return ELEVATOR_DISCARD_MERGE; return ELEVATOR_BACK_MERGE; } diff --git a/block/mq-deadline-main.c b/block/mq-deadline-main.c index 6f612e6dc82b..294be0c0db65 100644 --- a/block/mq-deadline-main.c +++ b/block/mq-deadline-main.c @@ -677,6 +677,8 @@ static int dd_request_merge(struct request_queue *q, struct request **rq, if (elv_bio_merge_ok(__rq, bio)) { *rq = __rq; + if (blk_discard_mergable(__rq)) + return ELEVATOR_DISCARD_MERGE; return ELEVATOR_FRONT_MERGE; } } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 23e1253a8d88..07eef02325b4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1519,6 +1519,22 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector return offset << SECTOR_SHIFT; } +/* + * Two cases of handling DISCARD merge: + * If max_discard_segments > 1, the driver takes every bio + * as a range and send them to controller together. The ranges + * needn't to be contiguous. + * Otherwise, the bios/requests will be handled as same as + * others which should be contiguous. + */ +static inline bool blk_discard_mergable(struct request *req) +{ + if (req_op(req) == REQ_OP_DISCARD && + queue_max_discard_segments(req->q) > 1) + return true; + return false; +} + static inline int bdev_discard_alignment(struct block_device *bdev) { struct request_queue *q = bdev_get_queue(bdev); -- cgit v1.2.3-71-gd317 From 018eca456c4b4dca56aaf1ec27f309c74d0fe246 Mon Sep 17 00:00:00 2001 From: Guoqing Jiang Date: Wed, 21 Jul 2021 10:53:15 +0800 Subject: block: move some macros to blkdev.h Move them (PAGE_SECTORS_SHIFT, PAGE_SECTORS and SECTOR_MASK) to the generic header file to remove redundancy. Signed-off-by: Guoqing Jiang Link: https://lore.kernel.org/r/20210721025315.1729118-1-guoqing.jiang@linux.dev Signed-off-by: Jens Axboe --- drivers/block/brd.c | 3 --- drivers/block/null_blk/main.c | 4 ---- drivers/md/bcache/util.h | 2 -- include/linux/blkdev.h | 4 ++++ include/linux/device-mapper.h | 1 - 5 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 95694113e38e..58ec167aa018 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -27,9 +27,6 @@ #include -#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) -#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) - /* * Each block ramdisk device has a radix_tree brd_pages of pages that stores * the pages containing the block device's contents. A brd page's ->index is diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index d734e9ee1546..f128242d1170 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -11,10 +11,6 @@ #include #include "null_blk.h" -#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) -#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) -#define SECTOR_MASK (PAGE_SECTORS - 1) - #define FREE_BATCH 16 #define TICKS_PER_SEC 50ULL diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h index bca4a7c97da7..b64460a76267 100644 --- a/drivers/md/bcache/util.h +++ b/drivers/md/bcache/util.h @@ -15,8 +15,6 @@ #include "closure.h" -#define PAGE_SECTORS (PAGE_SIZE / 512) - struct closure; #ifdef CONFIG_BCACHE_DEBUG diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 07eef02325b4..df404c1fb087 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -939,6 +939,10 @@ static inline struct request_queue *bdev_get_queue(struct block_device *bdev) #define SECTOR_SIZE (1 << SECTOR_SHIFT) #endif +#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) +#define SECTOR_MASK (PAGE_SECTORS - 1) + /* * blk_rq_pos() : the current sector * blk_rq_bytes() : bytes left in the entire request diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 7457d49acf9a..94f2cd6a8e83 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -151,7 +151,6 @@ typedef size_t (*dm_dax_copy_iter_fn)(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i); typedef int (*dm_dax_zero_page_range_fn)(struct dm_target *ti, pgoff_t pgoff, size_t nr_pages); -#define PAGE_SECTORS (PAGE_SIZE / 512) void dm_error(const char *message); -- cgit v1.2.3-71-gd317 From d152c682f03ceb65c0d9663d4ba6ee2d46aa784d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 16 Aug 2021 15:46:24 +0200 Subject: block: add an explicit ->disk backpointer to the request_queue Replace the magic lookup through the kobject tree with an explicit backpointer, given that the device model links are set up and torn down at times when I/O is still possible, leading to potential NULL or invalid pointer dereferences. Fixes: edb0872f44ec ("block: move the bdi from the request_queue to the gendisk") Reported-by: syzbot Signed-off-by: Christoph Hellwig Tested-by: Sven Schnelle Link: https://lore.kernel.org/r/20210816134624.GA24234@lst.de Signed-off-by: Jens Axboe --- block/bfq-iosched.c | 2 +- block/blk-cgroup.c | 4 ++-- block/blk-mq.c | 2 +- block/blk-settings.c | 8 ++++---- block/blk-sysfs.c | 13 ++++++------- block/blk-wbt.c | 10 +++++----- block/genhd.c | 2 ++ include/linux/blkdev.h | 5 ++--- include/trace/events/kyber.h | 6 +++--- 9 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include/linux/blkdev.h') diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index e92bc0348433..480e1a134859 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -5269,7 +5269,7 @@ bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) switch (ioprio_class) { default: pr_err("bdi %s: bfq: bad prio class %d\n", - bdi_dev_name(queue_to_disk(bfqq->bfqd->queue)->bdi), + bdi_dev_name(bfqq->bfqd->queue->disk->bdi), ioprio_class); fallthrough; case IOPRIO_CLASS_NONE: diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index b8ec47dcce42..f575aa42922b 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -489,9 +489,9 @@ static int blkcg_reset_stats(struct cgroup_subsys_state *css, const char *blkg_dev_name(struct blkcg_gq *blkg) { - if (!queue_has_disk(blkg->q) || !queue_to_disk(blkg->q)->bdi->dev) + if (!blkg->q->disk || !blkg->q->disk->bdi->dev) return NULL; - return bdi_dev_name(queue_to_disk(blkg->q)->bdi); + return bdi_dev_name(blkg->q->disk->bdi); } /** diff --git a/block/blk-mq.c b/block/blk-mq.c index 2ca7e7c94b18..0a33d16a7298 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -525,7 +525,7 @@ void blk_mq_free_request(struct request *rq) __blk_mq_dec_active_requests(hctx); if (unlikely(laptop_mode && !blk_rq_is_passthrough(rq))) - laptop_io_completion(queue_to_disk(q)->bdi); + laptop_io_completion(q->disk->bdi); rq_qos_done(q, rq); diff --git a/block/blk-settings.c b/block/blk-settings.c index 3613d2cc0688..a7c857ad7d10 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -141,9 +141,9 @@ void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_hw_secto limits->logical_block_size >> SECTOR_SHIFT); limits->max_sectors = max_sectors; - if (!queue_has_disk(q)) + if (!q->disk) return; - queue_to_disk(q)->bdi->io_pages = max_sectors >> (PAGE_SHIFT - 9); + q->disk->bdi->io_pages = max_sectors >> (PAGE_SHIFT - 9); } EXPORT_SYMBOL(blk_queue_max_hw_sectors); @@ -475,9 +475,9 @@ EXPORT_SYMBOL(blk_limits_io_opt); void blk_queue_io_opt(struct request_queue *q, unsigned int opt) { blk_limits_io_opt(&q->limits, opt); - if (!queue_has_disk(q)) + if (!q->disk) return; - queue_to_disk(q)->bdi->ra_pages = + q->disk->bdi->ra_pages = max(queue_io_opt(q) * 2 / PAGE_SIZE, VM_READAHEAD_PAGES); } EXPORT_SYMBOL(blk_queue_io_opt); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 586507a5b8c2..7fd99487300c 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -90,9 +90,9 @@ static ssize_t queue_ra_show(struct request_queue *q, char *page) { unsigned long ra_kb; - if (!queue_has_disk(q)) + if (!q->disk) return -EINVAL; - ra_kb = queue_to_disk(q)->bdi->ra_pages << (PAGE_SHIFT - 10); + ra_kb = q->disk->bdi->ra_pages << (PAGE_SHIFT - 10); return queue_var_show(ra_kb, page); } @@ -102,12 +102,12 @@ queue_ra_store(struct request_queue *q, const char *page, size_t count) unsigned long ra_kb; ssize_t ret; - if (!queue_has_disk(q)) + if (!q->disk) return -EINVAL; ret = queue_var_store(&ra_kb, page, count); if (ret < 0) return ret; - queue_to_disk(q)->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10); + q->disk->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10); return ret; } @@ -254,9 +254,8 @@ queue_max_sectors_store(struct request_queue *q, const char *page, size_t count) spin_lock_irq(&q->queue_lock); q->limits.max_sectors = max_sectors_kb << 1; - if (queue_has_disk(q)) - queue_to_disk(q)->bdi->io_pages = - max_sectors_kb >> (PAGE_SHIFT - 10); + if (q->disk) + q->disk->bdi->io_pages = max_sectors_kb >> (PAGE_SHIFT - 10); spin_unlock_irq(&q->queue_lock); return ret; diff --git a/block/blk-wbt.c b/block/blk-wbt.c index 31086afaad9c..874c1c37bf0c 100644 --- a/block/blk-wbt.c +++ b/block/blk-wbt.c @@ -97,7 +97,7 @@ static void wb_timestamp(struct rq_wb *rwb, unsigned long *var) */ static bool wb_recent_wait(struct rq_wb *rwb) { - struct bdi_writeback *wb = &queue_to_disk(rwb->rqos.q)->bdi->wb; + struct bdi_writeback *wb = &rwb->rqos.q->disk->bdi->wb; return time_before(jiffies, wb->dirty_sleep + HZ); } @@ -234,7 +234,7 @@ enum { static int latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat) { - struct backing_dev_info *bdi = queue_to_disk(rwb->rqos.q)->bdi; + struct backing_dev_info *bdi = rwb->rqos.q->disk->bdi; struct rq_depth *rqd = &rwb->rq_depth; u64 thislat; @@ -287,7 +287,7 @@ static int latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat) static void rwb_trace_step(struct rq_wb *rwb, const char *msg) { - struct backing_dev_info *bdi = queue_to_disk(rwb->rqos.q)->bdi; + struct backing_dev_info *bdi = rwb->rqos.q->disk->bdi; struct rq_depth *rqd = &rwb->rq_depth; trace_wbt_step(bdi, msg, rqd->scale_step, rwb->cur_win_nsec, @@ -359,8 +359,8 @@ static void wb_timer_fn(struct blk_stat_callback *cb) status = latency_exceeded(rwb, cb->stat); - trace_wbt_timer(queue_to_disk(rwb->rqos.q)->bdi, status, - rqd->scale_step, inflight); + trace_wbt_timer(rwb->rqos.q->disk->bdi, status, rqd->scale_step, + inflight); /* * If we exceeded the latency target, step down. If we did not, diff --git a/block/genhd.c b/block/genhd.c index 6294517cebe6..02cd9ec93e52 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1078,6 +1078,7 @@ static void disk_release(struct device *dev) disk_release_events(disk); kfree(disk->random); xa_destroy(&disk->part_tbl); + disk->queue->disk = NULL; blk_put_queue(disk->queue); iput(disk->part0->bd_inode); /* frees the disk */ } @@ -1276,6 +1277,7 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id, device_initialize(disk_to_dev(disk)); inc_diskseq(disk); disk->queue = q; + q->disk = disk; lockdep_init_map(&disk->lockdep_map, "(bio completion)", lkclass, 0); #ifdef CONFIG_BLOCK_HOLDER_DEPRECATED INIT_LIST_HEAD(&disk->slave_bdevs); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index df404c1fb087..22b5b8502d2a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -421,6 +421,8 @@ struct request_queue { spinlock_t queue_lock; + struct gendisk *disk; + /* * queue kobject */ @@ -661,9 +663,6 @@ extern void blk_clear_pm_only(struct request_queue *q); dma_map_page_attrs(dev, (bv)->bv_page, (bv)->bv_offset, (bv)->bv_len, \ (dir), (attrs)) -#define queue_has_disk(q) ((q)->kobj.parent != NULL) -#define queue_to_disk(q) (dev_to_disk(kobj_to_dev((q)->kobj.parent))) - static inline bool queue_is_mq(struct request_queue *q) { return q->mq_ops; diff --git a/include/trace/events/kyber.h b/include/trace/events/kyber.h index f9802562edf6..491098a0d8ed 100644 --- a/include/trace/events/kyber.h +++ b/include/trace/events/kyber.h @@ -30,7 +30,7 @@ TRACE_EVENT(kyber_latency, ), TP_fast_assign( - __entry->dev = disk_devt(queue_to_disk(q)); + __entry->dev = disk_devt(q->disk); strlcpy(__entry->domain, domain, sizeof(__entry->domain)); strlcpy(__entry->type, type, sizeof(__entry->type)); __entry->percentile = percentile; @@ -59,7 +59,7 @@ TRACE_EVENT(kyber_adjust, ), TP_fast_assign( - __entry->dev = disk_devt(queue_to_disk(q)); + __entry->dev = disk_devt(q->disk); strlcpy(__entry->domain, domain, sizeof(__entry->domain)); __entry->depth = depth; ), @@ -81,7 +81,7 @@ TRACE_EVENT(kyber_throttled, ), TP_fast_assign( - __entry->dev = disk_devt(queue_to_disk(q)); + __entry->dev = disk_devt(q->disk); strlcpy(__entry->domain, domain, sizeof(__entry->domain)); ), -- cgit v1.2.3-71-gd317 From 0bdfbca8a623e262e0f343b143151000a300cbaf Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 20 Aug 2021 03:45:33 +0300 Subject: block: Add alternative_gpt_sector() operation Add alternative_gpt_sector() block device operation which specifies alternative location of a GPT entry. This allows us to support Android devices that have GPT entry at a non-standard location and can't be repartitioned easily. Reviewed-by: Christoph Hellwig Suggested-by: Christoph Hellwig Signed-off-by: Dmitry Osipenko Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20210820004536.15791-2-digetx@gmail.com Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux/blkdev.h') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 22b5b8502d2a..c9cb12483e12 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1872,6 +1872,13 @@ struct block_device_operations { char *(*devnode)(struct gendisk *disk, umode_t *mode); struct module *owner; const struct pr_ops *pr_ops; + + /* + * Special callback for probing GPT entry at a given sector. + * Needed by Android devices, used by GPT scanner and MMC blk + * driver. + */ + int (*alternative_gpt_sector)(struct gendisk *disk, sector_t *sector); }; #ifdef CONFIG_COMPAT -- cgit v1.2.3-71-gd317