diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-02 15:09:46 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-02 15:09:46 -0700 |
commit | a9c9a6f741cdaa2fa9ba24a790db8d07295761e3 (patch) | |
tree | 222aaa35ed4e66c2027845213251e2a3f491b5ba /block | |
parent | 23852bec534a1633dc08f4df88b8493ae99953a9 (diff) | |
parent | 9b5ac8ab4e8bf5636d1d425aee68ddf45af12057 (diff) | |
download | lwn-a9c9a6f741cdaa2fa9ba24a790db8d07295761e3.tar.gz lwn-a9c9a6f741cdaa2fa9ba24a790db8d07295761e3.zip |
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley:
"This series consists of the usual driver updates (ufs, qla2xxx,
target, smartpqi, lpfc, mpt3sas).
The core change causing the most churn was replacing the command
request field request with a macro, allowing us to offset map to it
and remove the redundant field; the same was also done for the tag
field.
The most impactful change is the final removal of scsi_ioctl, which
has been deprecated for over a decade"
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (293 commits)
scsi: ufs: Fix ufshcd_request_sense_async() for Samsung KLUFG8RHDA-B2D1
scsi: ufs: ufs-exynos: Fix static checker warning
scsi: mpt3sas: Use the proper SCSI midlayer interfaces for PI
scsi: lpfc: Use the proper SCSI midlayer interfaces for PI
scsi: lpfc: Copyright updates for 14.0.0.1 patches
scsi: lpfc: Update lpfc version to 14.0.0.1
scsi: lpfc: Add bsg support for retrieving adapter cmf data
scsi: lpfc: Add cmf_info sysfs entry
scsi: lpfc: Add debugfs support for cm framework buffers
scsi: lpfc: Add support for maintaining the cm statistics buffer
scsi: lpfc: Add rx monitoring statistics
scsi: lpfc: Add support for the CM framework
scsi: lpfc: Add cmfsync WQE support
scsi: lpfc: Add support for cm enablement buffer
scsi: lpfc: Add cm statistics buffer support
scsi: lpfc: Add EDC ELS support
scsi: lpfc: Expand FPIN and RDF receive logging
scsi: lpfc: Add MIB feature enablement support
scsi: lpfc: Add SET_HOST_DATA mbox cmd to pass date/time info to firmware
scsi: fc: Add EDC ELS definition
...
Diffstat (limited to 'block')
-rw-r--r-- | block/Kconfig | 26 | ||||
-rw-r--r-- | block/Makefile | 3 | ||||
-rw-r--r-- | block/blk-mq.c | 2 | ||||
-rw-r--r-- | block/bsg-lib.c | 90 | ||||
-rw-r--r-- | block/bsg.c | 463 | ||||
-rw-r--r-- | block/scsi_ioctl.c | 890 |
6 files changed, 149 insertions, 1325 deletions
diff --git a/block/Kconfig b/block/Kconfig index bac87d773c54..8e28ae7718bd 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -29,35 +29,15 @@ if BLOCK config BLK_RQ_ALLOC_TIME bool -config BLK_SCSI_REQUEST - bool - 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 1d0d466f2182..6cf40276d3cf 100644 --- a/block/Makefile +++ b/block/Makefile @@ -12,8 +12,7 @@ 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) += 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/blk-mq.c b/block/blk-mq.c index 944049982e6e..65d3a63aecc6 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -3280,8 +3280,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-lib.c b/block/bsg-lib.c index a89d80102304..ccb98276c964 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 <linux/bsg.h> #include <linux/slab.h> #include <linux/blk-mq.h> #include <linux/delay.h> @@ -19,36 +20,44 @@ struct bsg_set { struct blk_mq_tag_set tag_set; + struct bsg_device *bd; bsg_job_fn *job_fn; bsg_timeout_fn *timeout_fn; }; -static int bsg_transport_check_proto(struct sg_io_v4 *hdr) +static int bsg_transport_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr, + fmode_t mode, unsigned int timeout) { + struct bsg_job *job; + struct request *rq; + struct bio *bio; + int ret; + if (hdr->protocol != BSG_PROTOCOL_SCSI || hdr->subprotocol != BSG_SUB_PROTOCOL_SCSI_TRANSPORT) return -EINVAL; if (!capable(CAP_SYS_RAWIO)) return -EPERM; - return 0; -} -static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, - fmode_t mode) -{ - struct bsg_job *job = blk_mq_rq_to_pdu(rq); - int ret; + rq = blk_get_request(q, hdr->dout_xfer_len ? + REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + if (IS_ERR(rq)) + return PTR_ERR(rq); + rq->timeout = timeout; + job = blk_mq_rq_to_pdu(rq); job->request_len = hdr->request_len; job->request = memdup_user(uptr64(hdr->request), hdr->request_len); - if (IS_ERR(job->request)) - return PTR_ERR(job->request); + if (IS_ERR(job->request)) { + ret = PTR_ERR(job->request); + goto out_put_request; + } if (hdr->dout_xfer_len && hdr->din_xfer_len) { job->bidi_rq = blk_get_request(rq->q, REQ_OP_DRV_IN, 0); if (IS_ERR(job->bidi_rq)) { ret = PTR_ERR(job->bidi_rq); - goto out; + goto out_free_job_request; } ret = blk_rq_map_user(rq->q, job->bidi_rq, NULL, @@ -63,20 +72,20 @@ static int bsg_transport_fill_hdr(struct request *rq, struct sg_io_v4 *hdr, job->bidi_bio = NULL; } - return 0; + ret = 0; + if (hdr->dout_xfer_len) { + 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(rq->q, rq, NULL, uptr64(hdr->din_xferp), + hdr->din_xfer_len, GFP_KERNEL); + } -out_free_bidi_rq: - if (job->bidi_rq) - blk_put_request(job->bidi_rq); -out: - kfree(job->request); - return ret; -} + if (ret) + goto out_unmap_bidi_rq; -static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr) -{ - struct bsg_job *job = blk_mq_rq_to_pdu(rq); - int ret = 0; + bio = rq->bio; + blk_execute_rq(NULL, rq, !(hdr->flags & BSG_FLAG_Q_AT_TAIL)); /* * The assignments below don't make much sense, but are kept for @@ -119,28 +128,20 @@ static int bsg_transport_complete_rq(struct request *rq, struct sg_io_v4 *hdr) hdr->din_resid = 0; } - return ret; -} - -static void bsg_transport_free_rq(struct request *rq) -{ - struct bsg_job *job = blk_mq_rq_to_pdu(rq); - - if (job->bidi_rq) { + blk_rq_unmap_user(bio); +out_unmap_bidi_rq: + if (job->bidi_rq) blk_rq_unmap_user(job->bidi_bio); +out_free_bidi_rq: + if (job->bidi_rq) blk_put_request(job->bidi_rq); - } - +out_free_job_request: kfree(job->request); +out_put_request: + blk_put_request(rq); + return ret; } -static const struct bsg_ops bsg_transport_ops = { - .check_proto = bsg_transport_check_proto, - .fill_hdr = bsg_transport_fill_hdr, - .complete_rq = bsg_transport_complete_rq, - .free_rq = bsg_transport_free_rq, -}; - /** * bsg_teardown_job - routine to teardown a bsg job * @kref: kref inside bsg_job that is to be torn down @@ -327,7 +328,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 +397,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_sg_io_fn); + 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 1f196563ae6c..351095193788 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -15,343 +15,97 @@ #include <scsi/scsi.h> #include <scsi/scsi_ioctl.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_driver.h> #include <scsi/sg.h> #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]; + struct device device; + struct cdev cdev; int max_queue; + unsigned int timeout; + unsigned int reserved_size; + bsg_sg_io_fn *sg_io_fn; }; +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_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) +static unsigned int bsg_timeout(struct bsg_device *bd, 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; - } + unsigned int timeout = BLK_DEFAULT_SG_TIMEOUT; - if (rq_data_dir(rq) == READ) - hdr->din_resid = sreq->resid_len; - else - hdr->dout_resid = sreq->resid_len; + if (hdr->timeout) + timeout = msecs_to_jiffies(hdr->timeout); + else if (bd->timeout) + timeout = bd->timeout; - return ret; + return max_t(unsigned int, timeout, BLK_MIN_SG_TIMEOUT); } -static void bsg_scsi_free_rq(struct request *rq) +static int bsg_sg_io(struct bsg_device *bd, fmode_t mode, void __user *uarg) { - 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; - struct bio *bio; struct sg_io_v4 hdr; int ret; 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); - if (ret) - return ret; - - rq = blk_get_request(q, 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); - if (ret) { - blk_put_request(rq); - return ret; - } - - 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; - - if (hdr.dout_xfer_len) { - ret = blk_rq_map_user(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), - hdr.din_xfer_len, GFP_KERNEL); - } - - if (ret) - goto out_free_rq; - - 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); - blk_rq_unmap_user(bio); - -out_free_rq: - rq->q->bsg_dev.ops->free_rq(rq); - blk_put_request(rq); + ret = bd->sg_io_fn(bd->queue, &hdr, mode, bsg_timeout(bd, &hdr)); 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; + int val; switch (cmd) { /* @@ -366,17 +120,37 @@ static long bsg_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * SCSI/sg ioctls */ case SG_GET_VERSION_NUM: + return put_user(30527, intp); case SCSI_IOCTL_GET_IDLUN: + return put_user(0, intp); case SCSI_IOCTL_GET_BUS_NUMBER: + return put_user(0, intp); case SG_SET_TIMEOUT: + if (get_user(val, intp)) + return -EFAULT; + bd->timeout = clock_t_to_jiffies(val); + return 0; case SG_GET_TIMEOUT: + return jiffies_to_clock_t(bd->timeout); case SG_GET_RESERVED_SIZE: + 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; + bd->reserved_size = + min_t(unsigned int, val, queue_max_bytes(q)); + return 0; case SG_EMULATED_HOST: - case SCSI_IOCTL_SEND_COMMAND: - return scsi_cmd_ioctl(bd->queue, NULL, file->f_mode, cmd, uarg); + return put_user(1, intp); case SG_IO: - return bsg_sg_io(bd->queue, 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); + return -EINVAL; default: return -ENOTTY; } @@ -391,92 +165,65 @@ 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, bsg_sg_io_fn *sg_io_fn) { - 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->reserved_size = INT_MAX; + bd->queue = q; + bd->sg_io_fn = sg_io_fn; - 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; - -unregister_class_dev: - device_unregister(class_dev); -idr_remove: - idr_remove(&bsg_minor_idr, bcd->minor); -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 bd; - return bsg_register_queue(q, parent, dev_name(parent), &bsg_scsi_ops); +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_scsi_register_queue); - -static struct cdev bsg_cdev; +EXPORT_SYMBOL_GPL(bsg_register_queue); static char *bsg_devnode(struct device *dev, umode_t *mode) { @@ -485,11 +232,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)) @@ -499,19 +243,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/block/scsi_ioctl.c b/block/scsi_ioctl.c deleted file mode 100644 index d247431a6853..000000000000 --- a/block/scsi_ioctl.c +++ /dev/null @@ -1,890 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2001 Jens Axboe <axboe@suse.de> - */ -#include <linux/compat.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/module.h> -#include <linux/blkdev.h> -#include <linux/capability.h> -#include <linux/completion.h> -#include <linux/cdrom.h> -#include <linux/ratelimit.h> -#include <linux/slab.h> -#include <linux/times.h> -#include <linux/uio.h> -#include <linux/uaccess.h> - -#include <scsi/scsi.h> -#include <scsi/scsi_ioctl.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/sg.h> - -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; - -/* Command group 3 is reserved and should never be used. */ -const unsigned char scsi_command_size_tbl[8] = -{ - 6, 10, 10, 12, - 16, 12, 10, 10 -}; -EXPORT_SYMBOL(scsi_command_size_tbl); - -static int sg_get_version(int __user *p) -{ - static const int sg_version_num = 30527; - return put_user(sg_version_num, p); -} - -static int scsi_get_idlun(struct request_queue *q, int __user *p) -{ - return put_user(0, p); -} - -static int scsi_get_bus(struct request_queue *q, int __user *p) -{ - return put_user(0, 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 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)); - - 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(size, max_sectors_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); -} - -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) -{ - struct blk_cmd_filter *filter = &blk_default_cmd_filter; - - /* root can do any command. */ - if (capable(CAP_SYS_RAWIO)) - return 0; - - /* 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; -} -EXPORT_SYMBOL(blk_verify_command); - -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 (blk_verify_command(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 = blk_verify_command(req->cmd, mode); - if (err) - 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 SCSI_IOCTL_GET_IDLUN: - err = scsi_get_idlun(q, arg); - break; - case SCSI_IOCTL_GET_BUS_NUMBER: - err = scsi_get_bus(q, 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); - -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); - -int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode, - unsigned int cmd, void __user *arg) -{ - int ret; - - ret = scsi_verify_blk_ioctl(bd, cmd); - if (ret < 0) - return ret; - - return scsi_cmd_ioctl(bd->bd_disk->queue, bd->bd_disk, mode, cmd, arg); -} -EXPORT_SYMBOL(scsi_cmd_blk_ioctl); - -/** - * scsi_req_init - initialize certain fields of a scsi_request structure - * @req: Pointer to a scsi_request structure. - * Initializes .__cmd[], .cmd, .cmd_len and .sense_len but no other members - * of struct scsi_request. - */ -void scsi_req_init(struct scsi_request *req) -{ - memset(req->__cmd, 0, sizeof(req->__cmd)); - req->cmd = req->__cmd; - req->cmd_len = BLK_MAX_CDB; - req->sense_len = 0; -} -EXPORT_SYMBOL(scsi_req_init); - -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); |