From patchwork Mon Oct 24 03:20:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016532 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 515D5C433FE for ; Mon, 24 Oct 2022 03:23:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230132AbiJXDXC (ORCPT ); Sun, 23 Oct 2022 23:23:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230127AbiJXDWe (ORCPT ); Sun, 23 Oct 2022 23:22:34 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id DA2FF1A22F for ; Sun, 23 Oct 2022 20:21:04 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 9B0FC2041BD; Mon, 24 Oct 2022 05:21:03 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id LCMX7Te23Q-1; Mon, 24 Oct 2022 05:21:03 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 2554C2041AF; Mon, 24 Oct 2022 05:21:02 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 01/44] sg: move functions around Date: Sun, 23 Oct 2022 23:20:15 -0400 Message-Id: <20221024032058.14077-2-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Move main entry point functions around so submission code comes before completion code. Prior to this, the driver used the traditional open(), close(), read(), write(), ioctl() ordering however in this case that places completion code (i.e. sg_read()) before submission code (i.e. sg_write()). The main driver entry points are considered to be those named in struct file_operations sg_fops' definition. Helper functions are placed above their caller to reduce the number of forward function declarations needed. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 422 +++++++++++++++++++++++----------------------- 1 file changed, 210 insertions(+), 212 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ce34a8ad53b4..fc125d0aff97 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -8,11 +8,12 @@ * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2014 Douglas Gilbert + * Copyright (C) 1998 - 2019 Douglas Gilbert */ -static int sg_version_num = 30536; /* 2 digits for each component */ -#define SG_VERSION_STR "3.5.36" +static int sg_version_num = 30901; /* [x]xyyzz where [x] empty when x=0 */ +#define SG_VERSION_STR "3.9.01" /* [x]x.[y]y.zz */ +static char *sg_version_date = "20190606"; /* * D. P. Gilbert (dgilbert@interlog.com), notes: @@ -48,6 +49,7 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include /* for sg_check_file_access() */ +#include #include #include @@ -62,12 +64,6 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include "scsi_logging.h" -#ifdef CONFIG_SCSI_PROC_FS -#include -static char *sg_version_date = "20140603"; - -static int sg_proc_init(void); -#endif #define SG_ALLOW_DIO_DEF 0 @@ -178,11 +174,11 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ /* tasklet or soft irq callback */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); +/* Declarations of other static functions used before they are defined */ +static int sg_proc_init(void); static int sg_start_req(Sg_request *srp, unsigned char *cmd); static int sg_finish_rem_req(Sg_request * srp); static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); -static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, - Sg_request * srp); static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, size_t count, int blocking, int read_only, int sg_io_owned, Sg_request **o_srp); @@ -231,17 +227,6 @@ static int sg_check_file_access(struct file *filp, const char *caller) return 0; } -static int sg_allow_access(struct file *filp, unsigned char *cmd) -{ - struct sg_fd *sfp = filp->private_data; - - if (sfp->parentdp->device->type == TYPE_SCANNER) - return 0; - if (!scsi_cmd_allowed(cmd, filp->f_mode)) - return -EPERM; - return 0; -} - static int open_wait(Sg_device *sdp, int flags) { @@ -405,196 +390,6 @@ sg_release(struct inode *inode, struct file *filp) return 0; } -static int get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) -{ - struct sg_header __user *old_hdr = buf; - int reply_len; - - if (count >= SZ_SG_HEADER) { - /* negative reply_len means v3 format, otherwise v1/v2 */ - if (get_user(reply_len, &old_hdr->reply_len)) - return -EFAULT; - - if (reply_len >= 0) - return get_user(*pack_id, &old_hdr->pack_id); - - if (in_compat_syscall() && - count >= sizeof(struct compat_sg_io_hdr)) { - struct compat_sg_io_hdr __user *hp = buf; - - return get_user(*pack_id, &hp->pack_id); - } - - if (count >= sizeof(struct sg_io_hdr)) { - struct sg_io_hdr __user *hp = buf; - - return get_user(*pack_id, &hp->pack_id); - } - } - - /* no valid header was passed, so ignore the pack_id */ - *pack_id = -1; - return 0; -} - -static ssize_t -sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) -{ - Sg_device *sdp; - Sg_fd *sfp; - Sg_request *srp; - int req_pack_id = -1; - bool busy; - sg_io_hdr_t *hp; - struct sg_header *old_hdr; - int retval; - - /* - * This could cause a response to be stranded. Close the associated - * file descriptor to free up any resources being held. - */ - retval = sg_check_file_access(filp, __func__); - if (retval) - return retval; - - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_read: count=%d\n", (int) count)); - - if (sfp->force_packid) - retval = get_sg_io_pack_id(&req_pack_id, buf, count); - if (retval) - return retval; - - srp = sg_get_rq_mark(sfp, req_pack_id, &busy); - if (!srp) { /* now wait on packet to arrive */ - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - retval = wait_event_interruptible(sfp->read_wait, - ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || - (!busy && atomic_read(&sdp->detaching)))); - if (!srp) - /* signal or detaching */ - return retval ? retval : -ENODEV; - } - if (srp->header.interface_id != '\0') - return sg_new_read(sfp, buf, count, srp); - - hp = &srp->header; - old_hdr = kzalloc(SZ_SG_HEADER, GFP_KERNEL); - if (!old_hdr) - return -ENOMEM; - - old_hdr->reply_len = (int) hp->timeout; - old_hdr->pack_len = old_hdr->reply_len; /* old, strange behaviour */ - old_hdr->pack_id = hp->pack_id; - old_hdr->twelve_byte = - ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; - old_hdr->target_status = hp->masked_status; - old_hdr->host_status = hp->host_status; - old_hdr->driver_status = hp->driver_status; - if ((CHECK_CONDITION & hp->masked_status) || - (srp->sense_b[0] & 0x70) == 0x70) { - old_hdr->driver_status = DRIVER_SENSE; - memcpy(old_hdr->sense_buffer, srp->sense_b, - sizeof (old_hdr->sense_buffer)); - } - switch (hp->host_status) { - /* This setup of 'result' is for backward compatibility and is best - ignored by the user who should use target, host + driver status */ - case DID_OK: - case DID_PASSTHROUGH: - case DID_SOFT_ERROR: - old_hdr->result = 0; - break; - case DID_NO_CONNECT: - case DID_BUS_BUSY: - case DID_TIME_OUT: - old_hdr->result = EBUSY; - break; - case DID_BAD_TARGET: - case DID_ABORT: - case DID_PARITY: - case DID_RESET: - case DID_BAD_INTR: - old_hdr->result = EIO; - break; - case DID_ERROR: - old_hdr->result = (srp->sense_b[0] == 0 && - hp->masked_status == GOOD) ? 0 : EIO; - break; - default: - old_hdr->result = EIO; - break; - } - - /* Now copy the result back to the user buffer. */ - if (count >= SZ_SG_HEADER) { - if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) { - retval = -EFAULT; - goto free_old_hdr; - } - buf += SZ_SG_HEADER; - if (count > old_hdr->reply_len) - count = old_hdr->reply_len; - if (count > SZ_SG_HEADER) { - if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) { - retval = -EFAULT; - goto free_old_hdr; - } - } - } else - count = (old_hdr->result == 0) ? 0 : -EIO; - sg_finish_rem_req(srp); - sg_remove_request(sfp, srp); - retval = count; -free_old_hdr: - kfree(old_hdr); - return retval; -} - -static ssize_t -sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) -{ - sg_io_hdr_t *hp = &srp->header; - int err = 0, err2; - int len; - - if (in_compat_syscall()) { - if (count < sizeof(struct compat_sg_io_hdr)) { - err = -EINVAL; - goto err_out; - } - } else if (count < SZ_SG_IO_HDR) { - err = -EINVAL; - goto err_out; - } - hp->sb_len_wr = 0; - if ((hp->mx_sb_len > 0) && hp->sbp) { - if ((CHECK_CONDITION & hp->masked_status) || - (srp->sense_b[0] & 0x70) == 0x70) { - int sb_len = SCSI_SENSE_BUFFERSIZE; - sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; - len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */ - len = (len > sb_len) ? sb_len : len; - if (copy_to_user(hp->sbp, srp->sense_b, len)) { - err = -EFAULT; - goto err_out; - } - hp->driver_status = DRIVER_SENSE; - hp->sb_len_wr = len; - } - } - if (hp->masked_status || hp->host_status || hp->driver_status) - hp->info |= SG_INFO_CHECK; - err = put_sg_io_hdr(hp, buf); -err_out: - err2 = sg_finish_rem_req(srp); - sg_remove_request(sfp, srp); - return err ? : err2 ? : count; -} - static ssize_t sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) { @@ -708,6 +503,18 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return (k < 0) ? k : count; } +static int +sg_allow_access(struct file *filp, unsigned char *cmd) +{ + struct sg_fd *sfp = filp->private_data; + + if (sfp->parentdp->device->type == TYPE_SCANNER) + return 0; + if (!scsi_cmd_allowed(cmd, filp->f_mode)) + return -EPERM; + return 0; +} + static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, size_t count, int blocking, int read_only, int sg_io_owned, @@ -833,6 +640,80 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, return 0; } +static int +get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) +{ + struct sg_header __user *old_hdr = buf; + int reply_len; + + if (count >= SZ_SG_HEADER) { + /* negative reply_len means v3 format, otherwise v1/v2 */ + if (get_user(reply_len, &old_hdr->reply_len)) + return -EFAULT; + + if (reply_len >= 0) + return get_user(*pack_id, &old_hdr->pack_id); + + if (in_compat_syscall() && + count >= sizeof(struct compat_sg_io_hdr)) { + struct compat_sg_io_hdr __user *hp = buf; + + return get_user(*pack_id, &hp->pack_id); + } + + if (count >= sizeof(struct sg_io_hdr)) { + struct sg_io_hdr __user *hp = buf; + + return get_user(*pack_id, &hp->pack_id); + } + } + + /* no valid header was passed, so ignore the pack_id */ + *pack_id = -1; + return 0; +} + +static ssize_t +sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) +{ + sg_io_hdr_t *hp = &srp->header; + int err = 0, err2; + int len; + + if (in_compat_syscall()) { + if (count < sizeof(struct compat_sg_io_hdr)) { + err = -EINVAL; + goto err_out; + } + } else if (count < SZ_SG_IO_HDR) { + err = -EINVAL; + goto err_out; + } + hp->sb_len_wr = 0; + if ((hp->mx_sb_len > 0) && hp->sbp) { + if ((CHECK_CONDITION & hp->masked_status) || + (srp->sense_b[0] & 0x70) == 0x70) { + int sb_len = SCSI_SENSE_BUFFERSIZE; + sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; + len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */ + len = (len > sb_len) ? sb_len : len; + if (copy_to_user(hp->sbp, srp->sense_b, len)) { + err = -EFAULT; + goto err_out; + } + hp->driver_status = DRIVER_SENSE; + hp->sb_len_wr = len; + } + } + if (hp->masked_status || hp->host_status || hp->driver_status) + hp->info |= SG_INFO_CHECK; + err = put_sg_io_hdr(hp, buf); +err_out: + err2 = sg_finish_rem_req(srp); + sg_remove_request(sfp, srp); + return err ? : err2 ? : count; +} + static int srp_done(Sg_fd *sfp, Sg_request *srp) { unsigned long flags; @@ -844,6 +725,123 @@ static int srp_done(Sg_fd *sfp, Sg_request *srp) return ret; } +static ssize_t +sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) +{ + Sg_device *sdp; + Sg_fd *sfp; + Sg_request *srp; + int req_pack_id = -1; + bool busy; + sg_io_hdr_t *hp; + struct sg_header *old_hdr; + int retval; + + /* + * This could cause a response to be stranded. Close the associated + * file descriptor to free up any resources being held. + */ + retval = sg_check_file_access(filp, __func__); + if (retval) + return retval; + + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_read: count=%d\n", (int) count)); + + if (sfp->force_packid) + retval = get_sg_io_pack_id(&req_pack_id, buf, count); + if (retval) + return retval; + + srp = sg_get_rq_mark(sfp, req_pack_id, &busy); + if (!srp) { /* now wait on packet to arrive */ + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + retval = wait_event_interruptible(sfp->read_wait, + ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || + (!busy && atomic_read(&sdp->detaching)))); + if (!srp) + /* signal or detaching */ + return retval ? retval : -ENODEV; + } + if (srp->header.interface_id != '\0') + return sg_new_read(sfp, buf, count, srp); + + hp = &srp->header; + old_hdr = kzalloc(SZ_SG_HEADER, GFP_KERNEL); + if (!old_hdr) + return -ENOMEM; + + old_hdr->reply_len = (int) hp->timeout; + old_hdr->pack_len = old_hdr->reply_len; /* old, strange behaviour */ + old_hdr->pack_id = hp->pack_id; + old_hdr->twelve_byte = + ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; + old_hdr->target_status = hp->masked_status; + old_hdr->host_status = hp->host_status; + old_hdr->driver_status = hp->driver_status; + if ((CHECK_CONDITION & hp->masked_status) || + (srp->sense_b[0] & 0x70) == 0x70) { + old_hdr->driver_status = DRIVER_SENSE; + memcpy(old_hdr->sense_buffer, srp->sense_b, + sizeof (old_hdr->sense_buffer)); + } + switch (hp->host_status) { + /* This setup of 'result' is for backward compatibility and is best + ignored by the user who should use target, host + driver status */ + case DID_OK: + case DID_PASSTHROUGH: + case DID_SOFT_ERROR: + old_hdr->result = 0; + break; + case DID_NO_CONNECT: + case DID_BUS_BUSY: + case DID_TIME_OUT: + old_hdr->result = EBUSY; + break; + case DID_BAD_TARGET: + case DID_ABORT: + case DID_PARITY: + case DID_RESET: + case DID_BAD_INTR: + old_hdr->result = EIO; + break; + case DID_ERROR: + old_hdr->result = (srp->sense_b[0] == 0 && + hp->masked_status == GOOD) ? 0 : EIO; + break; + default: + old_hdr->result = EIO; + break; + } + + /* Now copy the result back to the user buffer. */ + if (count >= SZ_SG_HEADER) { + if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) { + retval = -EFAULT; + goto free_old_hdr; + } + buf += SZ_SG_HEADER; + if (count > old_hdr->reply_len) + count = old_hdr->reply_len; + if (count > SZ_SG_HEADER) { + if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) { + retval = -EFAULT; + goto free_old_hdr; + } + } + } else + count = (old_hdr->result == 0) ? 0 : -EIO; + sg_finish_rem_req(srp); + sg_remove_request(sfp, srp); + retval = count; +free_old_hdr: + kfree(old_hdr); + return retval; +} + static int max_sectors_bytes(struct request_queue *q) { unsigned int max_sectors = queue_max_sectors(q); From patchwork Mon Oct 24 03:20:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016536 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82A72FA3740 for ; Mon, 24 Oct 2022 03:23:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230138AbiJXDXI (ORCPT ); Sun, 23 Oct 2022 23:23:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58694 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230327AbiJXDWf (ORCPT ); Sun, 23 Oct 2022 23:22:35 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E3E3352467 for ; Sun, 23 Oct 2022 20:21:07 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 95E072041C0; Mon, 24 Oct 2022 05:21:05 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 5juxq+JdD2Gw; Mon, 24 Oct 2022 05:21:05 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id C50102041AF; Mon, 24 Oct 2022 05:21:03 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Johannes Thumshirn , Christoph Hellwig , Hannes Reinecke Subject: [PATCH v25 02/44] sg: remove typedefs, type+formatting cleanup Date: Sun, 23 Oct 2022 23:20:16 -0400 Message-Id: <20221024032058.14077-3-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Typedefs for structure types are discouraged so those structures that are private to the driver have had their typedefs removed. This also means that most "camel" type variable names (i.e. mixed case) have been removed. Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 393 +++++++++++++++++++++++++--------------------- 1 file changed, 218 insertions(+), 175 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index fc125d0aff97..9078037d5b20 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -3,7 +3,7 @@ * History: * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), * to allow user process control of SCSI devices. - * Development Sponsored by Killy Corp. NY NY + * Development Sponsored by Killy Corp. NY NY [guess: 1992] * * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard @@ -15,13 +15,6 @@ static int sg_version_num = 30901; /* [x]xyyzz where [x] empty when x=0 */ #define SG_VERSION_STR "3.9.01" /* [x]x.[y]y.zz */ static char *sg_version_date = "20190606"; -/* - * D. P. Gilbert (dgilbert@interlog.com), notes: - * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First - * the kernel/module needs to be built with CONFIG_SCSI_LOGGING - * (otherwise the macros compile to empty statements). - * - */ #include #include @@ -96,33 +89,32 @@ static int sg_add_device(struct device *, struct class_interface *); static void sg_remove_device(struct device *, struct class_interface *); static DEFINE_IDR(sg_index_idr); -static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock - file descriptor list for device */ +static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock fd list for device */ static struct class_interface sg_interface = { .add_dev = sg_add_device, .remove_dev = sg_remove_device, }; -typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ - unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ - unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */ - unsigned bufflen; /* Size of (aggregate) data buffer */ +struct sg_scatter_hold { /* holding area for scsi scatter gather info */ + u16 k_use_sg; /* Count of kernel scatter-gather pieces */ + unsigned int sglist_len; /* size of malloc'd scatter-gather list ++ */ + unsigned int bufflen; /* Size of (aggregate) data buffer */ struct page **pages; int page_order; char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ - unsigned char cmd_opcode; /* first byte of command */ -} Sg_scatter_hold; + u8 cmd_opcode; /* first byte of command */ +}; struct sg_device; /* forward declarations */ struct sg_fd; -typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ +struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct list_head entry; /* list entry */ struct sg_fd *parentfp; /* NULL -> not in use */ - Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ + struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ sg_io_hdr_t header; /* scsi command+info, see */ - unsigned char sense_b[SCSI_SENSE_BUFFERSIZE]; + u8 sense_b[SCSI_SENSE_BUFFERSIZE]; char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ @@ -131,9 +123,9 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct request *rq; struct bio *bio; struct execute_work ew; -} Sg_request; +}; -typedef struct sg_fd { /* holds the state of a file descriptor */ +struct sg_fd { /* holds the state of a file descriptor */ struct list_head sfd_siblings; /* protected by device's sfd_lock */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ @@ -141,21 +133,21 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ - Sg_scatter_hold reserve; /* buffer held for this file descriptor */ + struct sg_scatter_hold reserve; /* buffer for this file descriptor */ struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */ - Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ + struct sg_request req_arr[SG_MAX_QUEUE];/* use as singly-linked list */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ - unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ + u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ char mmap_called; /* 0 -> mmap() never called on this fd */ char res_in_use; /* 1 -> 'reserve' array in use */ struct kref f_ref; struct execute_work ew; -} Sg_fd; +}; -typedef struct sg_device { /* holds the state of each scsi generic device */ +struct sg_device { /* holds the state of each scsi generic device */ struct scsi_device *device; wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ struct mutex open_rel_lock; /* held when in open() or release() */ @@ -168,33 +160,38 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ int open_cnt; /* count of opens (perhaps < num(sfds) ) */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ char name[DISK_NAME_LEN]; - struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg] */ + struct cdev *cdev; struct kref d_ref; -} Sg_device; +}; /* tasklet or soft irq callback */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); -static int sg_start_req(Sg_request *srp, unsigned char *cmd); -static int sg_finish_rem_req(Sg_request * srp); -static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); -static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, - const char __user *buf, size_t count, int blocking, - int read_only, int sg_io_owned, Sg_request **o_srp); -static int sg_common_write(Sg_fd * sfp, Sg_request * srp, - unsigned char *cmnd, int timeout, int blocking); -static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); -static void sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp); -static void sg_build_reserve(Sg_fd * sfp, int req_size); -static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size); -static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp); -static Sg_fd *sg_add_sfp(Sg_device * sdp); +static int sg_start_req(struct sg_request *srp, u8 *cmd); +static int sg_finish_rem_req(struct sg_request *srp); +static int sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, + int buff_size); +static ssize_t sg_new_write(struct sg_fd *sfp, struct file *file, + const char __user *buf, size_t count, int blocking, + int read_only, int sg_io_owned, + struct sg_request **o_srp); +static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, + u8 *cmnd, int timeout, int blocking); +static int sg_read_oxfer(struct sg_request *srp, char __user *outp, + int num_read_xfer); +static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); +static void sg_build_reserve(struct sg_fd *sfp, int req_size); +static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, + int size); +static void sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp); +static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); -static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy); -static Sg_request *sg_add_request(Sg_fd * sfp); -static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); -static Sg_device *sg_get_dev(int dev); +static struct sg_request *sg_get_rq_mark(struct sg_fd *sfp, int pack_id, + bool *busy); +static struct sg_request *sg_add_request(struct sg_fd *sfp); +static int sg_remove_request(struct sg_fd *sfp, struct sg_request *srp); +static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); #define SZ_SG_HEADER sizeof(struct sg_header) @@ -217,7 +214,8 @@ static void sg_device_destroy(struct kref *kref); * This function provides protection for the legacy API by restricting the * calling context. */ -static int sg_check_file_access(struct file *filp, const char *caller) +static int +sg_check_file_access(struct file *filp, const char *caller) { if (filp->f_cred != current_real_cred()) { pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n", @@ -228,11 +226,11 @@ static int sg_check_file_access(struct file *filp, const char *caller) } static int -open_wait(Sg_device *sdp, int flags) +sg_wait_open_event(struct sg_device *sdp, bool o_excl) { int retval = 0; - if (flags & O_EXCL) { + if (o_excl) { while (sdp->open_cnt > 0) { mutex_unlock(&sdp->open_rel_lock); retval = wait_event_interruptible(sdp->open_wait, @@ -263,26 +261,34 @@ open_wait(Sg_device *sdp, int flags) return retval; } -/* Returns 0 on success, else a negated errno value */ +/* + * Corresponds to the open() system call on sg devices. Implements O_EXCL on + * a per device basis using 'open_cnt'. If O_EXCL and O_NONBLOCK and there is + * already a sg handle open on this device then it fails with an errno of + * EBUSY. Without the O_NONBLOCK flag then this thread enters an interruptible + * wait until the other handle(s) are closed. + */ static int sg_open(struct inode *inode, struct file *filp) { - int dev = iminor(inode); - int flags = filp->f_flags; + bool o_excl; + int min_dev = iminor(inode); + int op_flags = filp->f_flags; struct request_queue *q; - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; int retval; nonseekable_open(inode, filp); - if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) + o_excl = !!(op_flags & O_EXCL); + if (o_excl && ((op_flags & O_ACCMODE) == O_RDONLY)) return -EPERM; /* Can't lock it with read only access */ - sdp = sg_get_dev(dev); + sdp = sg_get_dev(min_dev); if (IS_ERR(sdp)) return PTR_ERR(sdp); SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_open: flags=0x%x\n", flags)); + "sg_open: flags=0x%x\n", op_flags)); /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ @@ -297,7 +303,7 @@ sg_open(struct inode *inode, struct file *filp) /* scsi_block_when_processing_errors() may block so bypass * check if O_NONBLOCK. Permits SCSI commands to be issued * during error recovery. Tread carefully. */ - if (!((flags & O_NONBLOCK) || + if (!((op_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) { retval = -ENXIO; /* we are in error recovery for this device */ @@ -305,8 +311,8 @@ sg_open(struct inode *inode, struct file *filp) } mutex_lock(&sdp->open_rel_lock); - if (flags & O_NONBLOCK) { - if (flags & O_EXCL) { + if (op_flags & O_NONBLOCK) { + if (o_excl) { if (sdp->open_cnt > 0) { retval = -EBUSY; goto error_mutex_locked; @@ -318,13 +324,13 @@ sg_open(struct inode *inode, struct file *filp) } } } else { - retval = open_wait(sdp, flags); + retval = sg_wait_open_event(sdp, o_excl); if (retval) /* -ERESTARTSYS or -ENODEV */ goto error_mutex_locked; } /* N.B. at this point we are holding the open_rel_lock */ - if (flags & O_EXCL) + if (o_excl) sdp->exclude = true; if (sdp->open_cnt < 1) { /* no existing opens */ @@ -348,7 +354,7 @@ sg_open(struct inode *inode, struct file *filp) return retval; out_undo: - if (flags & O_EXCL) { + if (o_excl) { sdp->exclude = false; /* undo if error */ wake_up_interruptible(&sdp->open_wait); } @@ -366,10 +372,12 @@ sg_open(struct inode *inode, struct file *filp) static int sg_release(struct inode *inode, struct file *filp) { - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); @@ -378,7 +386,7 @@ sg_release(struct inode *inode, struct file *filp) kref_put(&sfp->f_ref, sg_remove_sfp); sdp->open_cnt--; - /* possibly many open()s waiting on exlude clearing, start many; + /* possibly many open()s waiting on exclude clearing, start many; * only open(O_EXCL)s wait on 0==open_cnt so only start one */ if (sdp->exclude) { sdp->exclude = false; @@ -395,20 +403,22 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) { int mxsize, cmd_size, k; int input_size, blocking; - unsigned char opcode; - Sg_device *sdp; - Sg_fd *sfp; - Sg_request *srp; + u8 opcode; + struct sg_device *sdp; + struct sg_fd *sfp; + struct sg_request *srp; struct sg_header old_hdr; sg_io_hdr_t *hp; - unsigned char cmnd[SG_MAX_CDB_SIZE]; + u8 cmnd[SG_MAX_CDB_SIZE]; int retval; retval = sg_check_file_access(filp, __func__); if (retval) return retval; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_write: count=%d\n", (int) count)); @@ -461,7 +471,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) } hp = &srp->header; hp->interface_id = '\0'; /* indicator of old interface tunnelled */ - hp->cmd_len = (unsigned char) cmd_size; + hp->cmd_len = (u8)cmd_size; hp->iovec_count = 0; hp->mx_sb_len = 0; if (input_size > 0) @@ -516,14 +526,14 @@ sg_allow_access(struct file *filp, unsigned char *cmd) } static ssize_t -sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, - size_t count, int blocking, int read_only, int sg_io_owned, - Sg_request **o_srp) +sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, + size_t count, int blocking, int read_only, int sg_io_owned, + struct sg_request **o_srp) { int k; - Sg_request *srp; + struct sg_request *srp; sg_io_hdr_t *hp; - unsigned char cmnd[SG_MAX_CDB_SIZE]; + u8 cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -583,11 +593,11 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, } static int -sg_common_write(Sg_fd * sfp, Sg_request * srp, - unsigned char *cmnd, int timeout, int blocking) +sg_common_write(struct sg_fd *sfp, struct sg_request *srp, + u8 *cmnd, int timeout, int blocking) { int k, at_head; - Sg_device *sdp = sfp->parentdp; + struct sg_device *sdp = sfp->parentdp; sg_io_hdr_t *hp = &srp->header; srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ @@ -674,7 +684,8 @@ get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) } static ssize_t -sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) +sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, + struct sg_request *srp) { sg_io_hdr_t *hp = &srp->header; int err = 0, err2; @@ -714,7 +725,8 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) return err ? : err2 ? : count; } -static int srp_done(Sg_fd *sfp, Sg_request *srp) +static int +srp_done(struct sg_fd *sfp, struct sg_request *srp) { unsigned long flags; int ret; @@ -728,9 +740,9 @@ static int srp_done(Sg_fd *sfp, Sg_request *srp) static ssize_t sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) { - Sg_device *sdp; - Sg_fd *sfp; - Sg_request *srp; + struct sg_device *sdp; + struct sg_fd *sfp; + struct sg_request *srp; int req_pack_id = -1; bool busy; sg_io_hdr_t *hp; @@ -745,7 +757,8 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) if (retval) return retval; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + if ((!(sfp = (struct sg_fd *)filp->private_data)) || + (!(sdp = sfp->parentdp))) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_read: count=%d\n", (int) count)); @@ -842,7 +855,8 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) return retval; } -static int max_sectors_bytes(struct request_queue *q) +static int +max_sectors_bytes(struct request_queue *q) { unsigned int max_sectors = queue_max_sectors(q); @@ -852,9 +866,9 @@ static int max_sectors_bytes(struct request_queue *q) } static void -sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) +sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) { - Sg_request *srp; + struct sg_request *srp; int val; unsigned int ms; @@ -912,12 +926,12 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o, #endif static long -sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp, +sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, unsigned int cmd_in, void __user *p) { int __user *ip = p; int result, val, read_only; - Sg_request *srp; + struct sg_request *srp; unsigned long iflags; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, @@ -1147,11 +1161,13 @@ static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { void __user *p = (void __user *)arg; - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; int ret; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p); @@ -1164,9 +1180,9 @@ static __poll_t sg_poll(struct file *filp, poll_table * wait) { __poll_t res = 0; - Sg_device *sdp; - Sg_fd *sfp; - Sg_request *srp; + struct sg_device *sdp; + struct sg_fd *sfp; + struct sg_request *srp; int count = 0; unsigned long iflags; @@ -1201,10 +1217,12 @@ sg_poll(struct file *filp, poll_table * wait) static int sg_fasync(int fd, struct file *filp, int mode) { - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_fasync: mode=%d\n", mode)); @@ -1216,13 +1234,21 @@ static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; - Sg_fd *sfp; + struct sg_fd *sfp; unsigned long offset, len, sa; - Sg_scatter_hold *rsv_schp; + struct sg_scatter_hold *rsv_schp; int k, length; + const char *nbp = "==NULL, bad"; - if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data))) - return VM_FAULT_SIGBUS; + if (!vma) { + pr_warn("%s: vma%s\n", __func__, nbp); + goto out_err; + } + sfp = vma->vm_private_data; + if (!sfp) { + pr_warn("%s: sfp%s\n", __func__, nbp); + goto out_err; + } rsv_schp = &sfp->reserve; offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rsv_schp->bufflen) @@ -1245,7 +1271,7 @@ sg_vma_fault(struct vm_fault *vmf) sa += len; offset -= len; } - +out_err: return VM_FAULT_SIGBUS; } @@ -1256,14 +1282,19 @@ static const struct vm_operations_struct sg_mmap_vm_ops = { static int sg_mmap(struct file *filp, struct vm_area_struct *vma) { - Sg_fd *sfp; + struct sg_fd *sfp; unsigned long req_sz, len, sa; - Sg_scatter_hold *rsv_schp; + struct sg_scatter_hold *rsv_schp; int k, length; int ret = 0; - if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) + if (!filp || !vma) + return -ENXIO; + sfp = filp->private_data; + if (!sfp) { + pr_warn("sg: %s: sfp is NULL\n", __func__); return -ENXIO; + } req_sz = vma->vm_end - vma->vm_start; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, "sg_mmap starting, vm_start=%p, len=%d\n", @@ -1314,8 +1345,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) { struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); struct sg_request *srp = rq->end_io_data; - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; unsigned long iflags; unsigned int ms; char *sense; @@ -1425,21 +1456,18 @@ static struct class *sg_sysfs_class; static int sg_sysfs_valid = 0; -static Sg_device * +static struct sg_device * sg_alloc(struct scsi_device *scsidp) { struct request_queue *q = scsidp->request_queue; - Sg_device *sdp; + struct sg_device *sdp; unsigned long iflags; int error; u32 k; - sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL); - if (!sdp) { - sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device " - "failure\n", __func__); + sdp = kzalloc(sizeof(*sdp), GFP_KERNEL); + if (!sdp) return ERR_PTR(-ENOMEM); - } idr_preload(GFP_KERNEL); write_lock_irqsave(&sg_index_lock, iflags); @@ -1452,8 +1480,8 @@ sg_alloc(struct scsi_device *scsidp) scsidp->type, SG_MAX_DEVS - 1); error = -ENODEV; } else { - sdev_printk(KERN_WARNING, scsidp, "%s: idr " - "allocation Sg_device failure: %d\n", + sdev_printk(KERN_WARNING, scsidp, + "%s: idr alloc sg_device failure: %d\n", __func__, error); } goto out_unlock; @@ -1489,7 +1517,7 @@ static int sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); - Sg_device *sdp = NULL; + struct sg_device *sdp = NULL; struct cdev * cdev = NULL; int error; unsigned long iflags; @@ -1579,9 +1607,9 @@ static void sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); - Sg_device *sdp = dev_get_drvdata(cl_dev); + struct sg_device *sdp = dev_get_drvdata(cl_dev); unsigned long iflags; - Sg_fd *sfp; + struct sg_fd *sfp; int val; if (!sdp) @@ -1711,17 +1739,17 @@ exit_sg(void) } static int -sg_start_req(Sg_request *srp, unsigned char *cmd) +sg_start_req(struct sg_request *srp, unsigned char *cmd) { int res; struct request *rq; - Sg_fd *sfp = srp->parentfp; + struct sg_fd *sfp = srp->parentfp; sg_io_hdr_t *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; unsigned int iov_count = hp->iovec_count; - Sg_scatter_hold *req_schp = &srp->data; - Sg_scatter_hold *rsv_schp = &sfp->reserve; + struct sg_scatter_hold *req_schp = &srp->data; + struct sg_scatter_hold *rsv_schp = &sfp->reserve; struct request_queue *q = sfp->parentdp->device->request_queue; struct rq_map_data *md, map_data; int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; @@ -1816,12 +1844,12 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) } static int -sg_finish_rem_req(Sg_request *srp) +sg_finish_rem_req(struct sg_request *srp) { int ret = 0; - Sg_fd *sfp = srp->parentfp; - Sg_scatter_hold *req_schp = &srp->data; + struct sg_fd *sfp = srp->parentfp; + struct sg_scatter_hold *req_schp = &srp->data; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_finish_rem_req: res_used=%d\n", @@ -1841,7 +1869,8 @@ sg_finish_rem_req(Sg_request *srp) } static int -sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) +sg_build_sgat(struct sg_scatter_hold *schp, const struct sg_fd *sfp, + int tablesize) { int sg_bufflen = tablesize * sizeof(struct page *); gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; @@ -1854,7 +1883,8 @@ sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) } static int -sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) +sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, + int buff_size) { int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; int sg_tablesize = sfp->parentdp->sg_tablesize; @@ -1932,7 +1962,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) } static void -sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp) +sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) { SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); @@ -1955,9 +1985,9 @@ sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp) } static int -sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) +sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_read_xfer) { - Sg_scatter_hold *schp = &srp->data; + struct sg_scatter_hold *schp = &srp->data; int k, num; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, @@ -1988,9 +2018,9 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) } static void -sg_build_reserve(Sg_fd * sfp, int req_size) +sg_build_reserve(struct sg_fd *sfp, int req_size) { - Sg_scatter_hold *schp = &sfp->reserve; + struct sg_scatter_hold *schp = &sfp->reserve; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_build_reserve: req_size=%d\n", req_size)); @@ -2006,10 +2036,10 @@ sg_build_reserve(Sg_fd * sfp, int req_size) } static void -sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) +sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) { - Sg_scatter_hold *req_schp = &srp->data; - Sg_scatter_hold *rsv_schp = &sfp->reserve; + struct sg_scatter_hold *req_schp = &srp->data; + struct sg_scatter_hold *rsv_schp = &sfp->reserve; int k, num, rem; srp->res_used = 1; @@ -2037,9 +2067,9 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) } static void -sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) +sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) { - Sg_scatter_hold *req_schp = &srp->data; + struct sg_scatter_hold *req_schp = &srp->data; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, "sg_unlink_reserve: req->k_use_sg=%d\n", @@ -2054,10 +2084,10 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) sfp->res_in_use = 0; } -static Sg_request * -sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy) +static struct sg_request * +sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) { - Sg_request *resp; + struct sg_request *resp; unsigned long iflags; *busy = false; @@ -2084,12 +2114,12 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy) } /* always adds to end of list */ -static Sg_request * -sg_add_request(Sg_fd * sfp) +static struct sg_request * +sg_add_request(struct sg_fd *sfp) { int k; unsigned long iflags; - Sg_request *rp = sfp->req_arr; + struct sg_request *rp = sfp->req_arr; write_lock_irqsave(&sfp->rq_list_lock, iflags); if (!list_empty(&sfp->rq_list)) { @@ -2103,7 +2133,7 @@ sg_add_request(Sg_fd * sfp) if (k >= SG_MAX_QUEUE) goto out_unlock; } - memset(rp, 0, sizeof (Sg_request)); + memset(rp, 0, sizeof(struct sg_request)); rp->parentfp = sfp; rp->header.duration = jiffies_to_msecs(jiffies); list_add_tail(&rp->entry, &sfp->rq_list); @@ -2116,7 +2146,7 @@ sg_add_request(Sg_fd * sfp) /* Return of 1 for found; 0 for not found */ static int -sg_remove_request(Sg_fd * sfp, Sg_request * srp) +sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) { unsigned long iflags; int res = 0; @@ -2142,10 +2172,10 @@ sg_remove_request(Sg_fd * sfp, Sg_request * srp) return res; } -static Sg_fd * -sg_add_sfp(Sg_device * sdp) +static struct sg_fd * +sg_add_sfp(struct sg_device *sdp) { - Sg_fd *sfp; + struct sg_fd *sfp; unsigned long iflags; int bufflen; @@ -2195,13 +2225,13 @@ sg_remove_sfp_usercontext(struct work_struct *work) { struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); struct sg_device *sdp = sfp->parentdp; - Sg_request *srp; + struct sg_request *srp; unsigned long iflags; /* Cleanup any responses which were never read(). */ write_lock_irqsave(&sfp->rq_list_lock, iflags); while (!list_empty(&sfp->rq_list)) { - srp = list_first_entry(&sfp->rq_list, Sg_request, entry); + srp = list_first_entry(&sfp->rq_list, struct sg_request, entry); sg_finish_rem_req(srp); list_del(&srp->entry); srp->parentfp = NULL; @@ -2266,12 +2296,13 @@ sg_last_dev(void) #endif /* must be called with sg_index_lock held */ -static Sg_device *sg_lookup_dev(int dev) +static struct sg_device * +sg_lookup_dev(int dev) { return idr_find(&sg_index_idr, dev); } -static Sg_device * +static struct sg_device * sg_get_dev(int dev) { struct sg_device *sdp; @@ -2367,13 +2398,15 @@ sg_proc_init(void) } -static int sg_proc_seq_show_int(struct seq_file *s, void *v) +static int +sg_proc_seq_show_int(struct seq_file *s, void *v) { seq_printf(s, "%d\n", *((int *)s->private)); return 0; } -static int sg_proc_single_open_adio(struct inode *inode, struct file *file) +static int +sg_proc_single_open_adio(struct inode *inode, struct file *file) { return single_open(file, sg_proc_seq_show_int, &sg_allow_dio); } @@ -2394,7 +2427,8 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer, return count; } -static int sg_proc_single_open_dressz(struct inode *inode, struct file *file) +static int +sg_proc_single_open_dressz(struct inode *inode, struct file *file) { return single_open(file, sg_proc_seq_show_int, &sg_big_buff); } @@ -2419,14 +2453,16 @@ sg_proc_write_dressz(struct file *filp, const char __user *buffer, return -ERANGE; } -static int sg_proc_seq_show_version(struct seq_file *s, void *v) +static int +sg_proc_seq_show_version(struct seq_file *s, void *v) { seq_printf(s, "%d\t%s [%s]\n", sg_version_num, SG_VERSION_STR, sg_version_date); return 0; } -static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v) +static int +sg_proc_seq_show_devhdr(struct seq_file *s, void *v) { seq_puts(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); return 0; @@ -2437,7 +2473,8 @@ struct sg_proc_deviter { size_t max; }; -static void * dev_seq_start(struct seq_file *s, loff_t *pos) +static void * +dev_seq_start(struct seq_file *s, loff_t *pos) { struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL); @@ -2452,7 +2489,8 @@ static void * dev_seq_start(struct seq_file *s, loff_t *pos) return it; } -static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) +static void * +dev_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct sg_proc_deviter * it = s->private; @@ -2460,15 +2498,17 @@ static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) return (it->index < it->max) ? it : NULL; } -static void dev_seq_stop(struct seq_file *s, void *v) +static void +dev_seq_stop(struct seq_file *s, void *v) { kfree(s->private); } -static int sg_proc_seq_show_dev(struct seq_file *s, void *v) +static int +sg_proc_seq_show_dev(struct seq_file *s, void *v) { struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; - Sg_device *sdp; + struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2491,10 +2531,11 @@ static int sg_proc_seq_show_dev(struct seq_file *s, void *v) return 0; } -static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) +static int +sg_proc_seq_show_devstrs(struct seq_file *s, void *v) { struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; - Sg_device *sdp; + struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2511,11 +2552,12 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) } /* must be called while holding sg_index_lock */ -static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) +static void +sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) { int k, new_interface, blen, usg; - Sg_request *srp; - Sg_fd *fp; + struct sg_request *srp; + struct sg_fd *fp; const sg_io_hdr_t *hp; const char * cp; unsigned int ms; @@ -2573,10 +2615,11 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) } } -static int sg_proc_seq_show_debug(struct seq_file *s, void *v) +static int +sg_proc_seq_show_debug(struct seq_file *s, void *v) { struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; - Sg_device *sdp; + struct sg_device *sdp; unsigned long iflags; if (it && (0 == it->index)) From patchwork Mon Oct 24 03:20:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016534 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 065D7C3A59D for ; Mon, 24 Oct 2022 03:23:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230126AbiJXDXE (ORCPT ); Sun, 23 Oct 2022 23:23:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230140AbiJXDWe (ORCPT ); Sun, 23 Oct 2022 23:22:34 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E40685247C for ; Sun, 23 Oct 2022 20:21:07 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 152DA2041CB; Mon, 24 Oct 2022 05:21:07 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aP1Mq1QBhnlF; Mon, 24 Oct 2022 05:21:06 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id B848D2041AF; Mon, 24 Oct 2022 05:21:05 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 03/44] sg: sg_log and is_enabled Date: Sun, 23 Oct 2022 23:20:17 -0400 Message-Id: <20221024032058.14077-4-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Replace SCSI_LOG_TIMEOUT macros with SG_LOG macros across the driver. The definition of SG_LOG calls SCSI_LOG_TIMEOUT if given and derived pointers are non-zero, calls pr_info otherwise. SG_LOGS additionally prints the sg device name and the thread id. The thread id is very useful, even in single threaded invocations because the driver not only uses the invocer's thread but also uses work queues and the main callback (i.e. sg_rq_end_io()) may hit any thread. Some interesting cases arise when the callback hits its invocer's thread. SG_LOGS takes 48 bytes on the stack to build this printf format string: "sg%u: tid=%d" whose size is clearly bounded above by the maximum size of those two integers. Protecting against the 'current' pointer being zero is for safety and the case where the boot device is SCSI and the sg driver is built into the kernel. Also when debugging, getting a message from a compromised kernel can be very useful in pinpointing the location of the failure. The simple fact that the SG_LOG macro is shorter than SCSI_LOG_TIMEOUT macro allow more error message "payload" per line. Also replace #if and #ifdef conditional compilations with the IS_ENABLED macro. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 251 +++++++++++++++++++++++----------------------- 1 file changed, 126 insertions(+), 125 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9078037d5b20..2eca7cfd2412 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -62,6 +62,15 @@ static char *sg_version_date = "20190606"; #define SG_MAX_DEVS 32768 +/* Comment out the following line to compile out CONFIG_SCSI_LOGGING stuff */ +#define CONFIG_SG_DEBUG 1 + +#if !IS_ENABLED(CONFIG_SG_DEBUG) +#if IS_ENABLED(CONFIG_DEBUG) /* If SG_DEBUG not defined, check for DEBUG */ +#define CONFIG_SG_DEBUG CONFIG_DEBUG +#endif +#endif + /* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater * than 16 bytes are "variable length" whose length is a multiple of 4 @@ -179,7 +188,7 @@ static ssize_t sg_new_write(struct sg_fd *sfp, struct file *file, static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, u8 *cmnd, int timeout, int blocking); static int sg_read_oxfer(struct sg_request *srp, char __user *outp, - int num_read_xfer); + int num_xfer); static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); static void sg_build_reserve(struct sg_fd *sfp, int req_size); static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, @@ -194,13 +203,45 @@ static int sg_remove_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); -#define SZ_SG_HEADER sizeof(struct sg_header) -#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) -#define SZ_SG_IOVEC sizeof(sg_iovec_t) -#define SZ_SG_REQ_INFO sizeof(sg_req_info_t) +#define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ +#define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ +#define SZ_SG_REQ_INFO ((int)sizeof(struct sg_req_info)) + +/* + * Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages. + * 'depth' is a number between 1 (most severe) and 7 (most noisy, most + * information). All messages are logged as informational (KERN_INFO). In + * the unexpected situation where sfp or sdp is NULL the macro reverts to + * a pr_info and ignores SCSI_LOG_TIMEOUT and always prints to the log. + * Example: this invocation: 'scsi_logging_level -s -T 3' will print + * depth (aka level) 1 and 2 SG_LOG() messages. + */ + +#define SG_PROC_DEBUG_SZ 8192 + +#if IS_ENABLED(CONFIG_SCSI_LOGGING) && IS_ENABLED(CONFIG_SG_DEBUG) +#define SG_LOG_BUFF_SZ 48 + +#define SG_LOG(depth, sfp, fmt, a...) \ + do { \ + char _b[SG_LOG_BUFF_SZ]; \ + int _tid = (current ? current->pid : -1); \ + struct sg_fd *_fp = sfp; \ + struct sg_device *_sdp = _fp ? _fp->parentdp : NULL; \ + \ + if (likely(_sdp)) { \ + snprintf(_b, sizeof(_b), "sg%u: tid=%d", \ + _sdp->index, _tid); \ + SCSI_LOG_TIMEOUT(depth, \ + sdev_prefix_printk(KERN_INFO, \ + _sdp->device, _b, fmt, ##a)); \ + } else \ + pr_info("sg: sdp or sfp NULL, " fmt, ##a); \ + } while (0) +#else +#define SG_LOG(depth, sfp, fmt, a...) do { } while (0) +#endif /* end of CONFIG_SCSI_LOGGING && SG_DEBUG conditional */ -#define sg_printk(prefix, sdp, fmt, a...) \ - sdev_prefix_printk(prefix, (sdp)->device, (sdp)->name, fmt, ##a) /* * The SCSI interfaces that use read() and write() as an asynchronous variant of @@ -287,9 +328,6 @@ sg_open(struct inode *inode, struct file *filp) if (IS_ERR(sdp)) return PTR_ERR(sdp); - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_open: flags=0x%x\n", op_flags)); - /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ retval = scsi_device_get(sdp->device); @@ -347,6 +385,9 @@ sg_open(struct inode *inode, struct file *filp) filp->private_data = sfp; sdp->open_cnt++; mutex_unlock(&sdp->open_rel_lock); + SG_LOG(3, sfp, "%s: minor=%d, op_flags=0x%x; %s count prior=%d%s\n", + __func__, min_dev, op_flags, "device open", sdp->open_cnt, + ((op_flags & O_NONBLOCK) ? " O_NONBLOCK" : "")); retval = 0; sg_put: @@ -377,9 +418,10 @@ sg_release(struct inode *inode, struct file *filp) sfp = filp->private_data; sdp = sfp->parentdp; + SG_LOG(3, sfp, "%s: device open count prior=%d\n", __func__, + sdp->open_cnt); if (!sdp) return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); @@ -418,10 +460,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) sfp = filp->private_data; sdp = sfp->parentdp; + SG_LOG(3, sfp, "%s: write(3rd arg) count=%d\n", __func__, (int)count); if (!sdp) return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_write: count=%d\n", (int) count)); if (atomic_read(&sdp->detaching)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || @@ -444,8 +485,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -EFAULT; if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp, - "sg_write: queue full\n")); + SG_LOG(1, sfp, "%s: queue full\n", __func__); return -EDOM; } mutex_lock(&sfp->f_mutex); @@ -458,9 +498,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) cmd_size = 12; } mutex_unlock(&sfp->f_mutex); - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp, - "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size)); -/* Determine buffer size. */ + SG_LOG(4, sfp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (unsigned int)opcode, cmd_size); input_size = count - cmd_size; mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; mxsize -= SZ_SG_HEADER; @@ -542,8 +581,7 @@ sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, - "sg_new_write: queue full\n")); + SG_LOG(1, sfp, "%s: queue full\n", __func__); return -EDOM; } srp->sg_io_owned = sg_io_owned; @@ -608,9 +646,8 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, hp->host_status = 0; hp->driver_status = 0; hp->resid = 0; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", - (int) cmnd[0], (int) hp->cmd_len)); + SG_LOG(4, sfp, "%s: opcode=0x%02x, cmd_sz=%d\n", __func__, + (int)cmnd[0], hp->cmd_len); if (hp->dxfer_len >= SZ_256M) { sg_remove_request(sfp, srp); @@ -619,8 +656,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, k = sg_start_req(srp, cmnd); if (k) { - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, - "sg_common_write: start_req err=%d\n", k)); + SG_LOG(1, sfp, "%s: start_req err=%d\n", __func__, k); sg_finish_rem_req(srp); sg_remove_request(sfp, srp); return k; /* probably out of space --> ENOMEM */ @@ -760,8 +796,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) if ((!(sfp = (struct sg_fd *)filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_read: count=%d\n", (int) count)); + SG_LOG(3, sfp, "%s: read() count=%d\n", __func__, (int)count); if (sfp->force_packid) retval = get_sg_io_pack_id(&req_pack_id, buf, count); @@ -934,8 +969,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, struct sg_request *srp; unsigned long iflags; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_ioctl: cmd=0x%x\n", (int) cmd_in)); + SG_LOG(6, sfp, "%s: cmd=0x%x, O_NONBLOCK=%d\n", __func__, cmd_in, + !!(filp->f_flags & O_NONBLOCK)); read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); switch (cmd_in) { @@ -1209,24 +1244,16 @@ sg_poll(struct file *filp, poll_table * wait) res |= EPOLLOUT | EPOLLWRNORM; } else if (count < SG_MAX_QUEUE) res |= EPOLLOUT | EPOLLWRNORM; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_poll: res=0x%x\n", (__force u32) res)); + SG_LOG(3, sfp, "%s: res=0x%x\n", __func__, (__force u32)res); return res; } static int sg_fasync(int fd, struct file *filp, int mode) { - struct sg_device *sdp; - struct sg_fd *sfp; - - sfp = filp->private_data; - sdp = sfp->parentdp; - if (!sdp) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_fasync: mode=%d\n", mode)); + struct sg_fd *sfp = filp->private_data; + SG_LOG(3, sfp, "%s: mode(%s)\n", __func__, (mode ? "add" : "remove")); return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1253,10 +1280,8 @@ sg_vma_fault(struct vm_fault *vmf) offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rsv_schp->bufflen) return VM_FAULT_SIGBUS; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, - "sg_vma_fault: offset=%lu, scatg=%d\n", - offset, rsv_schp->k_use_sg)); sa = vma->vm_start; + SG_LOG(3, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, sa, offset); length = 1 << (PAGE_SHIFT + rsv_schp->page_order); for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { len = vma->vm_end - sa; @@ -1296,9 +1321,8 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return -ENXIO; } req_sz = vma->vm_end - vma->vm_start; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, - "sg_mmap starting, vm_start=%p, len=%d\n", - (void *) vma->vm_start, (int) req_sz)); + SG_LOG(3, sfp, "%s: vm_start=%p, len=%d\n", __func__, + (void *)vma->vm_start, (int)req_sz); if (vma->vm_pgoff) return -EINVAL; /* want no offset */ rsv_schp = &sfp->reserve; @@ -1367,10 +1391,9 @@ sg_rq_end_io(struct request *rq, blk_status_t status) result = scmd->result; resid = scmd->resid_len; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp, - "sg_cmd_done: pack_id=%d, res=0x%x\n", - srp->header.pack_id, result)); srp->header.resid = resid; + SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, + srp->header.pack_id, result); ms = jiffies_to_msecs(jiffies); srp->header.duration = (ms > srp->header.duration) ? (ms - srp->header.duration) : 0; @@ -1489,7 +1512,7 @@ sg_alloc(struct scsi_device *scsidp) k = error; SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, - "sg_alloc: dev=%d \n", k)); + "%s: dev=%d, sdp=0x%p ++\n", __func__, k, sdp)); sprintf(sdp->name, "sg%d", k); sdp->device = scsidp; mutex_init(&sdp->open_rel_lock); @@ -1588,7 +1611,11 @@ sg_device_destroy(struct kref *kref) struct sg_device *sdp = container_of(kref, struct sg_device, d_ref); unsigned long flags; - /* CAUTION! Note that the device can still be found via idr_find() + SCSI_LOG_TIMEOUT(1, pr_info("[tid=%d] %s: sdp idx=%d, sdp=0x%p --\n", + (current ? current->pid : -1), __func__, + sdp->index, sdp)); + /* + * CAUTION! Note that the device can still be found via idr_find() * even though the refcount is 0. Therefore, do idr_remove() BEFORE * any other cleanup. */ @@ -1597,9 +1624,6 @@ sg_device_destroy(struct kref *kref) idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, flags); - SCSI_LOG_TIMEOUT(3, - sg_printk(KERN_INFO, sdp, "sg_device_destroy\n")); - kfree(sdp); } @@ -1619,8 +1643,8 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) if (val > 1) return; /* only want to do following once per device */ - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "%s\n", __func__)); + SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, sdp->device, + "%s: 0x%p\n", __func__, sdp)); read_lock_irqsave(&sdp->sfd_lock, iflags); list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { @@ -1711,9 +1735,7 @@ init_sg(void) sg_sysfs_valid = 1; rc = scsi_register_interface(&sg_interface); if (0 == rc) { -#ifdef CONFIG_SCSI_PROC_FS sg_proc_init(); -#endif /* CONFIG_SCSI_PROC_FS */ return 0; } class_destroy(sg_sysfs_class); @@ -1727,9 +1749,8 @@ static void __exit exit_sg(void) { unregister_sg_sysctls(); -#ifdef CONFIG_SCSI_PROC_FS - remove_proc_subtree("scsi/sg", NULL); -#endif /* CONFIG_SCSI_PROC_FS */ + if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) + remove_proc_subtree("scsi/sg", NULL); scsi_unregister_interface(&sg_interface); class_destroy(sg_sysfs_class); sg_sysfs_valid = 0; @@ -1755,9 +1776,8 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; struct scsi_cmnd *scmd; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_start_req: dxfer_len=%d\n", - dxfer_len)); + SG_LOG(4, sfp, "%s: dxfer_len=%d, data-%s\n", __func__, dxfer_len, + (rw ? "OUT" : "IN")); /* * NOTE @@ -1851,9 +1871,8 @@ sg_finish_rem_req(struct sg_request *srp) struct sg_fd *sfp = srp->parentfp; struct sg_scatter_hold *req_schp = &srp->data; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_finish_rem_req: res_used=%d\n", - (int) srp->res_used)); + SG_LOG(4, sfp, "%s: srp=0x%p%s\n", __func__, srp, + (srp->res_used) ? " rsv" : ""); if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1897,9 +1916,8 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, ++blk_size; /* don't know why */ /* round request up to next highest SG_SECTOR_SZ byte boundary */ blk_size = ALIGN(blk_size, SG_SECTOR_SZ); - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_build_indirect: buff_size=%d, blk_size=%d\n", - buff_size, blk_size)); + SG_LOG(4, sfp, "%s: buff_size=%d, blk_size=%d\n", __func__, buff_size, + blk_size); /* N.B. ret_sz carried into this block ... */ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); @@ -1935,18 +1953,13 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, scatter_elem_sz_prev = ret_sz; } } - - SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, - "sg_build_indirect: k=%d, num=%d, ret_sz=%d\n", - k, num, ret_sz)); + SG_LOG(5, sfp, "%s: k=%d, num=%d, ret_sz=%d\n", __func__, k, + num, ret_sz); } /* end of for loop */ schp->page_order = order; schp->k_use_sg = k; - SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, - "sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", - k, rem_sz)); - + SG_LOG(5, sfp, "%s: k_use_sg=%d, order=%d\n", __func__, k, order); schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; @@ -1964,35 +1977,34 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) { - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); + SG_LOG(4, sfp, "%s: num_sgat=%d\n", __func__, schp->k_use_sg); if (schp->pages && schp->sglist_len > 0) { if (!schp->dio_in_use) { int k; for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { - SCSI_LOG_TIMEOUT(5, - sg_printk(KERN_INFO, sfp->parentdp, - "sg_remove_scat: k=%d, pg=0x%p\n", - k, schp->pages[k])); + SG_LOG(5, sfp, "%s: pg[%d]=0x%p --\n", + __func__, k, schp->pages[k]); __free_pages(schp->pages[k], schp->page_order); } - kfree(schp->pages); } } memset(schp, 0, sizeof (*schp)); } +/* + * For sg v1 and v2 interface: with a command yielding a data-in buffer, after + * it has arrived in kernel memory, this function copies it to the user space, + * appended to given struct sg_header object. + */ static int sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_read_xfer) { struct sg_scatter_hold *schp = &srp->data; int k, num; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, - "sg_read_oxfer: num_read_xfer=%d\n", - num_read_xfer)); + SG_LOG(4, srp->parentfp, "%s: num_xfer=%d\n", __func__, num_read_xfer); if ((!outp) || (num_read_xfer <= 0)) return 0; @@ -2022,8 +2034,7 @@ sg_build_reserve(struct sg_fd *sfp, int req_size) { struct sg_scatter_hold *schp = &sfp->reserve; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_build_reserve: req_size=%d\n", req_size)); + SG_LOG(3, sfp, "%s: buflen=%d\n", __func__, req_size); do { if (req_size < PAGE_SIZE) req_size = PAGE_SIZE; @@ -2043,8 +2054,7 @@ sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) int k, num, rem; srp->res_used = 1; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_link_reserve: size=%d\n", size)); + SG_LOG(4, sfp, "%s: size=%d\n", __func__, size); rem = size; num = 1 << (PAGE_SHIFT + rsv_schp->page_order); @@ -2062,8 +2072,7 @@ sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) } if (k >= rsv_schp->k_use_sg) - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, - "sg_link_reserve: BAD size\n")); + SG_LOG(1, sfp, "%s: BAD size\n", __func__); } static void @@ -2071,9 +2080,8 @@ sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) { struct sg_scatter_hold *req_schp = &srp->data; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, - "sg_unlink_reserve: req->k_use_sg=%d\n", - (int) req_schp->k_use_sg)); + SG_LOG(4, srp->parentfp, "%s: req->k_use_sg=%d\n", __func__, + (int)req_schp->k_use_sg); req_schp->k_use_sg = 0; req_schp->bufflen = 0; req_schp->pages = NULL; @@ -2202,18 +2210,15 @@ sg_add_sfp(struct sg_device *sdp) } list_add_tail(&sfp->sfd_siblings, &sdp->sfds); write_unlock_irqrestore(&sdp->sfd_lock, iflags); - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_add_sfp: sfp=0x%p\n", sfp)); + SG_LOG(3, sfp, "%s: sfp=0x%p\n", __func__, sfp); if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; bufflen = min_t(int, sg_big_buff, max_sectors_bytes(sdp->device->request_queue)); sg_build_reserve(sfp, bufflen); - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_add_sfp: bufflen=%d, k_use_sg=%d\n", - sfp->reserve.bufflen, - sfp->reserve.k_use_sg)); + SG_LOG(3, sfp, "%s: bufflen=%d, k_use_sg=%d\n", __func__, + sfp->reserve.bufflen, sfp->reserve.k_use_sg); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); @@ -2239,15 +2244,12 @@ sg_remove_sfp_usercontext(struct work_struct *work) write_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (sfp->reserve.bufflen > 0) { - SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, - "sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", - (int) sfp->reserve.bufflen, - (int) sfp->reserve.k_use_sg)); + SG_LOG(6, sfp, "%s: bufflen=%d, k_use_sg=%d\n", __func__, + (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg); sg_remove_scat(sfp, &sfp->reserve); } - SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, - "sg_remove_sfp: sfp=0x%p\n", sfp)); + SG_LOG(6, sfp, "%s: sfp=0x%p\n", __func__, sfp); kfree(sfp); scsi_device_put(sdp->device); @@ -2270,7 +2272,6 @@ sg_remove_sfp(struct kref *kref) schedule_work(&sfp->ew.work); } -#ifdef CONFIG_SCSI_PROC_FS static int sg_idr_max_id(int id, void *p, void *data) { @@ -2282,19 +2283,6 @@ sg_idr_max_id(int id, void *p, void *data) return 0; } -static int -sg_last_dev(void) -{ - int k = -1; - unsigned long iflags; - - read_lock_irqsave(&sg_index_lock, iflags); - idr_for_each(&sg_index_idr, sg_idr_max_id, &k); - read_unlock_irqrestore(&sg_index_lock, iflags); - return k + 1; /* origin 1 */ -} -#endif - /* must be called with sg_index_lock held */ static struct sg_device * sg_lookup_dev(int dev) @@ -2324,7 +2312,7 @@ sg_get_dev(int dev) return sdp; } -#ifdef CONFIG_SCSI_PROC_FS +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* long, almost to end of file */ static int sg_proc_seq_show_int(struct seq_file *s, void *v); static int sg_proc_single_open_adio(struct inode *inode, struct file *file); @@ -2381,6 +2369,7 @@ static const struct seq_operations debug_seq_ops = { static int sg_proc_init(void) { +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) struct proc_dir_entry *p; p = proc_mkdir("scsi/sg", NULL); @@ -2394,9 +2383,21 @@ sg_proc_init(void) proc_create_seq("devices", S_IRUGO, p, &dev_seq_ops); proc_create_seq("device_strs", S_IRUGO, p, &devstrs_seq_ops); proc_create_single("version", S_IRUGO, p, sg_proc_seq_show_version); +#endif return 0; } +static int +sg_last_dev(void) +{ + int k = -1; + unsigned long iflags; + + read_lock_irqsave(&sg_index_lock, iflags); + idr_for_each(&sg_index_idr, sg_idr_max_id, &k); + read_unlock_irqrestore(&sg_index_lock, iflags); + return k + 1; /* origin 1 */ +} static int sg_proc_seq_show_int(struct seq_file *s, void *v) @@ -2654,7 +2655,7 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) return 0; } -#endif /* CONFIG_SCSI_PROC_FS */ +#endif /* CONFIG_SCSI_PROC_FS (~300 lines back) */ module_init(init_sg); module_exit(exit_sg); From patchwork Mon Oct 24 03:20:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016535 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 23DD2FA373D for ; Mon, 24 Oct 2022 03:23:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230273AbiJXDXH (ORCPT ); Sun, 23 Oct 2022 23:23:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34724 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230138AbiJXDWe (ORCPT ); Sun, 23 Oct 2022 23:22:34 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 043A654CBE for ; Sun, 23 Oct 2022 20:21:09 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id D60162041CE; Mon, 24 Oct 2022 05:21:08 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id eAAEYdMrsS+B; Mon, 24 Oct 2022 05:21:08 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 3D5782041AF; Mon, 24 Oct 2022 05:21:07 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Johannes Thumshirn , Christoph Hellwig , Hannes Reinecke Subject: [PATCH v25 04/44] sg: remove typedefs, type+formatting cleanup Date: Sun, 23 Oct 2022 23:20:18 -0400 Message-Id: <20221024032058.14077-5-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Typedefs for structure types are discouraged so those structures that are private to the driver have had their typedefs removed. This also means that most "camel" type variable names (i.e. mixed case) have been removed. Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 59 ++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2eca7cfd2412..17e5294fb3e9 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -187,8 +187,8 @@ static ssize_t sg_new_write(struct sg_fd *sfp, struct file *file, struct sg_request **o_srp); static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, u8 *cmnd, int timeout, int blocking); -static int sg_read_oxfer(struct sg_request *srp, char __user *outp, - int num_xfer); +static int sg_rd_append(struct sg_request *srp, char __user *outp, + int num_xfer); static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); static void sg_build_reserve(struct sg_fd *sfp, int req_size); static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, @@ -875,7 +875,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) if (count > old_hdr->reply_len) count = old_hdr->reply_len; if (count > SZ_SG_HEADER) { - if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) { + if (sg_rd_append(srp, buf, count - SZ_SG_HEADER)) { retval = -EFAULT; goto free_old_hdr; } @@ -1214,38 +1214,34 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) static __poll_t sg_poll(struct file *filp, poll_table * wait) { - __poll_t res = 0; - struct sg_device *sdp; - struct sg_fd *sfp; + __poll_t p_res = 0; + struct sg_fd *sfp = filp->private_data; struct sg_request *srp; int count = 0; unsigned long iflags; - sfp = filp->private_data; if (!sfp) return EPOLLERR; - sdp = sfp->parentdp; - if (!sdp) - return EPOLLERR; poll_wait(filp, &sfp->read_wait, wait); read_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { /* if any read waiting, flag it */ - if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) - res = EPOLLIN | EPOLLRDNORM; + if (p_res == 0 && srp->done == 1 && !srp->sg_io_owned) + p_res = EPOLLIN | EPOLLRDNORM; ++count; } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (atomic_read(&sdp->detaching)) - res |= EPOLLHUP; - else if (!sfp->cmd_q) { - if (0 == count) - res |= EPOLLOUT | EPOLLWRNORM; - } else if (count < SG_MAX_QUEUE) - res |= EPOLLOUT | EPOLLWRNORM; - SG_LOG(3, sfp, "%s: res=0x%x\n", __func__, (__force u32)res); - return res; + if (sfp->parentdp && atomic_read(&sfp->parentdp->detaching)) { + p_res |= EPOLLHUP; + } else if (!sfp->cmd_q) { + if (count == 0) + p_res |= EPOLLOUT | EPOLLWRNORM; + } else if (count < SG_MAX_QUEUE) { + p_res |= EPOLLOUT | EPOLLWRNORM; + } + SG_LOG(3, sfp, "%s: p_res=0x%x\n", __func__, (__force u32)p_res); + return p_res; } static int @@ -1773,11 +1769,11 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) struct sg_scatter_hold *rsv_schp = &sfp->reserve; struct request_queue *q = sfp->parentdp->device->request_queue; struct rq_map_data *md, map_data; - int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; + int r0w = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; struct scsi_cmnd *scmd; SG_LOG(4, sfp, "%s: dxfer_len=%d, data-%s\n", __func__, dxfer_len, - (rw ? "OUT" : "IN")); + (r0w ? "OUT" : "IN")); /* * NOTE @@ -1851,7 +1847,7 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) } res = blk_rq_map_user_io(rq, md, hp->dxferp, hp->dxfer_len, - GFP_ATOMIC, iov_count, iov_count, 1, rw); + GFP_ATOMIC, iov_count, iov_count, 1, r0w); if (!res) { srp->bio = rq->bio; @@ -1999,33 +1995,32 @@ sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) * appended to given struct sg_header object. */ static int -sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_read_xfer) +sg_rd_append(struct sg_request *srp, char __user *outp, int num_xfer) { struct sg_scatter_hold *schp = &srp->data; int k, num; - SG_LOG(4, srp->parentfp, "%s: num_xfer=%d\n", __func__, num_read_xfer); - if ((!outp) || (num_read_xfer <= 0)) + SG_LOG(4, srp->parentfp, "%s: num_xfer=%d\n", __func__, num_xfer); + if (!outp || num_xfer <= 0) return 0; num = 1 << (PAGE_SHIFT + schp->page_order); for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { - if (num > num_read_xfer) { + if (num > num_xfer) { if (copy_to_user(outp, page_address(schp->pages[k]), - num_read_xfer)) + num_xfer)) return -EFAULT; break; } else { if (copy_to_user(outp, page_address(schp->pages[k]), num)) return -EFAULT; - num_read_xfer -= num; - if (num_read_xfer <= 0) + num_xfer -= num; + if (num_xfer <= 0) break; outp += num; } } - return 0; } From patchwork Mon Oct 24 03:20:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016538 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3A572C433FE for ; Mon, 24 Oct 2022 03:23:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230283AbiJXDXL (ORCPT ); Sun, 23 Oct 2022 23:23:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58696 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230150AbiJXDWi (ORCPT ); Sun, 23 Oct 2022 23:22:38 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6F7A31AD8E for ; Sun, 23 Oct 2022 20:21:12 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 57BE52041CF; Mon, 24 Oct 2022 05:21:10 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Pnp-669-gEZe; Mon, 24 Oct 2022 05:21:10 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 0C05B2041AF; Mon, 24 Oct 2022 05:21:08 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 05/44] sg: bitops in sg_device Date: Sun, 23 Oct 2022 23:20:19 -0400 Message-Id: <20221024032058.14077-6-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Introduce bitops in sg_device to replace an atomic, a bool and a char. That char (sgdebug) had been reduced to only two states. Add some associated macros to make the code a little clearer. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 98 ++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 17e5294fb3e9..6fd70322ade0 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -79,6 +79,11 @@ static char *sg_version_date = "20190606"; #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) +/* Bit positions (flags) for sg_device::fdev_bm bitmask follow */ +#define SG_FDEV_EXCLUDE 0 /* have fd open with O_EXCL */ +#define SG_FDEV_DETACHING 1 /* may be unexpected device removal */ +#define SG_FDEV_LOG_SENSE 2 /* set by ioctl(SG_SET_DEBUG) */ + static int sg_big_buff = SG_DEF_RESERVED_SIZE; /* N.B. This variable is readable and writeable via /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer @@ -160,14 +165,12 @@ struct sg_device { /* holds the state of each scsi generic device */ struct scsi_device *device; wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ struct mutex open_rel_lock; /* held when in open() or release() */ - int sg_tablesize; /* adapter's max scatter-gather table size */ - u32 index; /* device index number */ struct list_head sfds; rwlock_t sfd_lock; /* protect access to sfd list */ - atomic_t detaching; /* 0->device usable, 1->device detaching */ - bool exclude; /* 1->open(O_EXCL) succeeded and is active */ + int sg_tablesize; /* adapter's max scatter-gather table size */ + u32 index; /* device index number */ int open_cnt; /* count of opens (perhaps < num(sfds) ) */ - char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ + unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ char name[DISK_NAME_LEN]; struct cdev *cdev; struct kref d_ref; @@ -207,6 +210,9 @@ static void sg_device_destroy(struct kref *kref); #define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ #define SZ_SG_REQ_INFO ((int)sizeof(struct sg_req_info)) +#define SG_IS_DETACHING(sdp) test_bit(SG_FDEV_DETACHING, (sdp)->fdev_bm) +#define SG_HAVE_EXCLUDE(sdp) test_bit(SG_FDEV_EXCLUDE, (sdp)->fdev_bm) + /* * Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages. * 'depth' is a number between 1 (most severe) and 7 (most noisy, most @@ -275,26 +281,26 @@ sg_wait_open_event(struct sg_device *sdp, bool o_excl) while (sdp->open_cnt > 0) { mutex_unlock(&sdp->open_rel_lock); retval = wait_event_interruptible(sdp->open_wait, - (atomic_read(&sdp->detaching) || + (SG_IS_DETACHING(sdp) || !sdp->open_cnt)); mutex_lock(&sdp->open_rel_lock); if (retval) /* -ERESTARTSYS */ return retval; - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; } } else { - while (sdp->exclude) { + while (SG_HAVE_EXCLUDE(sdp)) { mutex_unlock(&sdp->open_rel_lock); retval = wait_event_interruptible(sdp->open_wait, - (atomic_read(&sdp->detaching) || - !sdp->exclude)); + (SG_IS_DETACHING(sdp) || + !SG_HAVE_EXCLUDE(sdp))); mutex_lock(&sdp->open_rel_lock); if (retval) /* -ERESTARTSYS */ return retval; - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; } } @@ -356,7 +362,7 @@ sg_open(struct inode *inode, struct file *filp) goto error_mutex_locked; } } else { - if (sdp->exclude) { + if (SG_HAVE_EXCLUDE(sdp)) { retval = -EBUSY; goto error_mutex_locked; } @@ -369,10 +375,10 @@ sg_open(struct inode *inode, struct file *filp) /* N.B. at this point we are holding the open_rel_lock */ if (o_excl) - sdp->exclude = true; + set_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm); if (sdp->open_cnt < 1) { /* no existing opens */ - sdp->sgdebug = 0; + clear_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm); q = sdp->device->request_queue; sdp->sg_tablesize = queue_max_segments(q); } @@ -395,8 +401,8 @@ sg_open(struct inode *inode, struct file *filp) return retval; out_undo: - if (o_excl) { - sdp->exclude = false; /* undo if error */ + if (o_excl) { /* undo if error */ + clear_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm); wake_up_interruptible(&sdp->open_wait); } error_mutex_locked: @@ -430,12 +436,10 @@ sg_release(struct inode *inode, struct file *filp) /* possibly many open()s waiting on exclude clearing, start many; * only open(O_EXCL)s wait on 0==open_cnt so only start one */ - if (sdp->exclude) { - sdp->exclude = false; + if (test_and_clear_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm)) wake_up_interruptible_all(&sdp->open_wait); - } else if (0 == sdp->open_cnt) { + else if (sdp->open_cnt == 0) wake_up_interruptible(&sdp->open_wait); - } mutex_unlock(&sdp->open_rel_lock); return 0; } @@ -463,7 +467,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) SG_LOG(3, sfp, "%s: write(3rd arg) count=%d\n", __func__, (int)count); if (!sdp) return -ENXIO; - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) @@ -661,7 +665,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, sg_remove_request(sfp, srp); return k; /* probably out of space --> ENOMEM */ } - if (atomic_read(&sdp->detaching)) { + if (SG_IS_DETACHING(sdp)) { if (srp->bio) { blk_mq_free_request(srp->rq); srp->rq = NULL; @@ -809,7 +813,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) return -EAGAIN; retval = wait_event_interruptible(sfp->read_wait, ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || - (!busy && atomic_read(&sdp->detaching)))); + (!busy && SG_IS_DETACHING(sdp)))); if (!srp) /* signal or detaching */ return retval ? retval : -ENODEV; @@ -975,7 +979,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, switch (cmd_in) { case SG_IO: - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; if (!scsi_block_when_processing_errors(sdp->device)) return -ENXIO; @@ -1024,7 +1028,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, { sg_scsi_id_t v; - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; memset(&v, 0, sizeof(v)); v.host_no = sdp->device->host->host_no; @@ -1144,18 +1148,18 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return result; } case SG_EMULATED_HOST: - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; return put_user(sdp->device->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; return scsi_ioctl(sdp->device, filp->f_mode, cmd_in, p); case SG_SET_DEBUG: result = get_user(val, ip); if (result) return result; - sdp->sgdebug = (char) val; + assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, val); return 0; case BLKSECTGET: return put_user(max_sectors_bytes(sdp->device->request_queue), @@ -1175,7 +1179,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, case SCSI_IOCTL_PROBE_HOST: case SG_GET_TRANSFORM: case SG_SCSI_RESET: - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) return -ENODEV; break; default: @@ -1232,7 +1236,7 @@ sg_poll(struct file *filp, poll_table * wait) } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (sfp->parentdp && atomic_read(&sfp->parentdp->detaching)) { + if (sfp->parentdp && SG_IS_DETACHING(sfp->parentdp)) { p_res |= EPOLLHUP; } else if (!sfp->cmd_q) { if (count == 0) @@ -1380,7 +1384,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) return RQ_END_IO_NONE; sdp = sfp->parentdp; - if (unlikely(atomic_read(&sdp->detaching))) + if (unlikely(SG_IS_DETACHING(sdp))) pr_info("%s: device detaching\n", __func__); sense = scmd->sense_buffer; @@ -1401,9 +1405,9 @@ sg_rq_end_io(struct request *rq, blk_status_t status) srp->header.msg_status = COMMAND_COMPLETE; srp->header.host_status = host_byte(result); srp->header.driver_status = driver_byte(result); - if ((sdp->sgdebug > 0) && - ((CHECK_CONDITION == srp->header.masked_status) || - (COMMAND_TERMINATED == srp->header.masked_status))) + if (test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm) && + (srp->header.masked_status == CHECK_CONDITION || + srp->header.masked_status == COMMAND_TERMINATED)) __scsi_print_sense(sdp->device, __func__, sense, SCSI_SENSE_BUFFERSIZE); @@ -1514,7 +1518,7 @@ sg_alloc(struct scsi_device *scsidp) mutex_init(&sdp->open_rel_lock); INIT_LIST_HEAD(&sdp->sfds); init_waitqueue_head(&sdp->open_wait); - atomic_set(&sdp->detaching, 0); + clear_bit(SG_FDEV_DETACHING, sdp->fdev_bm); rwlock_init(&sdp->sfd_lock); sdp->sg_tablesize = queue_max_segments(q); sdp->index = k; @@ -1630,13 +1634,11 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) struct sg_device *sdp = dev_get_drvdata(cl_dev); unsigned long iflags; struct sg_fd *sfp; - int val; if (!sdp) return; - /* want sdp->detaching non-zero as soon as possible */ - val = atomic_inc_return(&sdp->detaching); - if (val > 1) + /* set this flag as soon as possible as it could be a surprise */ + if (test_and_set_bit(SG_FDEV_DETACHING, sdp->fdev_bm)) return; /* only want to do following once per device */ SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, sdp->device, @@ -2169,7 +2171,7 @@ sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) * removed the last response, which would leave nothing for them to * return other than -ENODEV. */ - if (unlikely(atomic_read(&sfp->parentdp->detaching))) + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) wake_up_interruptible_all(&sfp->read_wait); return res; @@ -2198,7 +2200,7 @@ sg_add_sfp(struct sg_device *sdp) sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; sfp->parentdp = sdp; write_lock_irqsave(&sdp->sfd_lock, iflags); - if (atomic_read(&sdp->detaching)) { + if (SG_IS_DETACHING(sdp)) { write_unlock_irqrestore(&sdp->sfd_lock, iflags); kfree(sfp); return ERR_PTR(-ENODEV); @@ -2295,8 +2297,8 @@ sg_get_dev(int dev) sdp = sg_lookup_dev(dev); if (!sdp) sdp = ERR_PTR(-ENXIO); - else if (atomic_read(&sdp->detaching)) { - /* If sdp->detaching, then the refcount may already be 0, in + else if (SG_IS_DETACHING(sdp)) { + /* If detaching, then the refcount may already be 0, in * which case it would be a bug to do kref_get(). */ sdp = ERR_PTR(-ENODEV); @@ -2510,8 +2512,7 @@ sg_proc_seq_show_dev(struct seq_file *s, void *v) read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - if ((NULL == sdp) || (NULL == sdp->device) || - (atomic_read(&sdp->detaching))) + if (!sdp || !sdp->device || SG_IS_DETACHING(sdp)) seq_puts(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); else { scsidp = sdp->device; @@ -2538,7 +2539,7 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; scsidp = sdp ? sdp->device : NULL; - if (sdp && scsidp && (!atomic_read(&sdp->detaching))) + if (sdp && scsidp && !SG_IS_DETACHING(sdp)) seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n", scsidp->vendor, scsidp->model, scsidp->rev); else @@ -2629,7 +2630,7 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) read_lock(&sdp->sfd_lock); if (!list_empty(&sdp->sfds)) { seq_printf(s, " >>> device=%s ", sdp->name); - if (atomic_read(&sdp->detaching)) + if (SG_IS_DETACHING(sdp)) seq_puts(s, "detaching pending close "); else if (sdp->device) { struct scsi_device *scsidp = sdp->device; @@ -2641,7 +2642,8 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) scsidp->host->hostt->emulated); } seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n", - sdp->sg_tablesize, sdp->exclude, sdp->open_cnt); + sdp->sg_tablesize, SG_HAVE_EXCLUDE(sdp), + sdp->open_cnt); sg_proc_debug_helper(s, sdp); } read_unlock(&sdp->sfd_lock); From patchwork Mon Oct 24 03:20:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016537 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C28CC3A59D for ; Mon, 24 Oct 2022 03:23:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230029AbiJXDXJ (ORCPT ); Sun, 23 Oct 2022 23:23:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60542 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230100AbiJXDWg (ORCPT ); Sun, 23 Oct 2022 23:22:36 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6FEB152091 for ; Sun, 23 Oct 2022 20:21:12 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id CF24F2041BB; Mon, 24 Oct 2022 05:21:11 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id S7pNfi1Cg53b; Mon, 24 Oct 2022 05:21:11 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 835B92041AF; Mon, 24 Oct 2022 05:21:10 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 06/44] sg: make open count an atomic Date: Sun, 23 Oct 2022 23:20:20 -0400 Message-Id: <20221024032058.14077-7-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Convert sg_device::open_cnt into an atomic. Also rename sg_tablesize into the more descriptive max_sgat_elems. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6fd70322ade0..1c419bf9b435 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -167,9 +167,9 @@ struct sg_device { /* holds the state of each scsi generic device */ struct mutex open_rel_lock; /* held when in open() or release() */ struct list_head sfds; rwlock_t sfd_lock; /* protect access to sfd list */ - int sg_tablesize; /* adapter's max scatter-gather table size */ + int max_sgat_elems; /* adapter's max sgat number of elements */ u32 index; /* device index number */ - int open_cnt; /* count of opens (perhaps < num(sfds) ) */ + atomic_t open_cnt; /* count of opens (perhaps < num(sfds) ) */ unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ char name[DISK_NAME_LEN]; struct cdev *cdev; @@ -278,11 +278,11 @@ sg_wait_open_event(struct sg_device *sdp, bool o_excl) int retval = 0; if (o_excl) { - while (sdp->open_cnt > 0) { + while (atomic_read(&sdp->open_cnt) > 0) { mutex_unlock(&sdp->open_rel_lock); retval = wait_event_interruptible(sdp->open_wait, (SG_IS_DETACHING(sdp) || - !sdp->open_cnt)); + atomic_read(&sdp->open_cnt) == 0)); mutex_lock(&sdp->open_rel_lock); if (retval) /* -ERESTARTSYS */ @@ -330,7 +330,7 @@ sg_open(struct inode *inode, struct file *filp) o_excl = !!(op_flags & O_EXCL); if (o_excl && ((op_flags & O_ACCMODE) == O_RDONLY)) return -EPERM; /* Can't lock it with read only access */ - sdp = sg_get_dev(min_dev); + sdp = sg_get_dev(min_dev); /* increments sdp->d_ref */ if (IS_ERR(sdp)) return PTR_ERR(sdp); @@ -357,7 +357,7 @@ sg_open(struct inode *inode, struct file *filp) mutex_lock(&sdp->open_rel_lock); if (op_flags & O_NONBLOCK) { if (o_excl) { - if (sdp->open_cnt > 0) { + if (atomic_read(&sdp->open_cnt) > 0) { retval = -EBUSY; goto error_mutex_locked; } @@ -377,27 +377,29 @@ sg_open(struct inode *inode, struct file *filp) if (o_excl) set_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm); - if (sdp->open_cnt < 1) { /* no existing opens */ + if (atomic_read(&sdp->open_cnt) < 1) { /* no existing opens */ clear_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm); q = sdp->device->request_queue; - sdp->sg_tablesize = queue_max_segments(q); + sdp->max_sgat_elems = queue_max_segments(q); } - sfp = sg_add_sfp(sdp); + sfp = sg_add_sfp(sdp); /* increments sdp->d_ref */ if (IS_ERR(sfp)) { retval = PTR_ERR(sfp); goto out_undo; } filp->private_data = sfp; - sdp->open_cnt++; + atomic_inc(&sdp->open_cnt); mutex_unlock(&sdp->open_rel_lock); SG_LOG(3, sfp, "%s: minor=%d, op_flags=0x%x; %s count prior=%d%s\n", - __func__, min_dev, op_flags, "device open", sdp->open_cnt, + __func__, min_dev, op_flags, "device open", + atomic_read(&sdp->open_cnt), ((op_flags & O_NONBLOCK) ? " O_NONBLOCK" : "")); retval = 0; sg_put: kref_put(&sdp->d_ref, sg_device_destroy); + /* if success, sdp->d_ref is incremented twice, decremented once */ return retval; out_undo: @@ -425,20 +427,20 @@ sg_release(struct inode *inode, struct file *filp) sfp = filp->private_data; sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: device open count prior=%d\n", __func__, - sdp->open_cnt); + atomic_read(&sdp->open_cnt)); if (!sdp) return -ENXIO; mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); kref_put(&sfp->f_ref, sg_remove_sfp); - sdp->open_cnt--; + atomic_dec(&sdp->open_cnt); /* possibly many open()s waiting on exclude clearing, start many; * only open(O_EXCL)s wait on 0==open_cnt so only start one */ if (test_and_clear_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm)) wake_up_interruptible_all(&sdp->open_wait); - else if (sdp->open_cnt == 0) + else if (atomic_read(&sdp->open_cnt) == 0) wake_up_interruptible(&sdp->open_wait); mutex_unlock(&sdp->open_rel_lock); return 0; @@ -1069,7 +1071,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, read_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(val, ip); case SG_GET_SG_TABLESIZE: - return put_user(sdp->sg_tablesize, ip); + return put_user(sdp->max_sgat_elems, ip); case SG_SET_RESERVED_SIZE: result = get_user(val, ip); if (result) @@ -1520,7 +1522,7 @@ sg_alloc(struct scsi_device *scsidp) init_waitqueue_head(&sdp->open_wait); clear_bit(SG_FDEV_DETACHING, sdp->fdev_bm); rwlock_init(&sdp->sfd_lock); - sdp->sg_tablesize = queue_max_segments(q); + sdp->max_sgat_elems = queue_max_segments(q); sdp->index = k; kref_init(&sdp->d_ref); error = 0; @@ -1904,7 +1906,7 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, int buff_size) { int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; - int sg_tablesize = sfp->parentdp->sg_tablesize; + int max_sgat_elems = sfp->parentdp->max_sgat_elems; int blk_size = buff_size, order; gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; @@ -1918,7 +1920,7 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, blk_size); /* N.B. ret_sz carried into this block ... */ - mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); + mx_sc_elems = sg_build_sgat(schp, sfp, max_sgat_elems); if (mx_sc_elems < 0) return mx_sc_elems; /* most likely -ENOMEM */ @@ -2641,9 +2643,9 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) scsidp->lun, scsidp->host->hostt->emulated); } - seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n", - sdp->sg_tablesize, SG_HAVE_EXCLUDE(sdp), - sdp->open_cnt); + seq_printf(s, " max_sgat_elems=%d excl=%d open_cnt=%d\n", + sdp->max_sgat_elems, SG_HAVE_EXCLUDE(sdp), + atomic_read(&sdp->open_cnt)); sg_proc_debug_helper(s, sdp); } read_unlock(&sdp->sfd_lock); From patchwork Mon Oct 24 03:20:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016540 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97FB6C433FE for ; Mon, 24 Oct 2022 03:23:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230334AbiJXDXb (ORCPT ); Sun, 23 Oct 2022 23:23:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230329AbiJXDWl (ORCPT ); Sun, 23 Oct 2022 23:22:41 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 68ECB57255 for ; Sun, 23 Oct 2022 20:21:14 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 5A3062041BD; Mon, 24 Oct 2022 05:21:13 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2v1ta+PgBPRR; Mon, 24 Oct 2022 05:21:13 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 08C412041AF; Mon, 24 Oct 2022 05:21:11 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 07/44] sg: move header to uapi section Date: Sun, 23 Oct 2022 23:20:21 -0400 Message-Id: <20221024032058.14077-8-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Move user interface part of scsi/sg.h into the new header file: include/uapi/scsi/sg.h . Since scsi/sg.h includes the new header, other code including scsi/sg.h should not be impacted. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- include/scsi/sg.h | 301 +--------------------------------- include/uapi/scsi/sg.h | 363 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+), 292 deletions(-) create mode 100644 include/uapi/scsi/sg.h diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 068e35d36557..69dc05ed4467 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -4,67 +4,16 @@ #include +#if defined(__KERNEL__) + /* - * History: - * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user - * process control of SCSI devices. - * Development Sponsored by Killy Corp. NY NY - * - * Original driver (sg.h): - * Copyright (C) 1992 Lawrence Foard - * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2014 Douglas Gilbert - * - * Version: 3.5.36 (20140603) - * This version is for 2.6 and 3 series kernels. - * - * Documentation - * ============= - * A web site for the SG device driver can be found at: - * http://sg.danny.cz/sg [alternatively check the MAINTAINERS file] - * The documentation for the sg version 3 driver can be found at: - * http://sg.danny.cz/sg/p/sg_v3_ho.html - * Also see: /Documentation/scsi/scsi-generic.rst - * - * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html + * In version 3.9.01 of the sg driver, this file was split in two, with the + * bulk of the user space interface being placed in the file being included + * in the following line. */ +#include -typedef struct sg_iovec /* same structure as used by readv() Linux system */ -{ /* call. It defines one scatter-gather element. */ - void __user *iov_base; /* Starting address */ - size_t iov_len; /* Length in bytes */ -} sg_iovec_t; - - -typedef struct sg_io_hdr -{ - int interface_id; /* [i] 'S' for SCSI generic (required) */ - int dxfer_direction; /* [i] data transfer direction */ - unsigned char cmd_len; /* [i] SCSI command length */ - unsigned char mx_sb_len; /* [i] max length to write to sbp */ - unsigned short iovec_count; /* [i] 0 implies no scatter gather */ - unsigned int dxfer_len; /* [i] byte count of data transfer */ - void __user *dxferp; /* [i], [*io] points to data transfer memory - or scatter gather list */ - unsigned char __user *cmdp; /* [i], [*i] points to command to perform */ - void __user *sbp; /* [i], [*o] points to sense_buffer memory */ - unsigned int timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */ - unsigned int flags; /* [i] 0 -> default, see SG_FLAG... */ - int pack_id; /* [i->o] unused internally (normally) */ - void __user * usr_ptr; /* [i->o] unused internally */ - unsigned char status; /* [o] scsi status */ - unsigned char masked_status;/* [o] shifted, masked scsi status */ - unsigned char msg_status; /* [o] messaging level data (optional) */ - unsigned char sb_len_wr; /* [o] byte count actually written to sbp */ - unsigned short host_status; /* [o] errors from host adapter */ - unsigned short driver_status;/* [o] errors from software driver */ - int resid; /* [o] dxfer_len - actual_transferred */ - unsigned int duration; /* [o] time taken by cmd (unit: millisec) */ - unsigned int info; /* [o] auxiliary information */ -} sg_io_hdr_t; /* 64 bytes long (on i386) */ - -#if defined(__KERNEL__) #include struct compat_sg_io_hdr { @@ -92,242 +41,10 @@ struct compat_sg_io_hdr { compat_uint_t duration; /* [o] time taken by cmd (unit: millisec) */ compat_uint_t info; /* [o] auxiliary information */ }; -#endif - -#define SG_INTERFACE_ID_ORIG 'S' - -/* Use negative values to flag difference from original sg_header structure */ -#define SG_DXFER_NONE (-1) /* e.g. a SCSI Test Unit Ready command */ -#define SG_DXFER_TO_DEV (-2) /* e.g. a SCSI WRITE command */ -#define SG_DXFER_FROM_DEV (-3) /* e.g. a SCSI READ command */ -#define SG_DXFER_TO_FROM_DEV (-4) /* treated like SG_DXFER_FROM_DEV with the - additional property than during indirect - IO the user buffer is copied into the - kernel buffers before the transfer */ -#define SG_DXFER_UNKNOWN (-5) /* Unknown data direction */ - -/* following flag values can be "or"-ed together */ -#define SG_FLAG_DIRECT_IO 1 /* default is indirect IO */ -#define SG_FLAG_UNUSED_LUN_INHIBIT 2 /* default is overwrite lun in SCSI */ - /* command block (when <= SCSI_2) */ -#define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */ -#define SG_FLAG_NO_DXFER 0x10000 /* no transfer of kernel buffers to/from */ - /* user space (debug indirect IO) */ -/* defaults:: for sg driver: Q_AT_HEAD; for block layer: Q_AT_TAIL */ -#define SG_FLAG_Q_AT_TAIL 0x10 -#define SG_FLAG_Q_AT_HEAD 0x20 - -/* following 'info' values are "or"-ed together */ -#define SG_INFO_OK_MASK 0x1 -#define SG_INFO_OK 0x0 /* no sense, host nor driver "noise" */ -#define SG_INFO_CHECK 0x1 /* something abnormal happened */ - -#define SG_INFO_DIRECT_IO_MASK 0x6 -#define SG_INFO_INDIRECT_IO 0x0 /* data xfer via kernel buffers (or no xfer) */ -#define SG_INFO_DIRECT_IO 0x2 /* direct IO requested and performed */ -#define SG_INFO_MIXED_IO 0x4 /* part direct, part indirect IO */ - -/* - * Obsolete DRIVER_SENSE driver byte - * - * Originally the SCSI midlayer would set the DRIVER_SENSE driver byte when - * a sense code was generated and a sense buffer was allocated. - * However, as nowadays every scsi command has a sense code allocated this - * distinction became moot as one could check the sense buffer directly. - * Consequently this byte is not set anymore from the midlayer, but SG will - * keep setting this byte to be compatible with previous releases. - */ -#define DRIVER_SENSE 0x08 -/* Obsolete driver_byte() declaration */ -#define driver_byte(result) (((result) >> 24) & 0xff) - -/* - * Original linux SCSI Status codes. They are shifted 1 bit right - * from those found in the SCSI standards. - */ - -#define GOOD 0x00 -#define CHECK_CONDITION 0x01 -#define CONDITION_GOOD 0x02 -#define BUSY 0x04 -#define INTERMEDIATE_GOOD 0x08 -#define INTERMEDIATE_C_GOOD 0x0a -#define RESERVATION_CONFLICT 0x0c -#define COMMAND_TERMINATED 0x11 -#define QUEUE_FULL 0x14 -#define ACA_ACTIVE 0x18 -#define TASK_ABORTED 0x20 - -/* Obsolete status_byte() declaration */ -#define status_byte(result) (((result) >> 1) & 0x7f) - -typedef struct sg_scsi_id { /* used by SG_GET_SCSI_ID ioctl() */ - int host_no; /* as in "scsi" where 'n' is one of 0, 1, 2 etc */ - int channel; - int scsi_id; /* scsi id of target device */ - int lun; - int scsi_type; /* TYPE_... defined in scsi/scsi.h */ - short h_cmd_per_lun;/* host (adapter) maximum commands per lun */ - short d_queue_depth;/* device (or adapter) maximum queue length */ - int unused[2]; /* probably find a good use, set 0 for now */ -} sg_scsi_id_t; /* 32 bytes long on i386 */ - -typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ - char req_state; /* 0 -> not used, 1 -> written, 2 -> ready to read */ - char orphan; /* 0 -> normal request, 1 -> from interrupted SG_IO */ - char sg_io_owned; /* 0 -> complete with read(), 1 -> owned by SG_IO */ - char problem; /* 0 -> no problem detected, 1 -> error to report */ - int pack_id; /* pack_id associated with request */ - void __user *usr_ptr; /* user provided pointer (in new interface) */ - unsigned int duration; /* millisecs elapsed since written (req_state==1) - or request duration (req_state==2) */ - int unused; -} sg_req_info_t; /* 20 bytes long on i386 */ - - -/* IOCTLs: Those ioctls that are relevant to the SG 3.x drivers follow. - [Those that only apply to the SG 2.x drivers are at the end of the file.] - (_GET_s yield result via 'int *' 3rd argument unless otherwise indicated) */ - -#define SG_EMULATED_HOST 0x2203 /* true for emulated host adapter (ATAPI) */ - -/* Used to configure SCSI command transformation layer for ATAPI devices */ -/* Only supported by the ide-scsi driver */ -#define SG_SET_TRANSFORM 0x2204 /* N.B. 3rd arg is not pointer but value: */ - /* 3rd arg = 0 to disable transform, 1 to enable it */ -#define SG_GET_TRANSFORM 0x2205 - -#define SG_SET_RESERVED_SIZE 0x2275 /* request a new reserved buffer size */ -#define SG_GET_RESERVED_SIZE 0x2272 /* actual size of reserved buffer */ - -/* The following ioctl has a 'sg_scsi_id_t *' object as its 3rd argument. */ -#define SG_GET_SCSI_ID 0x2276 /* Yields fd's bus, chan, dev, lun + type */ -/* SCSI id information can also be obtained from SCSI_IOCTL_GET_IDLUN */ - -/* Override host setting and always DMA using low memory ( <16MB on i386) */ -#define SG_SET_FORCE_LOW_DMA 0x2279 /* 0-> use adapter setting, 1-> force */ -#define SG_GET_LOW_DMA 0x227a /* 0-> use all ram for dma; 1-> low dma ram */ -/* When SG_SET_FORCE_PACK_ID set to 1, pack_id is input to read() which - tries to fetch a packet with a matching pack_id, waits, or returns EAGAIN. - If pack_id is -1 then read oldest waiting. When ...FORCE_PACK_ID set to 0 - then pack_id ignored by read() and oldest readable fetched. */ -#define SG_SET_FORCE_PACK_ID 0x227b -#define SG_GET_PACK_ID 0x227c /* Yields oldest readable pack_id (or -1) */ - -#define SG_GET_NUM_WAITING 0x227d /* Number of commands awaiting read() */ - -/* Yields max scatter gather tablesize allowed by current host adapter */ -#define SG_GET_SG_TABLESIZE 0x227F /* 0 implies can't do scatter gather */ - -#define SG_GET_VERSION_NUM 0x2282 /* Example: version 2.1.34 yields 20134 */ - -/* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */ -#define SG_SCSI_RESET 0x2284 -/* Associated values that can be given to SG_SCSI_RESET follow. - * SG_SCSI_RESET_NO_ESCALATE may be OR-ed to the _DEVICE, _TARGET, _BUS - * or _HOST reset value so only that action is attempted. */ -#define SG_SCSI_RESET_NOTHING 0 -#define SG_SCSI_RESET_DEVICE 1 -#define SG_SCSI_RESET_BUS 2 -#define SG_SCSI_RESET_HOST 3 -#define SG_SCSI_RESET_TARGET 4 -#define SG_SCSI_RESET_NO_ESCALATE 0x100 - -/* synchronous SCSI command ioctl, (only in version 3 interface) */ -#define SG_IO 0x2285 /* similar effect as write() followed by read() */ - -#define SG_GET_REQUEST_TABLE 0x2286 /* yields table of active requests */ - -/* How to treat EINTR during SG_IO ioctl(), only in SG 3.x series */ -#define SG_SET_KEEP_ORPHAN 0x2287 /* 1 -> hold for read(), 0 -> drop (def) */ -#define SG_GET_KEEP_ORPHAN 0x2288 - -/* yields scsi midlevel's access_count for this SCSI device */ -#define SG_GET_ACCESS_COUNT 0x2289 - - -#define SG_SCATTER_SZ (8 * 4096) -/* Largest size (in bytes) a single scatter-gather list element can have. - The value used by the driver is 'max(SG_SCATTER_SZ, PAGE_SIZE)'. - This value should be a power of 2 (and may be rounded up internally). - If scatter-gather is not supported by adapter then this value is the - largest data block that can be read/written by a single scsi command. */ - -#define SG_DEFAULT_RETRIES 0 - -/* Defaults, commented if they differ from original sg driver */ -#define SG_DEF_FORCE_PACK_ID 0 -#define SG_DEF_KEEP_ORPHAN 0 -#define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */ - -/* maximum outstanding requests, write() yields EDOM if exceeded */ -#define SG_MAX_QUEUE 16 - -#define SG_BIG_BUFF SG_DEF_RESERVED_SIZE /* for backward compatibility */ - -/* Alternate style type names, "..._t" variants preferred */ -typedef struct sg_io_hdr Sg_io_hdr; -typedef struct sg_io_vec Sg_io_vec; -typedef struct sg_scsi_id Sg_scsi_id; -typedef struct sg_req_info Sg_req_info; - - -/* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ -/* The older SG interface based on the 'sg_header' structure follows. */ -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ - -#define SG_MAX_SENSE 16 /* this only applies to the sg_header interface */ - -struct sg_header -{ - int pack_len; /* [o] reply_len (ie useless), ignored as input */ - int reply_len; /* [i] max length of expected reply (inc. sg_header) */ - int pack_id; /* [io] id number of packet (use ints >= 0) */ - int result; /* [o] 0==ok, else (+ve) Unix errno (best ignored) */ - unsigned int twelve_byte:1; - /* [i] Force 12 byte command length for group 6 & 7 commands */ - unsigned int target_status:5; /* [o] scsi status from target */ - unsigned int host_status:8; /* [o] host status (see "DID" codes) */ - unsigned int driver_status:8; /* [o] driver status+suggestion */ - unsigned int other_flags:10; /* unused */ - unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases: - when target_status is CHECK_CONDITION or - when target_status is COMMAND_TERMINATED or - when (driver_status & DRIVER_SENSE) is true. */ -}; /* This structure is 36 bytes long on i386 */ - - -/* IOCTLs: The following are not required (or ignored) when the sg_io_hdr_t - interface is used. They are kept for backward compatibility with - the original and version 2 drivers. */ - -#define SG_SET_TIMEOUT 0x2201 /* unit: jiffies (10ms on i386) */ -#define SG_GET_TIMEOUT 0x2202 /* yield timeout as _return_ value */ - -/* Get/set command queuing state per fd (default is SG_DEF_COMMAND_Q. - Each time a sg_io_hdr_t object is seen on this file descriptor, this - command queuing flag is set on (overriding the previous setting). */ -#define SG_GET_COMMAND_Q 0x2270 /* Yields 0 (queuing off) or 1 (on) */ -#define SG_SET_COMMAND_Q 0x2271 /* Change queuing state with 0 or 1 */ - -/* Turn on/off error sense trace (1 and 0 respectively, default is off). - Try using: "# cat /proc/scsi/sg/debug" instead in the v3 driver */ -#define SG_SET_DEBUG 0x227e /* 0 -> turn off debug */ - -#define SG_NEXT_CMD_LEN 0x2283 /* override SCSI command length with given - number on the next write() on this file descriptor */ - - -/* Defaults, commented if they differ from original sg driver */ -#ifdef __KERNEL__ -#define SG_DEFAULT_TIMEOUT_USER (60*USER_HZ) /* HZ == 'jiffies in 1 second' */ -#else -#define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */ +#define SG_DEFAULT_TIMEOUT_USER (60 * USER_HZ) /* HZ: jiffies in 1 second */ #endif -#define SG_DEF_COMMAND_Q 0 /* command queuing is always on when - the new interface is used */ -#define SG_DEF_UNDERRUN_FLAG 0 +#undef SG_DEFAULT_TIMEOUT /* because of conflicting define in sg.c */ -#endif +#endif /* end of ifndef _SCSI_GENERIC_H guard */ diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h new file mode 100644 index 000000000000..3c507fbef30f --- /dev/null +++ b/include/uapi/scsi/sg.h @@ -0,0 +1,363 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_SCSI_SG_H +#define _UAPI_SCSI_SG_H + +/* + * History: + * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user + * process control of SCSI devices. + * Development Sponsored by Killy Corp. NY NY + * + * Original driver (sg.h): + * Copyright (C) 1992 Lawrence Foard + * + * Later extensions (versions 2, 3 and 4) to driver: + * Copyright (C) 1998 - 2018 Douglas Gilbert + * + * Version 4.0.11 (20190502) + * This version is for Linux 4 and 5 series kernels. + * + * Documentation + * ============= + * A web site for the SG device driver can be found at: + * https://sg.danny.cz/sg [alternatively check the MAINTAINERS file] + * The documentation for the sg version 3 driver can be found at: + * https://sg.danny.cz/sg/p/sg_v3_ho.html + * Also see: /Documentation/scsi/scsi-generic.txt + * + * For utility and test programs see: https://sg.danny.cz/sg/sg3_utils.html + */ + +#include +#include + +/* bsg.h contains the sg v4 user space interface structure (sg_io_v4). */ +#include + +/* + * Same structure as used by readv() call. It defines one scatter-gather + * element. "Scatter-gather" is abbreviated to "sgat" in this driver to + * avoid confusion with this driver's name. + */ +typedef struct sg_iovec { + void __user *iov_base; /* Starting address (of a byte) */ + size_t iov_len; /* Length in bytes */ +} sg_iovec_t; + + +typedef struct sg_io_hdr { + int interface_id; /* [i] 'S' for SCSI generic (required) */ + int dxfer_direction; /* [i] data transfer direction */ + unsigned char cmd_len; /* [i] SCSI command length */ + unsigned char mx_sb_len;/* [i] max length to write to sbp */ + unsigned short iovec_count; /* [i] 0 implies no sgat list */ + unsigned int dxfer_len; /* [i] byte count of data transfer */ + /* dxferp points to data transfer memory or scatter gather list */ + void __user *dxferp; /* [i], [*io] */ + unsigned char __user *cmdp;/* [i], [*i] points to command to perform */ + void __user *sbp; /* [i], [*o] points to sense_buffer memory */ + unsigned int timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */ + unsigned int flags; /* [i] 0 -> default, see SG_FLAG... */ + int pack_id; /* [i->o] unused internally (normally) */ + void __user *usr_ptr; /* [i->o] unused internally */ + unsigned char status; /* [o] scsi status */ + unsigned char masked_status;/* [o] shifted, masked scsi status */ + unsigned char msg_status;/* [o] messaging level data (optional) */ + unsigned char sb_len_wr; /* [o] byte count actually written to sbp */ + unsigned short host_status; /* [o] errors from host adapter */ + unsigned short driver_status;/* [o] errors from software driver */ + int resid; /* [o] dxfer_len - actual_transferred */ + /* unit may be nanoseconds after SG_SET_GET_EXTENDED ioctl use */ + unsigned int duration; /* [o] time taken by cmd (unit: millisec) */ + unsigned int info; /* [o] auxiliary information */ +} sg_io_hdr_t; + +#define SG_INTERFACE_ID_ORIG 'S' + +/* Use negative values to flag difference from original sg_header structure */ +#define SG_DXFER_NONE (-1) /* e.g. a SCSI Test Unit Ready command */ +#define SG_DXFER_TO_DEV (-2) /* data-out buffer e.g. SCSI WRITE command */ +#define SG_DXFER_FROM_DEV (-3) /* data-in buffer e.g. SCSI READ command */ +/* + * SG_DXFER_TO_FROM_DEV is treated like SG_DXFER_FROM_DEV with the additional + * property than during indirect IO the user buffer is copied into the kernel + * buffers _before_ the transfer from the device takes place. Useful if short + * DMA transfers (less than requested) are not reported (e.g. resid always 0). + */ +#define SG_DXFER_TO_FROM_DEV (-4) +#define SG_DXFER_UNKNOWN (-5) /* Unknown data direction, do not use */ + +/* following flag values can be OR-ed together in v3::flags or v4::flags */ +#define SG_FLAG_DIRECT_IO 1 /* default is indirect IO */ +/* SG_FLAG_UNUSED_LUN_INHIBIT is ignored in sg v4 driver */ +#define SG_FLAG_UNUSED_LUN_INHIBIT 2 /* ignored, was LUN overwrite in cdb */ +#define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */ +/* no transfers between kernel<-->user space; keep device<-->kernel xfers */ +#define SG_FLAG_NO_DXFER 0x10000 /* See comment on previous line! */ +/* defaults: for sg driver (v3_v4): Q_AT_HEAD; for block layer: Q_AT_TAIL */ +#define SG_FLAG_Q_AT_TAIL 0x10 +#define SG_FLAG_Q_AT_HEAD 0x20 + +/* Output (potentially OR-ed together) in v3::info or v4::info field */ +#define SG_INFO_OK_MASK 0x1 +#define SG_INFO_OK 0x0 /* no sense, host nor driver "noise" */ +#define SG_INFO_CHECK 0x1 /* something abnormal happened */ + +#define SG_INFO_DIRECT_IO_MASK 0x6 +#define SG_INFO_INDIRECT_IO 0x0 /* data xfer via kernel buffers (or no xfer) */ +#define SG_INFO_DIRECT_IO 0x2 /* direct IO requested and performed */ +#define SG_INFO_MIXED_IO 0x4 /* not used, always 0 */ +#define SG_INFO_DEVICE_DETACHING 0x8 /* completed successfully but ... */ +#define SG_INFO_ABORTED 0x10 /* this command has been aborted */ +#define SG_INFO_MRQ_FINI 0x20 /* marks multi-reqs that have finished */ + +/* + * Obsolete DRIVER_SENSE driver byte + * + * Originally the SCSI midlayer would set the DRIVER_SENSE driver byte when + * a sense code was generated and a sense buffer was allocated. + * However, as nowadays every scsi command has a sense code allocated this + * distinction became moot as one could check the sense buffer directly. + * Consequently this byte is not set anymore from the midlayer, but SG will + * keep setting this byte to be compatible with previous releases. + */ +#define DRIVER_SENSE 0x08 +/* Obsolete driver_byte() declaration */ +#define driver_byte(result) (((result) >> 24) & 0xff) + +/* + * Original linux SCSI Status codes. They are shifted 1 bit right + * from those found in the SCSI standards. + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 +#define ACA_ACTIVE 0x18 +#define TASK_ABORTED 0x20 + +/* Obsolete status_byte() declaration */ +#define status_byte(result) (((result) >> 1) & 0x7f) + +/* + * Pointer to object of this structure filled by ioctl(SG_GET_SCSI_ID). Last + * field changed in v4 driver, was 'int unused[2]' so remains the same size. + */ +typedef struct sg_scsi_id { + int host_no; /* as in "scsi" where 'n' is one of 0, 1, 2 etc */ + int channel; + int scsi_id; /* scsi id of target device */ + int lun; /* lower 32 bits of internal 64 bit integer */ + int scsi_type; /* TYPE_... defined in scsi/scsi.h */ + short h_cmd_per_lun;/* host (adapter) maximum commands per lun */ + short d_queue_depth;/* device (or adapter) maximum queue length */ + int unused[2]; +} sg_scsi_id_t; + +/* For backward compatibility v4 driver yields at most SG_MAX_QUEUE of these */ +typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ + char req_state; /* See 'enum sg_rq_state' definition in v4 driver */ + char orphan; /* 0 -> normal request, 1 -> from interrupted SG_IO */ + /* sg_io_owned set imples synchronous, clear implies asynchronous */ + char sg_io_owned;/* 0 -> complete with read(), 1 -> owned by SG_IO */ + char problem; /* 0 -> no problem detected, 1 -> error to report */ + /* If SG_CTL_FLAGM_TAG_FOR_PACK_ID set on fd then next field is tag */ + int pack_id; /* pack_id, in v4 driver may be tag instead */ + void __user *usr_ptr; /* user provided pointer in v3+v4 interface */ + unsigned int duration; + int unused; +} sg_req_info_t; + +/* + * IOCTLs: Those ioctls that are relevant to the SG 3.x drivers follow. + * [Those that only apply to the SG 2.x drivers are at the end of the file.] + * (_GET_s yield result via 'int *' 3rd argument unless otherwise indicated) + */ + +#define SG_EMULATED_HOST 0x2203 /* true for emulated host adapter (ATAPI) */ + +/* + * Used to configure SCSI command transformation layer for ATAPI devices. + * Only supported by the ide-scsi driver. 20181014 No longer supported, this + * driver passes them to the mid-level which returns a EINVAL (22) errno. + * + * Original note: N.B. 3rd arg is not pointer but value: 3rd arg = 0 to + * disable transform, 1 to enable it + */ +#define SG_SET_TRANSFORM 0x2204 +#define SG_GET_TRANSFORM 0x2205 + +#define SG_SET_RESERVED_SIZE 0x2275 /* request new reserved buffer size */ +#define SG_GET_RESERVED_SIZE 0x2272 /* actual size of reserved buffer */ + +/* The following ioctl has a 'sg_scsi_id_t *' object as its 3rd argument. */ +#define SG_GET_SCSI_ID 0x2276 /* Yields fd's bus, chan, dev, lun + type */ +/* SCSI id information can also be obtained from SCSI_IOCTL_GET_IDLUN */ + +/* Override host setting and always DMA using low memory ( <16MB on i386) */ +#define SG_SET_FORCE_LOW_DMA 0x2279 /* 0-> use adapter setting, 1-> force */ +#define SG_GET_LOW_DMA 0x227a /* 0-> use all ram for dma; 1-> low dma ram */ + +/* + * When SG_SET_FORCE_PACK_ID set to 1, pack_id (or tag) is input to read() or + * ioctl(SG_IO_RECEIVE). These functions wait until matching packet (request/ + * command) is finished but they will return with EAGAIN quickly if the file + * descriptor was opened O_NONBLOCK or (in v4) if SGV4_FLAG_IMMED is given. + * The tag is used when SG_CTL_FLAGM_TAG_FOR_PACK_ID is set on the parent + * file descriptor (default: use pack_id). If pack_id or tag is -1 then read + * oldest waiting and this is the same action as when FORCE_PACK_ID is + * clear on the parent file descriptor. In the v4 interface the pack_id is + * placed the in sg_io_v4::request_extra field . + */ +#define SG_SET_FORCE_PACK_ID 0x227b /* pack_id or in v4 can be tag */ +#define SG_GET_PACK_ID 0x227c /* Yields oldest readable pack_id/tag, or -1 */ + +#define SG_GET_NUM_WAITING 0x227d /* Number of commands awaiting read() */ + +/* Yields max scatter gather tablesize allowed by current host adapter */ +#define SG_GET_SG_TABLESIZE 0x227F /* 0 implies can't do scatter gather */ + +/* + * Integer form of version number: [x]xyyzz where [x] empty when x=0 . + * String form of version number: "[x]x.[y]y.zz" + */ +#define SG_GET_VERSION_NUM 0x2282 /* Example: version "2.1.34" yields 20134 */ + +/* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */ +#define SG_SCSI_RESET 0x2284 +/* + * Associated values that can be given to SG_SCSI_RESET follow. + * SG_SCSI_RESET_NO_ESCALATE may be OR-ed to the _DEVICE, _TARGET, _BUS + * or _HOST reset value so only that action is attempted. + */ +#define SG_SCSI_RESET_NOTHING 0 +#define SG_SCSI_RESET_DEVICE 1 +#define SG_SCSI_RESET_BUS 2 +#define SG_SCSI_RESET_HOST 3 +#define SG_SCSI_RESET_TARGET 4 +#define SG_SCSI_RESET_NO_ESCALATE 0x100 + +/* synchronous SCSI command ioctl, (for version 3 and 4 interface) */ +#define SG_IO 0x2285 /* similar effect as write() followed by read() */ + +#define SG_GET_REQUEST_TABLE 0x2286 /* yields table of active requests */ + +/* How to treat EINTR during SG_IO ioctl(), only in sg v3 and v4 driver */ +#define SG_SET_KEEP_ORPHAN 0x2287 /* 1 -> hold for read(), 0 -> drop (def) */ +#define SG_GET_KEEP_ORPHAN 0x2288 + +/* + * Yields scsi midlevel's access_count for this SCSI device. 20181014 No + * longer available, always yields 1. + */ +#define SG_GET_ACCESS_COUNT 0x2289 + + +/* + * Default size (in bytes) a single scatter-gather list element can have. + * The value used by the driver is 'max(SG_SCATTER_SZ, PAGE_SIZE)'. This + * value should be a power of 2 (and may be rounded up internally). In the + * v4 driver this can be changed by ioctl(SG_SET_GET_EXTENDED{SGAT_ELEM_SZ}). + */ +#define SG_SCATTER_SZ (8 * 4096) + +/* sg driver users' code should handle retries (e.g. from Unit Attentions) */ +#define SG_DEFAULT_RETRIES 0 + +/* Defaults, commented if they differ from original sg driver */ +#define SG_DEF_FORCE_PACK_ID 0 +#define SG_DEF_KEEP_ORPHAN 0 +#define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ /* load time option */ + +/* + * Maximum outstanding requests (i.e write()s without corresponding read()s) + * yields EDOM from write() if exceeded. This limit only applies prior to + * version 3.9 . It is still used as a maximum number of sg_req_info objects + * that are returned from the SG_GET_REQUEST_TABLE ioctl. + */ +#define SG_MAX_QUEUE 16 + +#define SG_BIG_BUFF SG_DEF_RESERVED_SIZE /* for backward compatibility */ + +/* + * Alternate style type names, "..._t" variants (as found in the + * 'typedef struct * {};' definitions above) are preferred to these: + */ +typedef struct sg_io_hdr Sg_io_hdr; +typedef struct sg_io_vec Sg_io_vec; +typedef struct sg_scsi_id Sg_scsi_id; +typedef struct sg_req_info Sg_req_info; + + +/* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ +/* The v1+v2 SG interface based on the 'sg_header' structure follows. */ +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ + +#define SG_MAX_SENSE 16 /* this only applies to the sg_header interface */ + +struct sg_header { + int pack_len; /* [o] reply_len (ie useless), ignored as input */ + int reply_len; /* [i] max length of expected reply (inc. sg_header) */ + int pack_id; /* [io] id number of packet (use ints >= 0) */ + int result; /* [o] 0==ok, else (+ve) Unix errno (best ignored) */ + unsigned int twelve_byte:1; + /* [i] Force 12 byte command length for group 6 & 7 commands */ + unsigned int target_status:5; /* [o] scsi status from target */ + unsigned int host_status:8; /* [o] host status (see "DID" codes) */ + unsigned int driver_status:8; /* [o] driver status+suggestion */ + unsigned int other_flags:10; /* unused */ + unsigned char sense_buffer[SG_MAX_SENSE]; + /* + * [o] Output in 3 cases: + * when target_status is CHECK_CONDITION or + * when target_status is COMMAND_TERMINATED or + * when (driver_status & DRIVER_SENSE) is true. + */ +}; + +/* + * IOCTLs: The following are not required (or ignored) when the v3 or v4 + * interface is used as those structures contain a timeout field. These + * ioctls are kept for backward compatibility with v1+v2 interfaces. + */ + +#define SG_SET_TIMEOUT 0x2201 /* unit: (user space) jiffies */ +#define SG_GET_TIMEOUT 0x2202 /* yield timeout as _return_ value */ + +/* + * Get/set command queuing state per fd (default is SG_DEF_COMMAND_Q. + * Each time a sg_io_hdr_t object is seen on this file descriptor, this + * command queuing flag is set on (overriding the previous setting). + * This setting defaults to 0 (i.e. no queuing) but gets set the first + * time that fd sees a v3 or v4 interface request. + */ +#define SG_GET_COMMAND_Q 0x2270 /* Yields 0 (queuing off) or 1 (on) */ +#define SG_SET_COMMAND_Q 0x2271 /* Change queuing state with 0 or 1 */ + +/* + * Turn on/off error sense trace (1 and 0 respectively, default is off). + * Try using: "# cat /proc/scsi/sg/debug" instead in the v3 driver + */ +#define SG_SET_DEBUG 0x227e /* 0 -> turn off debug */ + +/* + * override SCSI command length with given number on the next write() on + * this file descriptor (v1 and v2 interface only) + */ +#define SG_NEXT_CMD_LEN 0x2283 + +/* command queuing is always on when the v3 or v4 interface is used */ +#define SG_DEF_COMMAND_Q 0 + +#define SG_DEF_UNDERRUN_FLAG 0 + +/* If the timeout value in the v3_v4 interfaces is 0, this value is used */ +#define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */ + +#endif /* end of _UAPI_SCSI_SG_H guard */ From patchwork Mon Oct 24 03:20:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016539 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78729C433FE for ; Mon, 24 Oct 2022 03:23:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230207AbiJXDX3 (ORCPT ); Sun, 23 Oct 2022 23:23:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55802 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230332AbiJXDWl (ORCPT ); Sun, 23 Oct 2022 23:22:41 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 54A7052E7A for ; Sun, 23 Oct 2022 20:21:15 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id CB73F2041CB; Mon, 24 Oct 2022 05:21:14 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Bfw7lGMkFVC0; Mon, 24 Oct 2022 05:21:14 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 806832041AF; Mon, 24 Oct 2022 05:21:13 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 08/44] sg: speed sg_poll and sg_get_num_waiting Date: Sun, 23 Oct 2022 23:20:22 -0400 Message-Id: <20221024032058.14077-9-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Track the number of submitted and waiting (for read/receive) requests on each file descriptor with two atomic integers. This speeds sg_poll() and ioctl(SG_GET_NUM_WAITING) which are oft used with the asynchronous (non-blocking) interfaces. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 1c419bf9b435..15841880f496 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -147,6 +147,8 @@ struct sg_fd { /* holds the state of a file descriptor */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + atomic_t submitted; /* number inflight or awaiting read */ + atomic_t waiting; /* number of requests awaiting read */ struct sg_scatter_hold reserve; /* buffer for this file descriptor */ struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */ @@ -685,6 +687,8 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, else at_head = 1; + if (!blocking) + atomic_inc(&sfp->submitted); srp->rq->timeout = timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ srp->rq->end_io = sg_rq_end_io; @@ -1062,14 +1066,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, read_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(-1, ip); case SG_GET_NUM_WAITING: - read_lock_irqsave(&sfp->rq_list_lock, iflags); - val = 0; - list_for_each_entry(srp, &sfp->rq_list, entry) { - if ((1 == srp->done) && (!srp->sg_io_owned)) - ++val; - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return put_user(val, ip); + return put_user(atomic_read(&sfp->waiting), ip); case SG_GET_SG_TABLESIZE: return put_user(sdp->max_sgat_elems, ip); case SG_SET_RESERVED_SIZE: @@ -1217,35 +1214,26 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return scsi_ioctl(sdp->device, filp->f_mode, cmd_in, p); } +/* + * Implements the poll(2) system call for this driver. Returns various EPOLL* + * flags OR-ed together. + */ static __poll_t sg_poll(struct file *filp, poll_table * wait) { __poll_t p_res = 0; struct sg_fd *sfp = filp->private_data; - struct sg_request *srp; - int count = 0; - unsigned long iflags; - if (!sfp) - return EPOLLERR; poll_wait(filp, &sfp->read_wait, wait); - read_lock_irqsave(&sfp->rq_list_lock, iflags); - list_for_each_entry(srp, &sfp->rq_list, entry) { - /* if any read waiting, flag it */ - if (p_res == 0 && srp->done == 1 && !srp->sg_io_owned) - p_res = EPOLLIN | EPOLLRDNORM; - ++count; - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + if (atomic_read(&sfp->waiting) > 0) + p_res = EPOLLIN | EPOLLRDNORM; - if (sfp->parentdp && SG_IS_DETACHING(sfp->parentdp)) { + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) p_res |= EPOLLHUP; - } else if (!sfp->cmd_q) { - if (count == 0) - p_res |= EPOLLOUT | EPOLLWRNORM; - } else if (count < SG_MAX_QUEUE) { + else if (likely(sfp->cmd_q)) + p_res |= EPOLLOUT | EPOLLWRNORM; + else if (atomic_read(&sfp->submitted) == 0) p_res |= EPOLLOUT | EPOLLWRNORM; - } SG_LOG(3, sfp, "%s: p_res=0x%x\n", __func__, (__force u32)p_res); return p_res; } @@ -1430,6 +1418,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) /* Rely on write phase to clean out srp status values, so no "else" */ + if (!srp->sg_io_owned) + atomic_inc(&sfp->waiting); /* * Free the request as soon as it is complete so that its resources * can be reused without waiting for userspace to read() the @@ -1873,6 +1863,10 @@ sg_finish_rem_req(struct sg_request *srp) SG_LOG(4, sfp, "%s: srp=0x%p%s\n", __func__, srp, (srp->res_used) ? " rsv" : ""); + if (!srp->sg_io_owned) { + atomic_dec(&sfp->submitted); + atomic_dec(&sfp->waiting); + } if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -2201,6 +2195,9 @@ sg_add_sfp(struct sg_device *sdp) sfp->cmd_q = SG_DEF_COMMAND_Q; sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; sfp->parentdp = sdp; + atomic_set(&sfp->submitted, 0); + atomic_set(&sfp->waiting, 0); + write_lock_irqsave(&sdp->sfd_lock, iflags); if (SG_IS_DETACHING(sdp)) { write_unlock_irqrestore(&sdp->sfd_lock, iflags); @@ -2573,6 +2570,9 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", (int) fp->cmd_q, (int) fp->force_packid, (int) fp->keep_orphan); + seq_printf(s, " submitted=%d waiting=%d\n", + atomic_read(&fp->submitted), + atomic_read(&fp->waiting)); list_for_each_entry(srp, &fp->rq_list, entry) { hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; From patchwork Mon Oct 24 03:20:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016541 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8D433C3A59D for ; Mon, 24 Oct 2022 03:23:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230370AbiJXDXt (ORCPT ); Sun, 23 Oct 2022 23:23:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59142 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230346AbiJXDWl (ORCPT ); Sun, 23 Oct 2022 23:22:41 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 00C2453032 for ; Sun, 23 Oct 2022 20:21:16 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 5AED02041BB; Mon, 24 Oct 2022 05:21:16 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cOJ5bWjqQnyJ; Mon, 24 Oct 2022 05:21:16 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 062732041AF; Mon, 24 Oct 2022 05:21:14 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 09/44] sg: sg_allow_if_err_recovery and renames Date: Sun, 23 Oct 2022 23:20:23 -0400 Message-Id: <20221024032058.14077-10-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Add sg_allow_if_err_recover() to do checks common to several entry points. Replace retval with either res or ret. Rename sg_finish_rem_req() to sg_finish_scsi_blk_rq(). Rename sg_new_write() to sg_submit(). Other cleanups triggered by checkpatch.pl . Remove use of typedef sg_io_hdr_t and replace with struct sg_io_hdr. Change some names on driver wide structure fields and comment them. Rename sg_alloc() to sg_add_device_helper() to reflect its current role. Rename sg_add_request() to sg_setup_req() to imply that it precedes sg_start_req(). Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 472 +++++++++++++++++++++++++--------------------- 1 file changed, 254 insertions(+), 218 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 15841880f496..13d9c69494d1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -73,7 +73,7 @@ static char *sg_version_date = "20190606"; /* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater - * than 16 bytes are "variable length" whose length is a multiple of 4 + * than 16 bytes are "variable length" whose length is a multiple of 4, so: */ #define SG_MAX_CDB_SIZE 252 @@ -97,7 +97,7 @@ static int sg_allow_dio = SG_ALLOW_DIO_DEF; static int scatter_elem_sz = SG_SCATTER_SZ; static int scatter_elem_sz_prev = SG_SCATTER_SZ; -#define SG_SECTOR_SZ 512 +#define SG_DEF_SECTOR_SZ 512 static int sg_add_device(struct device *, struct class_interface *); static void sg_remove_device(struct device *, struct class_interface *); @@ -110,12 +110,13 @@ static struct class_interface sg_interface = { .remove_dev = sg_remove_device, }; -struct sg_scatter_hold { /* holding area for scsi scatter gather info */ - u16 k_use_sg; /* Count of kernel scatter-gather pieces */ +struct sg_scatter_hold { /* holding area for scsi scatter gather info */ + struct page **pages; /* num_sgat element array of struct page* */ + int buflen; /* capacity in bytes (dlen<=buflen) */ + int dlen; /* current valid data length of this req */ + u16 page_order; /* byte_len = (page_size*(2**page_order)) */ + u16 num_sgat; /* actual number of scatter-gather segments */ unsigned int sglist_len; /* size of malloc'd scatter-gather list ++ */ - unsigned int bufflen; /* Size of (aggregate) data buffer */ - struct page **pages; - int page_order; char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ u8 cmd_opcode; /* first byte of command */ }; @@ -127,20 +128,20 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct list_head entry; /* list entry */ struct sg_fd *parentfp; /* NULL -> not in use */ struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ - sg_io_hdr_t header; /* scsi command+info, see */ + struct sg_io_hdr header; /* scsi command+info, see */ u8 sense_b[SCSI_SENSE_BUFFERSIZE]; char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ /* done protected by rq_list_lock */ char done; /* 0->before bh, 1->before read, 2->read */ - struct request *rq; - struct bio *bio; - struct execute_work ew; + struct request *rq; /* released in sg_rq_end_io(), bio kept */ + struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ + struct execute_work ew_orph; /* harvest orphan request */ }; struct sg_fd { /* holds the state of a file descriptor */ - struct list_head sfd_siblings; /* protected by device's sfd_lock */ + struct list_head sfd_entry; /* member sg_device::sfds list */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ rwlock_t rq_list_lock; /* protect access to list in req_arr */ @@ -160,7 +161,7 @@ struct sg_fd { /* holds the state of a file descriptor */ char mmap_called; /* 0 -> mmap() never called on this fd */ char res_in_use; /* 1 -> 'reserve' array in use */ struct kref f_ref; - struct execute_work ew; + struct execute_work ew_fd; /* harvest all fd resources and lists */ }; struct sg_device { /* holds the state of each scsi generic device */ @@ -169,7 +170,7 @@ struct sg_device { /* holds the state of each scsi generic device */ struct mutex open_rel_lock; /* held when in open() or release() */ struct list_head sfds; rwlock_t sfd_lock; /* protect access to sfd list */ - int max_sgat_elems; /* adapter's max sgat number of elements */ + int max_sgat_sz; /* max number of bytes in sgat list */ u32 index; /* device index number */ atomic_t open_cnt; /* count of opens (perhaps < num(sfds) ) */ unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ @@ -183,16 +184,16 @@ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); static int sg_start_req(struct sg_request *srp, u8 *cmd); -static int sg_finish_rem_req(struct sg_request *srp); +static int sg_finish_scsi_blk_rq(struct sg_request *srp); static int sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, int buff_size); -static ssize_t sg_new_write(struct sg_fd *sfp, struct file *file, - const char __user *buf, size_t count, int blocking, - int read_only, int sg_io_owned, - struct sg_request **o_srp); +static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, + const char __user *buf, size_t count, bool blocking, + bool read_only, bool sg_io_owned, + struct sg_request **o_srp); static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, u8 *cmnd, int timeout, int blocking); -static int sg_rd_append(struct sg_request *srp, char __user *outp, +static int sg_rd_append(struct sg_request *srp, void __user *outp, int num_xfer); static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); static void sg_build_reserve(struct sg_fd *sfp, int req_size); @@ -203,7 +204,7 @@ static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); static struct sg_request *sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy); -static struct sg_request *sg_add_request(struct sg_fd *sfp); +static struct sg_request *sg_setup_req(struct sg_fd *sfp); static int sg_remove_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); @@ -277,37 +278,60 @@ sg_check_file_access(struct file *filp, const char *caller) static int sg_wait_open_event(struct sg_device *sdp, bool o_excl) { - int retval = 0; + int res = 0; if (o_excl) { while (atomic_read(&sdp->open_cnt) > 0) { mutex_unlock(&sdp->open_rel_lock); - retval = wait_event_interruptible(sdp->open_wait, - (SG_IS_DETACHING(sdp) || - atomic_read(&sdp->open_cnt) == 0)); + res = wait_event_interruptible + (sdp->open_wait, + (SG_IS_DETACHING(sdp) || + atomic_read(&sdp->open_cnt) == 0)); mutex_lock(&sdp->open_rel_lock); - if (retval) /* -ERESTARTSYS */ - return retval; + if (res) /* -ERESTARTSYS */ + return res; if (SG_IS_DETACHING(sdp)) return -ENODEV; } } else { while (SG_HAVE_EXCLUDE(sdp)) { mutex_unlock(&sdp->open_rel_lock); - retval = wait_event_interruptible(sdp->open_wait, - (SG_IS_DETACHING(sdp) || - !SG_HAVE_EXCLUDE(sdp))); + res = wait_event_interruptible + (sdp->open_wait, + (SG_IS_DETACHING(sdp) || + !SG_HAVE_EXCLUDE(sdp))); mutex_lock(&sdp->open_rel_lock); - if (retval) /* -ERESTARTSYS */ - return retval; + if (res) /* -ERESTARTSYS */ + return res; if (SG_IS_DETACHING(sdp)) return -ENODEV; } } - return retval; + return res; +} + +/* + * scsi_block_when_processing_errors() returns 0 when dev was taken offline by + * error recovery, 1 otherwise (i.e. okay). Even if in error recovery, let + * user continue if O_NONBLOCK set. Permits SCSI commands to be issued during + * error recovery. Tread carefully. + * Returns 0 for ok (i.e. allow), -EPROTO if sdp is NULL, otherwise -ENXIO . + */ +static inline int +sg_allow_if_err_recovery(struct sg_device *sdp, bool non_block) +{ + if (!sdp) + return -EPROTO; + if (SG_IS_DETACHING(sdp)) + return -ENODEV; + if (non_block) + return 0; + if (likely(scsi_block_when_processing_errors(sdp->device))) + return 0; + return -ENXIO; } /* @@ -320,16 +344,17 @@ sg_wait_open_event(struct sg_device *sdp, bool o_excl) static int sg_open(struct inode *inode, struct file *filp) { - bool o_excl; + bool o_excl, non_block; int min_dev = iminor(inode); int op_flags = filp->f_flags; + int res; struct request_queue *q; struct sg_device *sdp; struct sg_fd *sfp; - int retval; nonseekable_open(inode, filp); o_excl = !!(op_flags & O_EXCL); + non_block = !!(op_flags & O_NONBLOCK); if (o_excl && ((op_flags & O_ACCMODE) == O_RDONLY)) return -EPERM; /* Can't lock it with read only access */ sdp = sg_get_dev(min_dev); /* increments sdp->d_ref */ @@ -338,20 +363,23 @@ sg_open(struct inode *inode, struct file *filp) /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ - retval = scsi_device_get(sdp->device); - if (retval) + res = scsi_device_get(sdp->device); + if (res) goto sg_put; - retval = scsi_autopm_get_device(sdp->device); - if (retval) + res = scsi_autopm_get_device(sdp->device); + if (res) goto sdp_put; + res = sg_allow_if_err_recovery(sdp, non_block); + if (res) + goto error_out; /* scsi_block_when_processing_errors() may block so bypass * check if O_NONBLOCK. Permits SCSI commands to be issued * during error recovery. Tread carefully. */ if (!((op_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) { - retval = -ENXIO; + res = -ENXIO; /* we are in error recovery for this device */ goto error_out; } @@ -360,18 +388,18 @@ sg_open(struct inode *inode, struct file *filp) if (op_flags & O_NONBLOCK) { if (o_excl) { if (atomic_read(&sdp->open_cnt) > 0) { - retval = -EBUSY; + res = -EBUSY; goto error_mutex_locked; } } else { if (SG_HAVE_EXCLUDE(sdp)) { - retval = -EBUSY; + res = -EBUSY; goto error_mutex_locked; } } } else { - retval = sg_wait_open_event(sdp, o_excl); - if (retval) /* -ERESTARTSYS or -ENODEV */ + res = sg_wait_open_event(sdp, o_excl); + if (res) /* -ERESTARTSYS or -ENODEV */ goto error_mutex_locked; } @@ -382,11 +410,11 @@ sg_open(struct inode *inode, struct file *filp) if (atomic_read(&sdp->open_cnt) < 1) { /* no existing opens */ clear_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm); q = sdp->device->request_queue; - sdp->max_sgat_elems = queue_max_segments(q); + sdp->max_sgat_sz = queue_max_segments(q); } sfp = sg_add_sfp(sdp); /* increments sdp->d_ref */ if (IS_ERR(sfp)) { - retval = PTR_ERR(sfp); + res = PTR_ERR(sfp); goto out_undo; } @@ -398,11 +426,11 @@ sg_open(struct inode *inode, struct file *filp) atomic_read(&sdp->open_cnt), ((op_flags & O_NONBLOCK) ? " O_NONBLOCK" : "")); - retval = 0; + res = 0; sg_put: kref_put(&sdp->d_ref, sg_device_destroy); /* if success, sdp->d_ref is incremented twice, decremented once */ - return retval; + return res; out_undo: if (o_excl) { /* undo if error */ @@ -449,50 +477,58 @@ sg_release(struct inode *inode, struct file *filp) } static ssize_t -sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) +sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) { - int mxsize, cmd_size, k; - int input_size, blocking; + bool blocking = !(filp->f_flags & O_NONBLOCK); + int mxsize, cmd_size, input_size, res; u8 opcode; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - struct sg_header old_hdr; - sg_io_hdr_t *hp; + struct sg_header ov2hdr; + struct sg_io_hdr v3hdr; + struct sg_header *ohp = &ov2hdr; + struct sg_io_hdr *h3p = &v3hdr; u8 cmnd[SG_MAX_CDB_SIZE]; - int retval; - retval = sg_check_file_access(filp, __func__); - if (retval) - return retval; + res = sg_check_file_access(filp, __func__); + if (res) + return res; sfp = filp->private_data; sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: write(3rd arg) count=%d\n", __func__, (int)count); - if (!sdp) - return -ENXIO; - if (SG_IS_DETACHING(sdp)) - return -ENODEV; - if (!((filp->f_flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) - return -ENXIO; + res = sg_allow_if_err_recovery(sdp, !!(filp->f_flags & O_NONBLOCK)); + if (res) + return res; if (count < SZ_SG_HEADER) return -EIO; - if (copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) + if (copy_from_user(ohp, p, SZ_SG_HEADER)) return -EFAULT; - blocking = !(filp->f_flags & O_NONBLOCK); - if (old_hdr.reply_len < 0) - return sg_new_write(sfp, filp, buf, count, - blocking, 0, 0, NULL); + if (ohp->reply_len < 0) { /* assume this is v3 */ + struct sg_io_hdr *reinter_2p = (struct sg_io_hdr *)ohp; + + if (count < SZ_SG_IO_HDR) + return -EIO; + if (reinter_2p->interface_id != 'S') { + pr_info_once("sg: %s: v3 interface only here\n", + __func__); + return -EPERM; + } + return sg_submit(sfp, filp, p, count, + !(filp->f_flags & O_NONBLOCK), false, false, + NULL); + } if (count < (SZ_SG_HEADER + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ - buf += SZ_SG_HEADER; - if (get_user(opcode, buf)) + p += SZ_SG_HEADER; + if (get_user(opcode, p)) return -EFAULT; - if (!(srp = sg_add_request(sfp))) { + srp = sg_setup_req(sfp); + if (!srp) { SG_LOG(1, sfp, "%s: queue full\n", __func__); return -EDOM; } @@ -501,43 +537,44 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) cmd_size = sfp->next_cmd_len; sfp->next_cmd_len = 0; /* reset so only this write() effected */ } else { - cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */ - if ((opcode >= 0xc0) && old_hdr.twelve_byte) + cmd_size = COMMAND_SIZE(opcode); /* old: SCSI command group */ + if (opcode >= 0xc0 && ohp->twelve_byte) cmd_size = 12; } mutex_unlock(&sfp->f_mutex); SG_LOG(4, sfp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, (unsigned int)opcode, cmd_size); input_size = count - cmd_size; - mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; + mxsize = max_t(int, input_size, ohp->reply_len); mxsize -= SZ_SG_HEADER; input_size -= SZ_SG_HEADER; if (input_size < 0) { sg_remove_request(sfp, srp); return -EIO; /* User did not pass enough bytes for this command. */ } - hp = &srp->header; - hp->interface_id = '\0'; /* indicator of old interface tunnelled */ - hp->cmd_len = (u8)cmd_size; - hp->iovec_count = 0; - hp->mx_sb_len = 0; + h3p = &srp->header; + h3p->interface_id = '\0'; /* indicator of old interface tunnelled */ + h3p->cmd_len = (u8)cmd_size; + h3p->iovec_count = 0; + h3p->mx_sb_len = 0; if (input_size > 0) - hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ? + h3p->dxfer_direction = (ohp->reply_len > SZ_SG_HEADER) ? SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; else - hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; - hp->dxfer_len = mxsize; - if ((hp->dxfer_direction == SG_DXFER_TO_DEV) || - (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV)) - hp->dxferp = (char __user *)buf + cmd_size; + h3p->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : + SG_DXFER_NONE; + h3p->dxfer_len = mxsize; + if (h3p->dxfer_direction == SG_DXFER_TO_DEV || + h3p->dxfer_direction == SG_DXFER_TO_FROM_DEV) + h3p->dxferp = (char __user *)p + cmd_size; else - hp->dxferp = NULL; - hp->sbp = NULL; - hp->timeout = old_hdr.reply_len; /* structure abuse ... */ - hp->flags = input_size; /* structure abuse ... */ - hp->pack_id = old_hdr.pack_id; - hp->usr_ptr = NULL; - if (copy_from_user(cmnd, buf, cmd_size)) { + h3p->dxferp = NULL; + h3p->sbp = NULL; + h3p->timeout = ohp->reply_len; /* structure abuse ... */ + h3p->flags = input_size; /* structure abuse ... */ + h3p->pack_id = ohp->pack_id; + h3p->usr_ptr = NULL; + if (copy_from_user(cmnd, p, cmd_size)) { sg_remove_request(sfp, srp); return -EFAULT; } @@ -546,18 +583,18 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) * but is is possible that the app intended SG_DXFER_TO_DEV, because there * is a non-zero input_size, so emit a warning. */ - if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) { + if (h3p->dxfer_direction == SG_DXFER_TO_FROM_DEV) { printk_ratelimited(KERN_WARNING "sg_write: data in/out %d/%d bytes " "for SCSI command 0x%x-- guessing " "data in;\n program %s not setting " "count and/or reply_len properly\n", - old_hdr.reply_len - (int)SZ_SG_HEADER, + ohp->reply_len - (int)SZ_SG_HEADER, input_size, (unsigned int) cmnd[0], current->comm); } - k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); - return (k < 0) ? k : count; + res = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); + return (res < 0) ? res : count; } static int @@ -573,13 +610,13 @@ sg_allow_access(struct file *filp, unsigned char *cmd) } static ssize_t -sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, - size_t count, int blocking, int read_only, int sg_io_owned, - struct sg_request **o_srp) +sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, + size_t count, bool blocking, bool read_only, bool sg_io_owned, + struct sg_request **o_srp) { int k; struct sg_request *srp; - sg_io_hdr_t *hp; + struct sg_io_hdr *hp; u8 cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -588,7 +625,8 @@ sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, return -EINVAL; sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ - if (!(srp = sg_add_request(sfp))) { + srp = sg_setup_req(sfp); + if (!srp) { SG_LOG(1, sfp, "%s: queue full\n", __func__); return -EDOM; } @@ -603,7 +641,7 @@ sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, return -ENOSYS; } if (hp->flags & SG_FLAG_MMAP_IO) { - if (hp->dxfer_len > sfp->reserve.bufflen) { + if (hp->dxfer_len > sfp->reserve.buflen) { sg_remove_request(sfp, srp); return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ } @@ -626,7 +664,7 @@ sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, sg_remove_request(sfp, srp); return -EFAULT; } - if (read_only && sg_allow_access(file, cmnd)) { + if (read_only && sg_allow_access(filp, cmnd)) { sg_remove_request(sfp, srp); return -EPERM; } @@ -644,7 +682,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, { int k, at_head; struct sg_device *sdp = sfp->parentdp; - sg_io_hdr_t *hp = &srp->header; + struct sg_io_hdr *hp = &srp->header; srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ hp->status = 0; @@ -665,7 +703,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, k = sg_start_req(srp, cmnd); if (k) { SG_LOG(1, sfp, "%s: start_req err=%d\n", __func__, k); - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); return k; /* probably out of space --> ENOMEM */ } @@ -675,7 +713,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, srp->rq = NULL; } - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); return -ENODEV; } @@ -733,7 +771,7 @@ static ssize_t sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, struct sg_request *srp) { - sg_io_hdr_t *hp = &srp->header; + struct sg_io_hdr *hp = &srp->header; int err = 0, err2; int len; @@ -766,7 +804,7 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, hp->info |= SG_INFO_CHECK; err = put_sg_io_hdr(hp, buf); err_out: - err2 = sg_finish_rem_req(srp); + err2 = sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); return err ? : err2 ? : count; } @@ -790,18 +828,18 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) struct sg_fd *sfp; struct sg_request *srp; int req_pack_id = -1; + int ret; bool busy; - sg_io_hdr_t *hp; + struct sg_io_hdr *hp; struct sg_header *old_hdr; - int retval; /* * This could cause a response to be stranded. Close the associated * file descriptor to free up any resources being held. */ - retval = sg_check_file_access(filp, __func__); - if (retval) - return retval; + ret = sg_check_file_access(filp, __func__); + if (ret) + return ret; if ((!(sfp = (struct sg_fd *)filp->private_data)) || (!(sdp = sfp->parentdp))) @@ -809,20 +847,20 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) SG_LOG(3, sfp, "%s: read() count=%d\n", __func__, (int)count); if (sfp->force_packid) - retval = get_sg_io_pack_id(&req_pack_id, buf, count); - if (retval) - return retval; + ret = get_sg_io_pack_id(&req_pack_id, buf, count); + if (ret) + return ret; srp = sg_get_rq_mark(sfp, req_pack_id, &busy); if (!srp) { /* now wait on packet to arrive */ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - retval = wait_event_interruptible(sfp->read_wait, + ret = wait_event_interruptible(sfp->read_wait, ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || (!busy && SG_IS_DETACHING(sdp)))); if (!srp) /* signal or detaching */ - return retval ? retval : -ENODEV; + return ret ? ret : -ENODEV; } if (srp->header.interface_id != '\0') return sg_new_read(sfp, buf, count, srp); @@ -878,7 +916,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) /* Now copy the result back to the user buffer. */ if (count >= SZ_SG_HEADER) { if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) { - retval = -EFAULT; + ret = -EFAULT; goto free_old_hdr; } buf += SZ_SG_HEADER; @@ -886,18 +924,18 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) count = old_hdr->reply_len; if (count > SZ_SG_HEADER) { if (sg_rd_append(srp, buf, count - SZ_SG_HEADER)) { - retval = -EFAULT; + ret = -EFAULT; goto free_old_hdr; } } } else count = (old_hdr->result == 0) ? 0 : -EIO; - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); - retval = count; + ret = count; free_old_hdr: kfree(old_hdr); - return retval; + return ret; } static int @@ -985,12 +1023,11 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, switch (cmd_in) { case SG_IO: - if (SG_IS_DETACHING(sdp)) - return -ENODEV; - if (!scsi_block_when_processing_errors(sdp->device)) - return -ENXIO; - result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, - 1, read_only, 1, &srp); + result = sg_allow_if_err_recovery(sdp, false); + if (result) + return result; + result = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only, + true, &srp); if (result < 0) return result; result = wait_event_interruptible(sfp->read_wait, @@ -1068,7 +1105,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, case SG_GET_NUM_WAITING: return put_user(atomic_read(&sfp->waiting), ip); case SG_GET_SG_TABLESIZE: - return put_user(sdp->max_sgat_elems, ip); + return put_user(sdp->max_sgat_sz, ip); case SG_SET_RESERVED_SIZE: result = get_user(val, ip); if (result) @@ -1078,7 +1115,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, val = min_t(int, val, max_sectors_bytes(sdp->device->request_queue)); mutex_lock(&sfp->f_mutex); - if (val != sfp->reserve.bufflen) { + if (val != sfp->reserve.buflen) { if (sfp->mmap_called || sfp->res_in_use) { mutex_unlock(&sfp->f_mutex); @@ -1091,7 +1128,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, mutex_unlock(&sfp->f_mutex); return 0; case SG_GET_RESERVED_SIZE: - val = min_t(int, sfp->reserve.bufflen, + val = min_t(int, sfp->reserve.buflen, max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q: @@ -1187,8 +1224,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, break; } - result = scsi_ioctl_block_when_processing_errors(sdp->device, - cmd_in, filp->f_flags & O_NDELAY); + result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY); if (result) return result; @@ -1268,12 +1304,12 @@ sg_vma_fault(struct vm_fault *vmf) } rsv_schp = &sfp->reserve; offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rsv_schp->bufflen) + if (offset >= rsv_schp->buflen) return VM_FAULT_SIGBUS; sa = vma->vm_start; SG_LOG(3, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, sa, offset); length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { + for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) { len = vma->vm_end - sa; len = (len < length) ? len : length; if (offset < len) { @@ -1317,14 +1353,14 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return -EINVAL; /* want no offset */ rsv_schp = &sfp->reserve; mutex_lock(&sfp->f_mutex); - if (req_sz > rsv_schp->bufflen) { + if (req_sz > rsv_schp->buflen) { ret = -ENOMEM; /* cannot map more than reserved buffer */ goto out; } sa = vma->vm_start; length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { + for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) { len = vma->vm_end - sa; len = (len < length) ? len : length; sa += len; @@ -1342,10 +1378,11 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) static void sg_rq_end_io_usercontext(struct work_struct *work) { - struct sg_request *srp = container_of(work, struct sg_request, ew.work); + struct sg_request *srp = container_of(work, struct sg_request, + ew_orph.work); struct sg_fd *sfp = srp->parentfp; - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); } @@ -1447,8 +1484,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kref_put(&sfp->f_ref, sg_remove_sfp); } else { - INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext); - schedule_work(&srp->ew.work); + INIT_WORK(&srp->ew_orph.work, sg_rq_end_io_usercontext); + schedule_work(&srp->ew_orph.work); } return RQ_END_IO_NONE; } @@ -1472,7 +1509,7 @@ static struct class *sg_sysfs_class; static int sg_sysfs_valid = 0; static struct sg_device * -sg_alloc(struct scsi_device *scsidp) +sg_add_device_helper(struct scsi_device *scsidp) { struct request_queue *q = scsidp->request_queue; struct sg_device *sdp; @@ -1512,7 +1549,7 @@ sg_alloc(struct scsi_device *scsidp) init_waitqueue_head(&sdp->open_wait); clear_bit(SG_FDEV_DETACHING, sdp->fdev_bm); rwlock_init(&sdp->sfd_lock); - sdp->max_sgat_elems = queue_max_segments(q); + sdp->max_sgat_sz = queue_max_segments(q); sdp->index = k; kref_init(&sdp->d_ref); error = 0; @@ -1546,9 +1583,9 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) cdev->owner = THIS_MODULE; cdev->ops = &sg_fops; - sdp = sg_alloc(scsidp); + sdp = sg_add_device_helper(scsidp); if (IS_ERR(sdp)) { - pr_warn("%s: sg_alloc failed\n", __func__); + pr_warn("%s: sg_add_device_helper failed\n", __func__); error = PTR_ERR(sdp); goto out; } @@ -1637,7 +1674,7 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) "%s: 0x%p\n", __func__, sdp)); read_lock_irqsave(&sdp->sfd_lock, iflags); - list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { + list_for_each_entry(sfp, &sdp->sfds, sfd_entry) { wake_up_interruptible_all(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); } @@ -1755,7 +1792,7 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) int res; struct request *rq; struct sg_fd *sfp = srp->parentfp; - sg_io_hdr_t *hp = &srp->header; + struct sg_io_hdr *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; unsigned int iov_count = hp->iovec_count; @@ -1810,13 +1847,13 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) if (md) { mutex_lock(&sfp->f_mutex); - if (dxfer_len <= rsv_schp->bufflen && + if (dxfer_len <= rsv_schp->buflen && !sfp->res_in_use) { sfp->res_in_use = 1; sg_link_reserve(sfp, srp, dxfer_len); } else if (hp->flags & SG_FLAG_MMAP_IO) { res = -EBUSY; /* sfp->res_in_use == 1 */ - if (dxfer_len > rsv_schp->bufflen) + if (dxfer_len > rsv_schp->buflen) res = -ENOMEM; mutex_unlock(&sfp->f_mutex); return res; @@ -1831,7 +1868,7 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) md->pages = req_schp->pages; md->page_order = req_schp->page_order; - md->nr_entries = req_schp->k_use_sg; + md->nr_entries = req_schp->num_sgat; md->offset = 0; md->null_mapped = hp->dxferp ? 0 : 1; if (dxfer_dir == SG_DXFER_TO_FROM_DEV) @@ -1854,7 +1891,7 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) } static int -sg_finish_rem_req(struct sg_request *srp) +sg_finish_scsi_blk_rq(struct sg_request *srp) { int ret = 0; @@ -1885,13 +1922,13 @@ static int sg_build_sgat(struct sg_scatter_hold *schp, const struct sg_fd *sfp, int tablesize) { - int sg_bufflen = tablesize * sizeof(struct page *); + int sg_buflen = tablesize * sizeof(struct page *); gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; - schp->pages = kzalloc(sg_bufflen, gfp_flags); + schp->pages = kzalloc(sg_buflen, gfp_flags); if (!schp->pages) return -ENOMEM; - schp->sglist_len = sg_bufflen; + schp->sglist_len = sg_buflen; return tablesize; /* number of scat_gath elements allocated */ } @@ -1900,7 +1937,7 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, int buff_size) { int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; - int max_sgat_elems = sfp->parentdp->max_sgat_elems; + int max_sgat_sz = sfp->parentdp->max_sgat_sz; int blk_size = buff_size, order; gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; @@ -1908,13 +1945,13 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, return -EFAULT; if (0 == blk_size) ++blk_size; /* don't know why */ - /* round request up to next highest SG_SECTOR_SZ byte boundary */ - blk_size = ALIGN(blk_size, SG_SECTOR_SZ); + /* round request up to next highest SG_DEF_SECTOR_SZ byte boundary */ + blk_size = ALIGN(blk_size, SG_DEF_SECTOR_SZ); SG_LOG(4, sfp, "%s: buff_size=%d, blk_size=%d\n", __func__, buff_size, blk_size); /* N.B. ret_sz carried into this block ... */ - mx_sc_elems = sg_build_sgat(schp, sfp, max_sgat_elems); + mx_sc_elems = sg_build_sgat(schp, sfp, max_sgat_sz); if (mx_sc_elems < 0) return mx_sc_elems; /* most likely -ENOMEM */ @@ -1952,9 +1989,9 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, } /* end of for loop */ schp->page_order = order; - schp->k_use_sg = k; - SG_LOG(5, sfp, "%s: k_use_sg=%d, order=%d\n", __func__, k, order); - schp->bufflen = blk_size; + schp->num_sgat = k; + SG_LOG(5, sfp, "%s: num_sgat=%d, order=%d\n", __func__, k, order); + schp->buflen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; return 0; @@ -1971,12 +2008,12 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) { - SG_LOG(4, sfp, "%s: num_sgat=%d\n", __func__, schp->k_use_sg); + SG_LOG(4, sfp, "%s: num_sgat=%d\n", __func__, schp->num_sgat); if (schp->pages && schp->sglist_len > 0) { if (!schp->dio_in_use) { int k; - for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { + for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { SG_LOG(5, sfp, "%s: pg[%d]=0x%p --\n", __func__, k, schp->pages[k]); __free_pages(schp->pages[k], schp->page_order); @@ -1993,7 +2030,7 @@ sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) * appended to given struct sg_header object. */ static int -sg_rd_append(struct sg_request *srp, char __user *outp, int num_xfer) +sg_rd_append(struct sg_request *srp, void __user *outp, int num_xfer) { struct sg_scatter_hold *schp = &srp->data; int k, num; @@ -2003,7 +2040,7 @@ sg_rd_append(struct sg_request *srp, char __user *outp, int num_xfer) return 0; num = 1 << (PAGE_SHIFT + schp->page_order); - for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { + for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { if (num > num_xfer) { if (copy_to_user(outp, page_address(schp->pages[k]), num_xfer)) @@ -2051,20 +2088,20 @@ sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) rem = size; num = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->k_use_sg; k++) { + for (k = 0; k < rsv_schp->num_sgat; k++) { if (rem <= num) { - req_schp->k_use_sg = k + 1; + req_schp->num_sgat = k + 1; req_schp->sglist_len = rsv_schp->sglist_len; req_schp->pages = rsv_schp->pages; - req_schp->bufflen = size; + req_schp->buflen = size; req_schp->page_order = rsv_schp->page_order; break; } else rem -= num; } - if (k >= rsv_schp->k_use_sg) + if (k >= rsv_schp->num_sgat) SG_LOG(1, sfp, "%s: BAD size\n", __func__); } @@ -2073,10 +2110,10 @@ sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) { struct sg_scatter_hold *req_schp = &srp->data; - SG_LOG(4, srp->parentfp, "%s: req->k_use_sg=%d\n", __func__, - (int)req_schp->k_use_sg); - req_schp->k_use_sg = 0; - req_schp->bufflen = 0; + SG_LOG(4, srp->parentfp, "%s: req->num_sgat=%d\n", __func__, + (int)req_schp->num_sgat); + req_schp->num_sgat = 0; + req_schp->buflen = 0; req_schp->pages = NULL; req_schp->page_order = 0; req_schp->sglist_len = 0; @@ -2116,7 +2153,7 @@ sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) /* always adds to end of list */ static struct sg_request * -sg_add_request(struct sg_fd *sfp) +sg_setup_req(struct sg_fd *sfp) { int k; unsigned long iflags; @@ -2204,7 +2241,7 @@ sg_add_sfp(struct sg_device *sdp) kfree(sfp); return ERR_PTR(-ENODEV); } - list_add_tail(&sfp->sfd_siblings, &sdp->sfds); + list_add_tail(&sfp->sfd_entry, &sdp->sfds); write_unlock_irqrestore(&sdp->sfd_lock, iflags); SG_LOG(3, sfp, "%s: sfp=0x%p\n", __func__, sfp); if (unlikely(sg_big_buff != def_reserved_size)) @@ -2213,8 +2250,8 @@ sg_add_sfp(struct sg_device *sdp) bufflen = min_t(int, sg_big_buff, max_sectors_bytes(sdp->device->request_queue)); sg_build_reserve(sfp, bufflen); - SG_LOG(3, sfp, "%s: bufflen=%d, k_use_sg=%d\n", __func__, - sfp->reserve.bufflen, sfp->reserve.k_use_sg); + SG_LOG(3, sfp, "%s: bufflen=%d, num_sgat=%d\n", __func__, + sfp->reserve.buflen, sfp->reserve.num_sgat); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); @@ -2224,7 +2261,7 @@ sg_add_sfp(struct sg_device *sdp) static void sg_remove_sfp_usercontext(struct work_struct *work) { - struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); + struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); struct sg_device *sdp = sfp->parentdp; struct sg_request *srp; unsigned long iflags; @@ -2233,15 +2270,15 @@ sg_remove_sfp_usercontext(struct work_struct *work) write_lock_irqsave(&sfp->rq_list_lock, iflags); while (!list_empty(&sfp->rq_list)) { srp = list_first_entry(&sfp->rq_list, struct sg_request, entry); - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); list_del(&srp->entry); srp->parentfp = NULL; } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (sfp->reserve.bufflen > 0) { - SG_LOG(6, sfp, "%s: bufflen=%d, k_use_sg=%d\n", __func__, - (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg); + if (sfp->reserve.buflen > 0) { + SG_LOG(6, sfp, "%s: buflen=%d, num_sgat=%d\n", __func__, + (int)sfp->reserve.buflen, (int)sfp->reserve.num_sgat); sg_remove_scat(sfp, &sfp->reserve); } @@ -2261,11 +2298,11 @@ sg_remove_sfp(struct kref *kref) unsigned long iflags; write_lock_irqsave(&sdp->sfd_lock, iflags); - list_del(&sfp->sfd_siblings); + list_del(&sfp->sfd_entry); write_unlock_irqrestore(&sdp->sfd_lock, iflags); - INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); - schedule_work(&sfp->ew.work); + INIT_WORK(&sfp->ew_fd.work, sg_remove_sfp_usercontext); + schedule_work(&sfp->ew_fd.work); } static int @@ -2311,7 +2348,7 @@ sg_get_dev(int dev) #if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* long, almost to end of file */ static int sg_proc_seq_show_int(struct seq_file *s, void *v); -static int sg_proc_single_open_adio(struct inode *inode, struct file *file); +static int sg_proc_single_open_adio(struct inode *inode, struct file *filp); static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off); static const struct proc_ops adio_proc_ops = { @@ -2322,7 +2359,7 @@ static const struct proc_ops adio_proc_ops = { .proc_release = single_release, }; -static int sg_proc_single_open_dressz(struct inode *inode, struct file *file); +static int sg_proc_single_open_dressz(struct inode *inode, struct file *filp); static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off); static const struct proc_ops dressz_proc_ops = { @@ -2372,13 +2409,13 @@ sg_proc_init(void) if (!p) return 1; - proc_create("allow_dio", S_IRUGO | S_IWUSR, p, &adio_proc_ops); - proc_create_seq("debug", S_IRUGO, p, &debug_seq_ops); - proc_create("def_reserved_size", S_IRUGO | S_IWUSR, p, &dressz_proc_ops); - proc_create_single("device_hdr", S_IRUGO, p, sg_proc_seq_show_devhdr); - proc_create_seq("devices", S_IRUGO, p, &dev_seq_ops); - proc_create_seq("device_strs", S_IRUGO, p, &devstrs_seq_ops); - proc_create_single("version", S_IRUGO, p, sg_proc_seq_show_version); + proc_create("allow_dio", 0644, p, &adio_proc_ops); + proc_create_seq("debug", 0444, p, &debug_seq_ops); + proc_create("def_reserved_size", 0644, p, &dressz_proc_ops); + proc_create_single("device_hdr", 0444, p, sg_proc_seq_show_devhdr); + proc_create_seq("devices", 0444, p, &dev_seq_ops); + proc_create_seq("device_strs", 0444, p, &devstrs_seq_ops); + proc_create_single("version", 0444, p, sg_proc_seq_show_version); #endif return 0; } @@ -2403,9 +2440,9 @@ sg_proc_seq_show_int(struct seq_file *s, void *v) } static int -sg_proc_single_open_adio(struct inode *inode, struct file *file) +sg_proc_single_open_adio(struct inode *inode, struct file *filp) { - return single_open(file, sg_proc_seq_show_int, &sg_allow_dio); + return single_open(filp, sg_proc_seq_show_int, &sg_allow_dio); } static ssize_t @@ -2425,9 +2462,9 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer, } static int -sg_proc_single_open_dressz(struct inode *inode, struct file *file) +sg_proc_single_open_dressz(struct inode *inode, struct file *filp) { - return single_open(file, sg_proc_seq_show_int, &sg_big_buff); + return single_open(filp, sg_proc_seq_show_int, &sg_big_buff); } static ssize_t @@ -2489,7 +2526,7 @@ dev_seq_start(struct seq_file *s, loff_t *pos) static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct sg_proc_deviter * it = s->private; + struct sg_proc_deviter *it = s->private; *pos = ++it->index; return (it->index < it->max) ? it : NULL; @@ -2504,7 +2541,7 @@ dev_seq_stop(struct seq_file *s, void *v) static int sg_proc_seq_show_dev(struct seq_file *s, void *v) { - struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; + struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2530,7 +2567,7 @@ sg_proc_seq_show_dev(struct seq_file *s, void *v) static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) { - struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; + struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2554,22 +2591,21 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) int k, new_interface, blen, usg; struct sg_request *srp; struct sg_fd *fp; - const sg_io_hdr_t *hp; + const struct sg_io_hdr *hp; const char * cp; unsigned int ms; k = 0; - list_for_each_entry(fp, &sdp->sfds, sfd_siblings) { + list_for_each_entry(fp, &sdp->sfds, sfd_entry) { k++; read_lock(&fp->rq_list_lock); /* irqs already disabled */ - seq_printf(s, " FD(%d): timeout=%dms bufflen=%d " - "(res)sgat=%d low_dma=%d\n", k, - jiffies_to_msecs(fp->timeout), - fp->reserve.bufflen, - (int) fp->reserve.k_use_sg, 0); - seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", - (int) fp->cmd_q, (int) fp->force_packid, - (int) fp->keep_orphan); + seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d\n", + k, jiffies_to_msecs(fp->timeout), + fp->reserve.buflen, + (int)fp->reserve.num_sgat); + seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d\n", + (int)fp->cmd_q, (int)fp->force_packid, + (int)fp->keep_orphan); seq_printf(s, " submitted=%d waiting=%d\n", atomic_read(&fp->submitted), atomic_read(&fp->waiting)); @@ -2589,8 +2625,8 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) cp = " "; } seq_puts(s, cp); - blen = srp->data.bufflen; - usg = srp->data.k_use_sg; + blen = srp->data.buflen; + usg = srp->data.num_sgat; seq_puts(s, srp->done ? ((1 == srp->done) ? "rcv:" : "fin:") : "act:"); @@ -2617,7 +2653,7 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) static int sg_proc_seq_show_debug(struct seq_file *s, void *v) { - struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; + struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; unsigned long iflags; @@ -2643,8 +2679,8 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) scsidp->lun, scsidp->host->hostt->emulated); } - seq_printf(s, " max_sgat_elems=%d excl=%d open_cnt=%d\n", - sdp->max_sgat_elems, SG_HAVE_EXCLUDE(sdp), + seq_printf(s, " max_sgat_sz=%d excl=%d open_cnt=%d\n", + sdp->max_sgat_sz, SG_HAVE_EXCLUDE(sdp), atomic_read(&sdp->open_cnt)); sg_proc_debug_helper(s, sdp); } From patchwork Mon Oct 24 03:20:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016542 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EF7FEC3A59D for ; Mon, 24 Oct 2022 03:24:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230081AbiJXDYX (ORCPT ); Sun, 23 Oct 2022 23:24:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59258 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230163AbiJXDWo (ORCPT ); Sun, 23 Oct 2022 23:22:44 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CD4E15B041 for ; Sun, 23 Oct 2022 20:21:18 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id A2E052041C0; Mon, 24 Oct 2022 05:21:17 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id j0AXh8KwthU2; Mon, 24 Oct 2022 05:21:17 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 7D68A2041AF; Mon, 24 Oct 2022 05:21:16 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 10/44] sg: change rwlock to spinlock Date: Sun, 23 Oct 2022 23:20:24 -0400 Message-Id: <20221024032058.14077-11-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org A reviewer suggested that the extra overhead associated with a rw lock compared to a spinlock was not worth it for short, oft-used critcal sections. So the rwlock on the request list/array is changed to a spinlock. The head of that list is in the owning sf file descriptor object. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 13d9c69494d1..0665e61d448c 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -144,7 +144,7 @@ struct sg_fd { /* holds the state of a file descriptor */ struct list_head sfd_entry; /* member sg_device::sfds list */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ - rwlock_t rq_list_lock; /* protect access to list in req_arr */ + spinlock_t rq_list_lock; /* protect access to list in req_arr */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ @@ -815,9 +815,9 @@ srp_done(struct sg_fd *sfp, struct sg_request *srp) unsigned long flags; int ret; - read_lock_irqsave(&sfp->rq_list_lock, flags); + spin_lock_irqsave(&sfp->rq_list_lock, flags); ret = srp->done; - read_unlock_irqrestore(&sfp->rq_list_lock, flags); + spin_unlock_irqrestore(&sfp->rq_list_lock, flags); return ret; } @@ -1032,15 +1032,15 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return result; result = wait_event_interruptible(sfp->read_wait, srp_done(sfp, srp)); - write_lock_irq(&sfp->rq_list_lock); + spin_lock_irq(&sfp->rq_list_lock); if (srp->done) { srp->done = 2; - write_unlock_irq(&sfp->rq_list_lock); + spin_unlock_irq(&sfp->rq_list_lock); result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; } srp->orphan = 1; - write_unlock_irq(&sfp->rq_list_lock); + spin_unlock_irq(&sfp->rq_list_lock); return result; /* -ERESTARTSYS because signal hit process */ case SG_SET_TIMEOUT: result = get_user(val, ip); @@ -1092,15 +1092,15 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, sfp->force_packid = val ? 1 : 0; return 0; case SG_GET_PACK_ID: - read_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned)) { - read_unlock_irqrestore(&sfp->rq_list_lock, + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(srp->header.pack_id, ip); } } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(-1, ip); case SG_GET_NUM_WAITING: return put_user(atomic_read(&sfp->waiting), ip); @@ -1169,9 +1169,9 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, GFP_KERNEL); if (!rinfo) return -ENOMEM; - read_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); sg_fill_request_table(sfp, rinfo); - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); #ifdef CONFIG_COMPAT if (in_compat_syscall()) result = put_compat_request_table(p, rinfo); @@ -1466,7 +1466,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) srp->rq = NULL; blk_mq_free_request(rq); - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan) srp->sg_io_owned = 0; @@ -1474,7 +1474,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) done = 0; } srp->done = done; - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (likely(done)) { /* Now wake up any sg_read() that is waiting for this @@ -2129,7 +2129,7 @@ sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) unsigned long iflags; *busy = false; - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(resp, &sfp->rq_list, entry) { /* look for requests that are not SG_IO owned */ if ((!resp->sg_io_owned) && @@ -2140,14 +2140,14 @@ sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) break; case 1: /* request done; response ready to return */ resp->done = 2; /* guard against other readers */ - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return resp; case 2: /* response already being returned */ break; } } } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return NULL; } @@ -2159,7 +2159,7 @@ sg_setup_req(struct sg_fd *sfp) unsigned long iflags; struct sg_request *rp = sfp->req_arr; - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); if (!list_empty(&sfp->rq_list)) { if (!sfp->cmd_q) goto out_unlock; @@ -2175,10 +2175,10 @@ sg_setup_req(struct sg_fd *sfp) rp->parentfp = sfp; rp->header.duration = jiffies_to_msecs(jiffies); list_add_tail(&rp->entry, &sfp->rq_list); - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return rp; out_unlock: - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return NULL; } @@ -2191,13 +2191,13 @@ sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) if (!sfp || !srp || list_empty(&sfp->rq_list)) return res; - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); if (!list_empty(&srp->entry)) { list_del(&srp->entry); srp->parentfp = NULL; res = 1; } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); /* * If the device is detaching, wakeup any readers in case we just @@ -2222,7 +2222,7 @@ sg_add_sfp(struct sg_device *sdp) return ERR_PTR(-ENOMEM); init_waitqueue_head(&sfp->read_wait); - rwlock_init(&sfp->rq_list_lock); + spin_lock_init(&sfp->rq_list_lock); INIT_LIST_HEAD(&sfp->rq_list); kref_init(&sfp->f_ref); mutex_init(&sfp->f_mutex); @@ -2267,14 +2267,14 @@ sg_remove_sfp_usercontext(struct work_struct *work) unsigned long iflags; /* Cleanup any responses which were never read(). */ - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&sfp->rq_list_lock, iflags); while (!list_empty(&sfp->rq_list)) { srp = list_first_entry(&sfp->rq_list, struct sg_request, entry); sg_finish_scsi_blk_rq(srp); list_del(&srp->entry); srp->parentfp = NULL; } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (sfp->reserve.buflen > 0) { SG_LOG(6, sfp, "%s: buflen=%d, num_sgat=%d\n", __func__, @@ -2556,9 +2556,9 @@ sg_proc_seq_show_dev(struct seq_file *s, void *v) scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, (int) scsidp->type, 1, - (int) scsidp->queue_depth, - (int) scsi_device_busy(scsidp), - (int) scsi_device_online(scsidp)); + (int)scsidp->queue_depth, + (int)scsi_device_busy(scsidp), + (int)scsi_device_online(scsidp)); } read_unlock_irqrestore(&sg_index_lock, iflags); return 0; @@ -2598,7 +2598,7 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) k = 0; list_for_each_entry(fp, &sdp->sfds, sfd_entry) { k++; - read_lock(&fp->rq_list_lock); /* irqs already disabled */ + spin_lock(&fp->rq_list_lock); /* irqs already disabled */ seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d\n", k, jiffies_to_msecs(fp->timeout), fp->reserve.buflen, @@ -2646,7 +2646,7 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) } if (list_empty(&fp->rq_list)) seq_puts(s, " No requests active\n"); - read_unlock(&fp->rq_list_lock); + spin_unlock(&fp->rq_list_lock); } } From patchwork Mon Oct 24 03:20:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016543 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0AD5AC433FE for ; Mon, 24 Oct 2022 03:24:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230168AbiJXDYZ (ORCPT ); Sun, 23 Oct 2022 23:24:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59256 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230224AbiJXDWp (ORCPT ); Sun, 23 Oct 2022 23:22:45 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CE0255B044 for ; Sun, 23 Oct 2022 20:21:19 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 4E4DD2041CE; Mon, 24 Oct 2022 05:21:19 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id foa4+XzGVufu; Mon, 24 Oct 2022 05:21:19 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id D0A472041AF; Mon, 24 Oct 2022 05:21:17 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, kernel test robot , Dan Carpenter Subject: [PATCH v25 11/44] sg: ioctl handling Date: Sun, 23 Oct 2022 23:20:25 -0400 Message-Id: <20221024032058.14077-12-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Shorten sg_ioctl() by adding some helper functions. sg_ioctl() is the main entry point for ioctls used on this driver's devices. Treat short copy to user space in sg_ctl_req_tbl() as -EFAULT after report from test robot. This makes it consistent with handling of all other copy_to_user/copy_from_user functions in the driver. Reported-by: kernel test robot Reported-by: Dan Carpenter Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 321 ++++++++++++++++++++++++++++------------------ 1 file changed, 195 insertions(+), 126 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 0665e61d448c..7ae7f8d60bca 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -981,6 +981,54 @@ sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) } } +/* + * Handles ioctl(SG_IO) for blocking (sync) usage of v3 or v4 interface. + * Returns 0 on success else a negated errno. + */ +static int +sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, + void __user *p) +{ + bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE); + int res; + struct sg_request *srp; + + res = sg_allow_if_err_recovery(sdp, false); + if (res) + return res; + res = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only, + true, &srp); + if (res < 0) + return res; + res = wait_event_interruptible + (sfp->read_wait, srp_done(sfp, srp)); + spin_lock_irq(&sfp->rq_list_lock); + if (srp->done) { + srp->done = 2; + spin_unlock_irq(&sfp->rq_list_lock); + res = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); + return (res < 0) ? res : 0; + } + srp->orphan = 1; + spin_unlock_irq(&sfp->rq_list_lock); + return res; /* -ERESTARTSYS because signal hit process */ +} + +static int +sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) +{ + if (want_rsv_sz != sfp->reserve.buflen) { + if (sfp->mmap_called || + sfp->res_in_use) { + return -EBUSY; + } + + sg_remove_scat(sfp, &sfp->reserve); + sg_build_reserve(sfp, want_rsv_sz); + } + return 0; +} + #ifdef CONFIG_COMPAT struct compat_sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ char req_state; @@ -1008,146 +1056,180 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o, } #endif +static int +sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p) +{ + int result; + unsigned long iflags; + sg_req_info_t *rinfo; + + rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, + GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + spin_lock_irqsave(&sfp->rq_list_lock, iflags); + sg_fill_request_table(sfp, rinfo); + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) + result = put_compat_request_table(p, rinfo); + else + result = copy_to_user(p, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); +#else + result = copy_to_user(p, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); +#endif + kfree(rinfo); + return result > 0 ? -EFAULT : result; /* treat short copy as error */ +} + +static int +sg_ctl_scsi_id(struct scsi_device *sdev, struct sg_fd *sfp, void __user *p) +{ + struct sg_scsi_id ss_id; + + SG_LOG(3, sfp, "%s: SG_GET_SCSI_ID\n", __func__); + ss_id.host_no = sdev->host->host_no; + ss_id.channel = sdev->channel; + ss_id.scsi_id = sdev->id; + ss_id.lun = sdev->lun; + ss_id.scsi_type = sdev->type; + ss_id.h_cmd_per_lun = sdev->host->cmd_per_lun; + ss_id.d_queue_depth = sdev->queue_depth; + ss_id.unused[0] = 0; + ss_id.unused[1] = 0; + if (copy_to_user(p, &ss_id, sizeof(struct sg_scsi_id))) + return -EFAULT; + return 0; +} + static long sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, unsigned int cmd_in, void __user *p) { + bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE); + int val; + int result = 0; int __user *ip = p; - int result, val, read_only; struct sg_request *srp; + struct scsi_device *sdev; unsigned long iflags; + __maybe_unused const char *pmlp = ", pass to mid-level"; SG_LOG(6, sfp, "%s: cmd=0x%x, O_NONBLOCK=%d\n", __func__, cmd_in, !!(filp->f_flags & O_NONBLOCK)); - read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); + if (unlikely(SG_IS_DETACHING(sdp))) + return -ENODEV; + sdev = sdp->device; switch (cmd_in) { case SG_IO: - result = sg_allow_if_err_recovery(sdp, false); - if (result) - return result; - result = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only, - true, &srp); - if (result < 0) - return result; - result = wait_event_interruptible(sfp->read_wait, - srp_done(sfp, srp)); - spin_lock_irq(&sfp->rq_list_lock); - if (srp->done) { - srp->done = 2; - spin_unlock_irq(&sfp->rq_list_lock); - result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); - return (result < 0) ? result : 0; - } - srp->orphan = 1; - spin_unlock_irq(&sfp->rq_list_lock); - return result; /* -ERESTARTSYS because signal hit process */ - case SG_SET_TIMEOUT: - result = get_user(val, ip); - if (result) - return result; - if (val < 0) - return -EIO; - if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ)) - val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ), - INT_MAX); - sfp->timeout_user = val; - sfp->timeout = mult_frac(val, HZ, USER_HZ); - - return 0; - case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ - /* strange ..., for backward compatibility */ - return sfp->timeout_user; - case SG_SET_FORCE_LOW_DMA: - /* - * N.B. This ioctl never worked properly, but failed to - * return an error value. So returning '0' to keep compability - * with legacy applications. - */ - return 0; - case SG_GET_LOW_DMA: - return put_user(0, ip); + return sg_ctl_sg_io(filp, sdp, sfp, p); case SG_GET_SCSI_ID: - { - sg_scsi_id_t v; - - if (SG_IS_DETACHING(sdp)) - return -ENODEV; - memset(&v, 0, sizeof(v)); - v.host_no = sdp->device->host->host_no; - v.channel = sdp->device->channel; - v.scsi_id = sdp->device->id; - v.lun = sdp->device->lun; - v.scsi_type = sdp->device->type; - v.h_cmd_per_lun = sdp->device->host->cmd_per_lun; - v.d_queue_depth = sdp->device->queue_depth; - if (copy_to_user(p, &v, sizeof(sg_scsi_id_t))) - return -EFAULT; - return 0; - } + return sg_ctl_scsi_id(sdev, sfp, p); case SG_SET_FORCE_PACK_ID: + SG_LOG(3, sfp, "%s: SG_SET_FORCE_PACK_ID\n", __func__); result = get_user(val, ip); if (result) return result; sfp->force_packid = val ? 1 : 0; return 0; case SG_GET_PACK_ID: + val = -1; spin_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned)) { - spin_unlock_irqrestore(&sfp->rq_list_lock, - iflags); - return put_user(srp->header.pack_id, ip); + val = srp->header.pack_id; + break; } } spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return put_user(-1, ip); + SG_LOG(3, sfp, "%s: SG_GET_PACK_ID=%d\n", __func__, val); + return put_user(val, ip); case SG_GET_NUM_WAITING: return put_user(atomic_read(&sfp->waiting), ip); case SG_GET_SG_TABLESIZE: + SG_LOG(3, sfp, "%s: SG_GET_SG_TABLESIZE=%d\n", __func__, + sdp->max_sgat_sz); return put_user(sdp->max_sgat_sz, ip); case SG_SET_RESERVED_SIZE: - result = get_user(val, ip); - if (result) - return result; - if (val < 0) - return -EINVAL; - val = min_t(int, val, - max_sectors_bytes(sdp->device->request_queue)); mutex_lock(&sfp->f_mutex); - if (val != sfp->reserve.buflen) { - if (sfp->mmap_called || - sfp->res_in_use) { - mutex_unlock(&sfp->f_mutex); - return -EBUSY; + result = get_user(val, ip); + if (!result) { + if (val >= 0 && val <= (1024 * 1024 * 1024)) { + result = sg_set_reserved_sz(sfp, val); + } else { + SG_LOG(3, sfp, "%s: invalid size\n", __func__); + result = -EINVAL; } - - sg_remove_scat(sfp, &sfp->reserve); - sg_build_reserve(sfp, val); } mutex_unlock(&sfp->f_mutex); - return 0; + return result; case SG_GET_RESERVED_SIZE: val = min_t(int, sfp->reserve.buflen, - max_sectors_bytes(sdp->device->request_queue)); + max_sectors_bytes(sdev->request_queue)); + SG_LOG(3, sfp, "%s: SG_GET_RESERVED_SIZE=%d\n", + __func__, val); return put_user(val, ip); case SG_SET_COMMAND_Q: + SG_LOG(3, sfp, "%s: SG_SET_COMMAND_Q\n", __func__); result = get_user(val, ip); if (result) return result; sfp->cmd_q = val ? 1 : 0; return 0; case SG_GET_COMMAND_Q: + SG_LOG(3, sfp, "%s: SG_GET_COMMAND_Q\n", __func__); return put_user((int) sfp->cmd_q, ip); case SG_SET_KEEP_ORPHAN: + SG_LOG(3, sfp, "%s: SG_SET_KEEP_ORPHAN\n", __func__); result = get_user(val, ip); if (result) return result; sfp->keep_orphan = val; return 0; case SG_GET_KEEP_ORPHAN: + SG_LOG(3, sfp, "%s: SG_GET_KEEP_ORPHAN\n", __func__); return put_user((int) sfp->keep_orphan, ip); + case SG_GET_VERSION_NUM: + SG_LOG(3, sfp, "%s: SG_GET_VERSION_NUM\n", __func__); + return put_user(sg_version_num, ip); + case SG_GET_REQUEST_TABLE: + return sg_ctl_req_tbl(sfp, p); + case SG_SCSI_RESET: + SG_LOG(3, sfp, "%s: SG_SCSI_RESET\n", __func__); + break; + case SG_SET_TIMEOUT: + SG_LOG(3, sfp, "%s: SG_SET_TIMEOUT\n", __func__); + result = get_user(val, ip); + if (result) + return result; + if (val < 0) + return -EIO; + if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ)) + val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ), + INT_MAX); + sfp->timeout_user = val; + sfp->timeout = mult_frac(val, HZ, USER_HZ); + return 0; + case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ + /* strange ..., for backward compatibility */ + SG_LOG(3, sfp, "%s: SG_GET_TIMEOUT\n", __func__); + return sfp->timeout_user; + case SG_SET_FORCE_LOW_DMA: + /* + * N.B. This ioctl never worked properly, but failed to + * return an error value. So returning '0' to keep + * compatibility with legacy applications. + */ + SG_LOG(3, sfp, "%s: SG_SET_FORCE_LOW_DMA\n", __func__); + return 0; + case SG_GET_LOW_DMA: + SG_LOG(3, sfp, "%s: SG_GET_LOW_DMA\n", __func__); + return put_user(0, ip); case SG_NEXT_CMD_LEN: + SG_LOG(3, sfp, "%s: SG_NEXT_CMD_LEN\n", __func__); result = get_user(val, ip); if (result) return result; @@ -1155,79 +1237,66 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return -ENOMEM; sfp->next_cmd_len = (val > 0) ? val : 0; return 0; - case SG_GET_VERSION_NUM: - return put_user(sg_version_num, ip); case SG_GET_ACCESS_COUNT: + SG_LOG(3, sfp, "%s: SG_GET_ACCESS_COUNT\n", __func__); /* faked - we don't have a real access count anymore */ - val = (sdp->device ? 1 : 0); + val = (sdev ? 1 : 0); return put_user(val, ip); - case SG_GET_REQUEST_TABLE: - { - sg_req_info_t *rinfo; - - rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, - GFP_KERNEL); - if (!rinfo) - return -ENOMEM; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - sg_fill_request_table(sfp, rinfo); - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - #ifdef CONFIG_COMPAT - if (in_compat_syscall()) - result = put_compat_request_table(p, rinfo); - else - #endif - result = copy_to_user(p, rinfo, - SZ_SG_REQ_INFO * SG_MAX_QUEUE); - result = result ? -EFAULT : 0; - kfree(rinfo); - return result; - } case SG_EMULATED_HOST: - if (SG_IS_DETACHING(sdp)) - return -ENODEV; - return put_user(sdp->device->host->hostt->emulated, ip); + SG_LOG(3, sfp, "%s: SG_EMULATED_HOST\n", __func__); + return put_user(sdev->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: - if (SG_IS_DETACHING(sdp)) - return -ENODEV; + SG_LOG(3, sfp, "%s: SCSI_IOCTL_SEND_COMMAND\n", __func__); return scsi_ioctl(sdp->device, filp->f_mode, cmd_in, p); case SG_SET_DEBUG: + SG_LOG(3, sfp, "%s: SG_SET_DEBUG\n", __func__); result = get_user(val, ip); if (result) return result; assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, val); return 0; case BLKSECTGET: - return put_user(max_sectors_bytes(sdp->device->request_queue), - ip); + SG_LOG(3, sfp, "%s: BLKSECTGET\n", __func__); + return put_user(max_sectors_bytes(sdev->request_queue), ip); case BLKTRACESETUP: - return blk_trace_setup(sdp->device->request_queue, sdp->name, + SG_LOG(3, sfp, "%s: BLKTRACESETUP\n", __func__); + return blk_trace_setup(sdev->request_queue, sdp->name, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), NULL, p); case BLKTRACESTART: - return blk_trace_startstop(sdp->device->request_queue, 1); + SG_LOG(3, sfp, "%s: BLKTRACESTART\n", __func__); + return blk_trace_startstop(sdev->request_queue, 1); case BLKTRACESTOP: - return blk_trace_startstop(sdp->device->request_queue, 0); + SG_LOG(3, sfp, "%s: BLKTRACESTOP\n", __func__); + return blk_trace_startstop(sdev->request_queue, 0); case BLKTRACETEARDOWN: - return blk_trace_remove(sdp->device->request_queue); + SG_LOG(3, sfp, "%s: BLKTRACETEARDOWN\n", __func__); + return blk_trace_remove(sdev->request_queue); case SCSI_IOCTL_GET_IDLUN: + SG_LOG(3, sfp, "%s: SCSI_IOCTL_GET_IDLUN %s\n", __func__, + pmlp); + break; case SCSI_IOCTL_GET_BUS_NUMBER: + SG_LOG(3, sfp, "%s: SCSI_IOCTL_GET_BUS_NUMBER%s\n", + __func__, pmlp); + break; case SCSI_IOCTL_PROBE_HOST: + SG_LOG(3, sfp, "%s: SCSI_IOCTL_PROBE_HOST%s", + __func__, pmlp); + break; case SG_GET_TRANSFORM: - case SG_SCSI_RESET: - if (SG_IS_DETACHING(sdp)) - return -ENODEV; + SG_LOG(3, sfp, "%s: SG_GET_TRANSFORM%s\n", __func__, pmlp); break; default: + SG_LOG(3, sfp, "%s: unrecognized ioctl [0x%x]%s\n", + __func__, cmd_in, pmlp); if (read_only) - return -EPERM; /* don't know so take safe approach */ + return -EPERM; /* don't know, so take safer approach */ break; } - result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY); if (result) return result; - return -ENOIOCTLCMD; } From patchwork Mon Oct 24 03:20:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016550 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 08582C433FE for ; Mon, 24 Oct 2022 03:25:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229952AbiJXDZS (ORCPT ); Sun, 23 Oct 2022 23:25:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34484 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230074AbiJXDW6 (ORCPT ); Sun, 23 Oct 2022 23:22:58 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C992C5C95A for ; Sun, 23 Oct 2022 20:21:21 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 9E4782041CF; Mon, 24 Oct 2022 05:21:20 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ZD6-FFhYs6zI; Mon, 24 Oct 2022 05:21:20 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 78AA72041AF; Mon, 24 Oct 2022 05:21:19 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 12/44] sg: split sg_read Date: Sun, 23 Oct 2022 23:20:26 -0400 Message-Id: <20221024032058.14077-13-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org As sg_read() is getting quite long, split out the v1 and v2 processing into sg_read_v1v2(). Rename sg_new_read() to sg_receive_v3() as the v3 interface is now older than the v4 interface which is being added in a later patch. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 194 ++++++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 94 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 7ae7f8d60bca..da40dd00d5a3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -193,8 +193,8 @@ static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, struct sg_request **o_srp); static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, u8 *cmnd, int timeout, int blocking); -static int sg_rd_append(struct sg_request *srp, void __user *outp, - int num_xfer); +static int sg_read_append(struct sg_request *srp, void __user *outp, + int num_xfer); static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); static void sg_build_reserve(struct sg_fd *sfp, int req_size); static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, @@ -768,8 +768,8 @@ get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) } static ssize_t -sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, - struct sg_request *srp) +sg_receive_v3(struct sg_fd *sfp, char __user *buf, size_t count, + struct sg_request *srp) { struct sg_io_hdr *hp = &srp->header; int err = 0, err2; @@ -810,28 +810,90 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, } static int -srp_done(struct sg_fd *sfp, struct sg_request *srp) +sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, + struct sg_request *srp) { - unsigned long flags; - int ret; + int res = 0; + struct sg_io_hdr *sh3p = &srp->header; + struct sg_header *h2p; + struct sg_header a_v2hdr; + + h2p = &a_v2hdr; + memset(h2p, 0, SZ_SG_HEADER); + h2p->reply_len = (int)sh3p->timeout; + h2p->pack_len = h2p->reply_len; /* old, strange behaviour */ + h2p->pack_id = sh3p->pack_id; + h2p->twelve_byte = (srp->data.cmd_opcode >= 0xc0 && + sh3p->cmd_len == 12); + h2p->target_status = sh3p->masked_status; + h2p->host_status = sh3p->host_status; + h2p->driver_status = sh3p->driver_status; + if ((CHECK_CONDITION & h2p->target_status) || + (srp->sense_b[0] & 0x70) == 0x70) { + h2p->driver_status = DRIVER_SENSE; + memcpy(h2p->sense_buffer, srp->sense_b, + sizeof(h2p->sense_buffer)); + } + switch (h2p->host_status) { + /* + * This following setting of 'result' is for backward compatibility + * and is best ignored by the user who should use target, host and + * driver status. + */ + case DID_OK: + case DID_PASSTHROUGH: + case DID_SOFT_ERROR: + h2p->result = 0; + break; + case DID_NO_CONNECT: + case DID_BUS_BUSY: + case DID_TIME_OUT: + h2p->result = EBUSY; + break; + case DID_BAD_TARGET: + case DID_ABORT: + case DID_PARITY: + case DID_RESET: + case DID_BAD_INTR: + h2p->result = EIO; + break; + case DID_ERROR: + h2p->result = (h2p->target_status == GOOD) ? 0 : EIO; + break; + default: + h2p->result = EIO; + break; + } - spin_lock_irqsave(&sfp->rq_list_lock, flags); - ret = srp->done; - spin_unlock_irqrestore(&sfp->rq_list_lock, flags); - return ret; + /* Now copy the result back to the user buffer. */ + if (count >= SZ_SG_HEADER) { + if (copy_to_user(buf, h2p, SZ_SG_HEADER)) + return -EFAULT; + buf += SZ_SG_HEADER; + if (count > h2p->reply_len) + count = h2p->reply_len; + if (count > SZ_SG_HEADER) { + if (sg_read_append(srp, buf, count - SZ_SG_HEADER)) + return -EFAULT; + } + } else { + res = (h2p->result == 0) ? 0 : -EIO; + } + sg_finish_scsi_blk_rq(srp); + sg_remove_request(sfp, srp); + return res; } static ssize_t -sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) +sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { + bool non_block = !!(filp->f_flags & O_NONBLOCK); + bool busy; + int ret; + int req_pack_id = -1; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - int req_pack_id = -1; - int ret; - bool busy; - struct sg_io_hdr *hp; - struct sg_header *old_hdr; /* * This could cause a response to be stranded. Close the associated @@ -841,9 +903,8 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) if (ret) return ret; - if ((!(sfp = (struct sg_fd *)filp->private_data)) || - (!(sdp = sfp->parentdp))) - return -ENXIO; + sfp = filp->private_data; + sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: read() count=%d\n", __func__, (int)count); if (sfp->force_packid) @@ -853,7 +914,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) srp = sg_get_rq_mark(sfp, req_pack_id, &busy); if (!srp) { /* now wait on packet to arrive */ - if (filp->f_flags & O_NONBLOCK) + if (non_block) return -EAGAIN; ret = wait_event_interruptible(sfp->read_wait, ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || @@ -862,79 +923,24 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) /* signal or detaching */ return ret ? ret : -ENODEV; } - if (srp->header.interface_id != '\0') - return sg_new_read(sfp, buf, count, srp); - - hp = &srp->header; - old_hdr = kzalloc(SZ_SG_HEADER, GFP_KERNEL); - if (!old_hdr) - return -ENOMEM; + if (srp->header.interface_id == '\0') + ret = sg_read_v1v2(buf, (int)count, sfp, srp); + else + ret = sg_receive_v3(sfp, buf, count, srp); + if (ret < 0) + SG_LOG(1, sfp, "%s: negated errno: %d\n", __func__, ret); + return ret < 0 ? ret : (int)count; +} - old_hdr->reply_len = (int) hp->timeout; - old_hdr->pack_len = old_hdr->reply_len; /* old, strange behaviour */ - old_hdr->pack_id = hp->pack_id; - old_hdr->twelve_byte = - ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; - old_hdr->target_status = hp->masked_status; - old_hdr->host_status = hp->host_status; - old_hdr->driver_status = hp->driver_status; - if ((CHECK_CONDITION & hp->masked_status) || - (srp->sense_b[0] & 0x70) == 0x70) { - old_hdr->driver_status = DRIVER_SENSE; - memcpy(old_hdr->sense_buffer, srp->sense_b, - sizeof (old_hdr->sense_buffer)); - } - switch (hp->host_status) { - /* This setup of 'result' is for backward compatibility and is best - ignored by the user who should use target, host + driver status */ - case DID_OK: - case DID_PASSTHROUGH: - case DID_SOFT_ERROR: - old_hdr->result = 0; - break; - case DID_NO_CONNECT: - case DID_BUS_BUSY: - case DID_TIME_OUT: - old_hdr->result = EBUSY; - break; - case DID_BAD_TARGET: - case DID_ABORT: - case DID_PARITY: - case DID_RESET: - case DID_BAD_INTR: - old_hdr->result = EIO; - break; - case DID_ERROR: - old_hdr->result = (srp->sense_b[0] == 0 && - hp->masked_status == GOOD) ? 0 : EIO; - break; - default: - old_hdr->result = EIO; - break; - } +static int +srp_done(struct sg_fd *sfp, struct sg_request *srp) +{ + unsigned long flags; + int ret; - /* Now copy the result back to the user buffer. */ - if (count >= SZ_SG_HEADER) { - if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) { - ret = -EFAULT; - goto free_old_hdr; - } - buf += SZ_SG_HEADER; - if (count > old_hdr->reply_len) - count = old_hdr->reply_len; - if (count > SZ_SG_HEADER) { - if (sg_rd_append(srp, buf, count - SZ_SG_HEADER)) { - ret = -EFAULT; - goto free_old_hdr; - } - } - } else - count = (old_hdr->result == 0) ? 0 : -EIO; - sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); - ret = count; -free_old_hdr: - kfree(old_hdr); + spin_lock_irqsave(&sfp->rq_list_lock, flags); + ret = srp->done; + spin_unlock_irqrestore(&sfp->rq_list_lock, flags); return ret; } @@ -1006,7 +1012,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, if (srp->done) { srp->done = 2; spin_unlock_irq(&sfp->rq_list_lock); - res = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); + res = sg_receive_v3(sfp, p, SZ_SG_IO_HDR, srp); return (res < 0) ? res : 0; } srp->orphan = 1; @@ -2099,7 +2105,7 @@ sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) * appended to given struct sg_header object. */ static int -sg_rd_append(struct sg_request *srp, void __user *outp, int num_xfer) +sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) { struct sg_scatter_hold *schp = &srp->data; int k, num; From patchwork Mon Oct 24 03:20:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016551 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7B59BFA373D for ; Mon, 24 Oct 2022 03:25:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230287AbiJXDZU (ORCPT ); Sun, 23 Oct 2022 23:25:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60006 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230255AbiJXDW6 (ORCPT ); Sun, 23 Oct 2022 23:22:58 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 3656F5DF0B for ; Sun, 23 Oct 2022 20:21:22 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id F34A22041CB; Mon, 24 Oct 2022 05:21:21 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id S+OXlHk1XHqg; Mon, 24 Oct 2022 05:21:21 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id CBE182041AF; Mon, 24 Oct 2022 05:21:20 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 13/44] sg: sg_common_write add structure for arguments Date: Sun, 23 Oct 2022 23:20:27 -0400 Message-Id: <20221024032058.14077-14-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org As the number of arguments to sg_common_write() starts to grow (more in later patches) add a structure to hold most of these arguments. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index da40dd00d5a3..114203f4a292 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -179,6 +179,13 @@ struct sg_device { /* holds the state of each scsi generic device */ struct kref d_ref; }; +struct sg_comm_wr_t { /* arguments to sg_common_write() */ + int timeout; + int blocking; + struct sg_request *srp; + u8 *cmnd; +}; + /* tasklet or soft irq callback */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ @@ -191,8 +198,7 @@ static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, struct sg_request **o_srp); -static int sg_common_write(struct sg_fd *sfp, struct sg_request *srp, - u8 *cmnd, int timeout, int blocking); +static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwp); static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer); static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); @@ -489,6 +495,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) struct sg_io_hdr v3hdr; struct sg_header *ohp = &ov2hdr; struct sg_io_hdr *h3p = &v3hdr; + struct sg_comm_wr_t cwr; u8 cmnd[SG_MAX_CDB_SIZE]; res = sg_check_file_access(filp, __func__); @@ -593,7 +600,11 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) input_size, (unsigned int) cmnd[0], current->comm); } - res = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); + cwr.timeout = sfp->timeout; + cwr.blocking = blocking; + cwr.srp = srp; + cwr.cmnd = cmnd; + res = sg_common_write(sfp, &cwr); return (res < 0) ? res : count; } @@ -617,6 +628,7 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, int k; struct sg_request *srp; struct sg_io_hdr *hp; + struct sg_comm_wr_t cwr; u8 cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -668,23 +680,28 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, sg_remove_request(sfp, srp); return -EPERM; } - k = sg_common_write(sfp, srp, cmnd, timeout, blocking); + cwr.timeout = timeout; + cwr.blocking = blocking; + cwr.srp = srp; + cwr.cmnd = cmnd; + k = sg_common_write(sfp, &cwr); if (k < 0) return k; if (o_srp) - *o_srp = srp; + *o_srp = cwr.srp; return count; } static int -sg_common_write(struct sg_fd *sfp, struct sg_request *srp, - u8 *cmnd, int timeout, int blocking) +sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) { - int k, at_head; + bool at_head; + int k; struct sg_device *sdp = sfp->parentdp; + struct sg_request *srp = cwrp->srp; struct sg_io_hdr *hp = &srp->header; - srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ + srp->data.cmd_opcode = cwrp->cmnd[0]; /* hold opcode of command */ hp->status = 0; hp->masked_status = 0; hp->msg_status = 0; @@ -693,14 +710,14 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, hp->driver_status = 0; hp->resid = 0; SG_LOG(4, sfp, "%s: opcode=0x%02x, cmd_sz=%d\n", __func__, - (int)cmnd[0], hp->cmd_len); + (int)cwrp->cmnd[0], hp->cmd_len); if (hp->dxfer_len >= SZ_256M) { sg_remove_request(sfp, srp); return -EINVAL; } - k = sg_start_req(srp, cmnd); + k = sg_start_req(srp, cwrp->cmnd); if (k) { SG_LOG(1, sfp, "%s: start_req err=%d\n", __func__, k); sg_finish_scsi_blk_rq(srp); @@ -721,13 +738,13 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, hp->duration = jiffies_to_msecs(jiffies); if (hp->interface_id != '\0' && /* v3 (or later) interface */ (SG_FLAG_Q_AT_TAIL & hp->flags)) - at_head = 0; + at_head = false; else - at_head = 1; + at_head = true; - if (!blocking) + if (!srp->sg_io_owned) atomic_inc(&sfp->submitted); - srp->rq->timeout = timeout; + srp->rq->timeout = cwrp->timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ srp->rq->end_io = sg_rq_end_io; blk_execute_rq_nowait(srp->rq, at_head); From patchwork Mon Oct 24 03:20:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016544 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F485C433FE for ; Mon, 24 Oct 2022 03:24:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230188AbiJXDYc (ORCPT ); Sun, 23 Oct 2022 23:24:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54864 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230151AbiJXDWr (ORCPT ); Sun, 23 Oct 2022 23:22:47 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CBC3D5D119 for ; Sun, 23 Oct 2022 20:21:23 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 4E13A2041D7; Mon, 24 Oct 2022 05:21:23 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id z5n69meodmKh; Mon, 24 Oct 2022 05:21:23 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 2B6732041AF; Mon, 24 Oct 2022 05:21:22 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 14/44] sg: rework sg_vma_fault Date: Sun, 23 Oct 2022 23:20:28 -0400 Message-Id: <20221024032058.14077-15-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Simple refactoring of the sg_vma_fault() function. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 114203f4a292..fa46f6450af5 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1375,14 +1375,16 @@ sg_fasync(int fd, struct file *filp, int mode) return fasync_helper(fd, filp, mode, &sfp->async_qp); } +/* Note: the error return: VM_FAULT_SIGBUS causes a "bus error" */ static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { - struct vm_area_struct *vma = vmf->vma; - struct sg_fd *sfp; + int k, length; unsigned long offset, len, sa; + struct vm_area_struct *vma = vmf->vma; struct sg_scatter_hold *rsv_schp; - int k, length; + struct sg_device *sdp; + struct sg_fd *sfp; const char *nbp = "==NULL, bad"; if (!vma) { @@ -1394,20 +1396,31 @@ sg_vma_fault(struct vm_fault *vmf) pr_warn("%s: sfp%s\n", __func__, nbp); goto out_err; } + sdp = sfp->parentdp; + if (sdp && unlikely(SG_IS_DETACHING(sdp))) { + SG_LOG(1, sfp, "%s: device detaching\n", __func__); + goto out_err; + } rsv_schp = &sfp->reserve; offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rsv_schp->buflen) - return VM_FAULT_SIGBUS; + if (offset >= (unsigned int)rsv_schp->buflen) { + SG_LOG(1, sfp, "%s: offset[%lu] >= rsv.buflen\n", __func__, + offset); + goto out_err; + } sa = vma->vm_start; SG_LOG(3, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, sa, offset); length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) { + for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) { len = vma->vm_end - sa; - len = (len < length) ? len : length; + len = min_t(int, len, (int)length); if (offset < len) { - struct page *page = nth_page(rsv_schp->pages[k], - offset >> PAGE_SHIFT); - get_page(page); /* increment page count */ + struct page *page; + struct page *pp; + + pp = rsv_schp->pages[k]; + page = nth_page(pp, offset >> PAGE_SHIFT); + get_page(page); /* increment page count */ vmf->page = page; return 0; /* success */ } From patchwork Mon Oct 24 03:20:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016547 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8E853FA373D for ; Mon, 24 Oct 2022 03:25:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230092AbiJXDZM (ORCPT ); Sun, 23 Oct 2022 23:25:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38330 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230250AbiJXDW4 (ORCPT ); Sun, 23 Oct 2022 23:22:56 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 366285DF0D for ; Sun, 23 Oct 2022 20:21:24 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id A53A62041BD; Mon, 24 Oct 2022 05:21:24 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id AhMtEr596A8k; Mon, 24 Oct 2022 05:21:24 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 7EE6A2041AF; Mon, 24 Oct 2022 05:21:23 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 15/44] sg: rework sg_mmap Date: Sun, 23 Oct 2022 23:20:29 -0400 Message-Id: <20221024032058.14077-16-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Simple rework of the sg_mmap() function. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index fa46f6450af5..8cd73246bdf5 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1435,14 +1435,15 @@ static const struct vm_operations_struct sg_mmap_vm_ops = { .fault = sg_vma_fault, }; +/* Entry point for mmap(2) system call */ static int sg_mmap(struct file *filp, struct vm_area_struct *vma) { - struct sg_fd *sfp; - unsigned long req_sz, len, sa; - struct sg_scatter_hold *rsv_schp; int k, length; int ret = 0; + unsigned long req_sz, len, sa; + struct sg_scatter_hold *rsv_schp; + struct sg_fd *sfp; if (!filp || !vma) return -ENXIO; @@ -1455,19 +1456,23 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) SG_LOG(3, sfp, "%s: vm_start=%p, len=%d\n", __func__, (void *)vma->vm_start, (int)req_sz); if (vma->vm_pgoff) - return -EINVAL; /* want no offset */ - rsv_schp = &sfp->reserve; + return -EINVAL; /* only an offset of 0 accepted */ + /* Check reserve request is inactive and has large enough buffer */ mutex_lock(&sfp->f_mutex); - if (req_sz > rsv_schp->buflen) { - ret = -ENOMEM; /* cannot map more than reserved buffer */ + if (sfp->res_in_use) { + ret = -EBUSY; + goto out; + } + rsv_schp = &sfp->reserve; + if (req_sz > (unsigned long)rsv_schp->buflen) { + ret = -ENOMEM; goto out; } - sa = vma->vm_start; length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; k++) { + for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) { len = vma->vm_end - sa; - len = (len < length) ? len : length; + len = min_t(unsigned long, len, (unsigned long)length); sa += len; } From patchwork Mon Oct 24 03:20:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016546 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8DB36C3A59D for ; Mon, 24 Oct 2022 03:25:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230435AbiJXDZE (ORCPT ); Sun, 23 Oct 2022 23:25:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38328 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229615AbiJXDW4 (ORCPT ); Sun, 23 Oct 2022 23:22:56 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 7B1355E336 for ; Sun, 23 Oct 2022 20:21:26 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 058612041BB; Mon, 24 Oct 2022 05:21:26 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id inaN08h4FXWs; Mon, 24 Oct 2022 05:21:25 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id D1FBC2041AF; Mon, 24 Oct 2022 05:21:24 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 16/44] sg: replace sg_allow_access Date: Sun, 23 Oct 2022 23:20:30 -0400 Message-Id: <20221024032058.14077-17-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Replace the sg_allow_access() function with sg_fetch_cmnd() which does a little more. Change sg_finish_scsi_blk_rq() from an int to a void returning function. Rename sg_remove_request() to sg_deact_request(). Other changes, mainly cosmetic. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 150 +++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 8cd73246bdf5..16bafe358186 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -191,7 +191,7 @@ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); static int sg_start_req(struct sg_request *srp, u8 *cmd); -static int sg_finish_scsi_blk_rq(struct sg_request *srp); +static void sg_finish_scsi_blk_rq(struct sg_request *srp); static int sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, int buff_size); static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, @@ -211,7 +211,7 @@ static void sg_remove_sfp(struct kref *); static struct sg_request *sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy); static struct sg_request *sg_setup_req(struct sg_fd *sfp); -static int sg_remove_request(struct sg_fd *sfp, struct sg_request *srp); +static int sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); @@ -542,7 +542,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) mutex_lock(&sfp->f_mutex); if (sfp->next_cmd_len > 0) { cmd_size = sfp->next_cmd_len; - sfp->next_cmd_len = 0; /* reset so only this write() effected */ + sfp->next_cmd_len = 0; /* reset, only this write() effected */ } else { cmd_size = COMMAND_SIZE(opcode); /* old: SCSI command group */ if (opcode >= 0xc0 && ohp->twelve_byte) @@ -556,7 +556,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) mxsize -= SZ_SG_HEADER; input_size -= SZ_SG_HEADER; if (input_size < 0) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EIO; /* User did not pass enough bytes for this command. */ } h3p = &srp->header; @@ -573,7 +573,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) h3p->dxfer_len = mxsize; if (h3p->dxfer_direction == SG_DXFER_TO_DEV || h3p->dxfer_direction == SG_DXFER_TO_FROM_DEV) - h3p->dxferp = (char __user *)p + cmd_size; + h3p->dxferp = (u8 __user *)p + cmd_size; else h3p->dxferp = NULL; h3p->sbp = NULL; @@ -582,7 +582,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) h3p->pack_id = ohp->pack_id; h3p->usr_ptr = NULL; if (copy_from_user(cmnd, p, cmd_size)) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EFAULT; } /* @@ -609,14 +609,24 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) } static int -sg_allow_access(struct file *filp, unsigned char *cmd) +sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, + int len, u8 *cdbp) { - struct sg_fd *sfp = filp->private_data; - - if (sfp->parentdp->device->type == TYPE_SCANNER) - return 0; - if (!scsi_cmd_allowed(cmd, filp->f_mode)) - return -EPERM; + if (!u_cdbp || len < 6 || len > SG_MAX_CDB_SIZE) + return -EMSGSIZE; + if (copy_from_user(cdbp, u_cdbp, len)) + return -EFAULT; + if (O_RDWR != (filp->f_flags & O_ACCMODE)) { /* read-only */ + switch (sfp->parentdp->device->type) { + case TYPE_DISK: + case TYPE_RBC: + case TYPE_ZBC: + if (!scsi_cmd_allowed(cdbp, filp->f_mode)) + return -EPERM; + default: /* SSC, SES, etc cbd_s may differ from SBC */ + break; + } + } return 0; } @@ -625,12 +635,11 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, struct sg_request **o_srp) { - int k; + int k, res, timeout; struct sg_request *srp; struct sg_io_hdr *hp; struct sg_comm_wr_t cwr; u8 cmnd[SG_MAX_CDB_SIZE]; - int timeout; unsigned long ul_timeout; if (count < SZ_SG_IO_HDR) @@ -644,41 +653,35 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, } srp->sg_io_owned = sg_io_owned; hp = &srp->header; + /* get_sg_io_hdr() is defined in block/scsi_ioctl.c */ if (get_sg_io_hdr(hp, buf)) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EFAULT; } if (hp->interface_id != 'S') { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -ENOSYS; } if (hp->flags & SG_FLAG_MMAP_IO) { if (hp->dxfer_len > sfp->reserve.buflen) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ } if (hp->flags & SG_FLAG_DIRECT_IO) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ } if (sfp->res_in_use) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EBUSY; /* reserve buffer already being used */ } } ul_timeout = msecs_to_jiffies(srp->header.timeout); timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX; - if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) { - sg_remove_request(sfp, srp); - return -EMSGSIZE; - } - if (copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) { - sg_remove_request(sfp, srp); - return -EFAULT; - } - if (read_only && sg_allow_access(filp, cmnd)) { - sg_remove_request(sfp, srp); - return -EPERM; + res = sg_fetch_cmnd(filp, sfp, hp->cmdp, hp->cmd_len, cmnd); + if (res) { + sg_deact_request(sfp, srp); + return res; } cwr.timeout = timeout; cwr.blocking = blocking; @@ -713,7 +716,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) (int)cwrp->cmnd[0], hp->cmd_len); if (hp->dxfer_len >= SZ_256M) { - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -EINVAL; } @@ -721,7 +724,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) if (k) { SG_LOG(1, sfp, "%s: start_req err=%d\n", __func__, k); sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return k; /* probably out of space --> ENOMEM */ } if (SG_IS_DETACHING(sdp)) { @@ -731,7 +734,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) } sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return -ENODEV; } @@ -784,12 +787,24 @@ get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) return 0; } +static int +srp_done(struct sg_fd *sfp, struct sg_request *srp) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sfp->rq_list_lock, flags); + ret = srp->done; + spin_unlock_irqrestore(&sfp->rq_list_lock, flags); + return ret; +} + static ssize_t sg_receive_v3(struct sg_fd *sfp, char __user *buf, size_t count, struct sg_request *srp) { struct sg_io_hdr *hp = &srp->header; - int err = 0, err2; + int err = 0; int len; if (in_compat_syscall()) { @@ -821,9 +836,9 @@ sg_receive_v3(struct sg_fd *sfp, char __user *buf, size_t count, hp->info |= SG_INFO_CHECK; err = put_sg_io_hdr(hp, buf); err_out: - err2 = sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); - return err ? : err2 ? : count; + sg_finish_scsi_blk_rq(srp); + sg_deact_request(sfp, srp); + return err; } static int @@ -897,7 +912,7 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, res = (h2p->result == 0) ? 0 : -EIO; } sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); return res; } @@ -949,18 +964,6 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) return ret < 0 ? ret : (int)count; } -static int -srp_done(struct sg_fd *sfp, struct sg_request *srp) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&sfp->rq_list_lock, flags); - ret = srp->done; - spin_unlock_irqrestore(&sfp->rq_list_lock, flags); - return ret; -} - static int max_sectors_bytes(struct request_queue *q) { @@ -1493,7 +1496,7 @@ sg_rq_end_io_usercontext(struct work_struct *work) struct sg_fd *sfp = srp->parentfp; sg_finish_scsi_blk_rq(srp); - sg_remove_request(sfp, srp); + sg_deact_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); } @@ -1616,7 +1619,7 @@ static const struct file_operations sg_fops = { static struct class *sg_sysfs_class; -static int sg_sysfs_valid = 0; +static bool sg_sysfs_valid; static struct sg_device * sg_add_device_helper(struct scsi_device *scsidp) @@ -1869,7 +1872,7 @@ init_sg(void) rc = PTR_ERR(sg_sysfs_class); goto err_out; } - sg_sysfs_valid = 1; + sg_sysfs_valid = true; rc = scsi_register_interface(&sg_interface); if (0 == rc) { sg_proc_init(); @@ -1890,7 +1893,7 @@ exit_sg(void) remove_proc_subtree("scsi/sg", NULL); scsi_unregister_interface(&sg_interface); class_destroy(sg_sysfs_class); - sg_sysfs_valid = 0; + sg_sysfs_valid = false; unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); idr_destroy(&sg_index_idr); @@ -2000,10 +2003,10 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) return res; } -static int +static void sg_finish_scsi_blk_rq(struct sg_request *srp) { - int ret = 0; + int ret; struct sg_fd *sfp = srp->parentfp; struct sg_scatter_hold *req_schp = &srp->data; @@ -2014,8 +2017,13 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) atomic_dec(&sfp->submitted); atomic_dec(&sfp->waiting); } - if (srp->bio) + if (srp->bio) { ret = blk_rq_unmap_user(srp->bio); + if (ret) /* -EINTR (-4) can be ignored */ + SG_LOG(6, sfp, "%s: blk_rq_unmap_user() --> %d\n", + __func__, ret); + srp->bio = NULL; + } if (srp->rq) blk_mq_free_request(srp->rq); @@ -2024,8 +2032,6 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) sg_unlink_reserve(sfp, srp); else sg_remove_scat(sfp, req_schp); - - return ret; } static int @@ -2294,7 +2300,7 @@ sg_setup_req(struct sg_fd *sfp) /* Return of 1 for found; 0 for not found */ static int -sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) +sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) { unsigned long iflags; int res = 0; @@ -2323,9 +2329,9 @@ sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) static struct sg_fd * sg_add_sfp(struct sg_device *sdp) { - struct sg_fd *sfp; unsigned long iflags; int bufflen; + struct sg_fd *sfp; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); if (!sfp) @@ -2371,10 +2377,16 @@ sg_add_sfp(struct sg_device *sdp) static void sg_remove_sfp_usercontext(struct work_struct *work) { + unsigned long iflags; struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); - struct sg_device *sdp = sfp->parentdp; + struct sg_device *sdp; struct sg_request *srp; - unsigned long iflags; + + if (!sfp) { + pr_warn("sg: %s: sfp is NULL\n", __func__); + return; + } + sdp = sfp->parentdp; /* Cleanup any responses which were never read(). */ spin_lock_irqsave(&sfp->rq_list_lock, iflags); @@ -2395,17 +2407,19 @@ sg_remove_sfp_usercontext(struct work_struct *work) SG_LOG(6, sfp, "%s: sfp=0x%p\n", __func__, sfp); kfree(sfp); - scsi_device_put(sdp->device); - kref_put(&sdp->d_ref, sg_device_destroy); + if (sdp) { + scsi_device_put(sdp->device); + kref_put(&sdp->d_ref, sg_device_destroy); + } module_put(THIS_MODULE); } static void sg_remove_sfp(struct kref *kref) { + unsigned long iflags; struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref); struct sg_device *sdp = sfp->parentdp; - unsigned long iflags; write_lock_irqsave(&sdp->sfd_lock, iflags); list_del(&sfp->sfd_entry); @@ -2620,7 +2634,7 @@ struct sg_proc_deviter { static void * dev_seq_start(struct seq_file *s, loff_t *pos) { - struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL); + struct sg_proc_deviter *it = kzalloc(sizeof(*it), GFP_KERNEL); s->private = it; if (! it) From patchwork Mon Oct 24 03:20:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016567 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDCD0C433FE for ; Mon, 24 Oct 2022 03:26:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229562AbiJXD0D (ORCPT ); Sun, 23 Oct 2022 23:26:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55242 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230257AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 68CDA5F7F7 for ; Sun, 23 Oct 2022 20:21:28 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id A99FA2041DC; Mon, 24 Oct 2022 05:21:27 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cxWT8vgRfM7P; Mon, 24 Oct 2022 05:21:27 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 30FAB2041AF; Mon, 24 Oct 2022 05:21:26 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, kernel test robot , Dan Carpenter Subject: [PATCH v25 17/44] sg: rework scatter gather handling Date: Sun, 23 Oct 2022 23:20:31 -0400 Message-Id: <20221024032058.14077-18-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Rename sg_build_indirect() to sg_mk_sgat() and sg_remove_scat() to sg_remove_sgat(). Re-implement those functions. Add sg_calc_sgat_param() to calculate various scatter gather list parameters. Some other minor clean-ups. Earlier versions of this patch made the order and o_order variables in sg_mk_sgat() unsigned int but that breaks 'if (--order >= 0)' as pointed out by test robot. Make those variable signed again. Reported-by: kernel test robot Reported-by: Dan Carpenter Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 290 +++++++++++++++++++++++++--------------------- 1 file changed, 160 insertions(+), 130 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 16bafe358186..68950cdae163 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -95,7 +95,6 @@ static int def_reserved_size = -1; /* picks up init parameter */ static int sg_allow_dio = SG_ALLOW_DIO_DEF; static int scatter_elem_sz = SG_SCATTER_SZ; -static int scatter_elem_sz_prev = SG_SCATTER_SZ; #define SG_DEF_SECTOR_SZ 512 @@ -150,6 +149,7 @@ struct sg_fd { /* holds the state of a file descriptor */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ atomic_t submitted; /* number inflight or awaiting read */ atomic_t waiting; /* number of requests awaiting read */ + int sgat_elem_sz; /* initialized to scatter_elem_sz */ struct sg_scatter_hold reserve; /* buffer for this file descriptor */ struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */ @@ -170,6 +170,7 @@ struct sg_device { /* holds the state of each scsi generic device */ struct mutex open_rel_lock; /* held when in open() or release() */ struct list_head sfds; rwlock_t sfd_lock; /* protect access to sfd list */ + int max_sgat_elems; /* adapter's max number of elements in sgat */ int max_sgat_sz; /* max number of bytes in sgat list */ u32 index; /* device index number */ atomic_t open_cnt; /* count of opens (perhaps < num(sfds) ) */ @@ -192,8 +193,8 @@ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); static int sg_proc_init(void); static int sg_start_req(struct sg_request *srp, u8 *cmd); static void sg_finish_scsi_blk_rq(struct sg_request *srp); -static int sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, - int buff_size); +static int sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, + int minlen); static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, @@ -201,7 +202,7 @@ static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwp); static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer); -static void sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp); +static void sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp); static void sg_build_reserve(struct sg_fd *sfp, int req_size); static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size); @@ -214,6 +215,7 @@ static struct sg_request *sg_setup_req(struct sg_fd *sfp); static int sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); +static void sg_calc_sgat_param(struct sg_device *sdp); #define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ #define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ @@ -354,7 +356,6 @@ sg_open(struct inode *inode, struct file *filp) int min_dev = iminor(inode); int op_flags = filp->f_flags; int res; - struct request_queue *q; struct sg_device *sdp; struct sg_fd *sfp; @@ -413,16 +414,12 @@ sg_open(struct inode *inode, struct file *filp) if (o_excl) set_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm); - if (atomic_read(&sdp->open_cnt) < 1) { /* no existing opens */ - clear_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm); - q = sdp->device->request_queue; - sdp->max_sgat_sz = queue_max_segments(q); - } + if (atomic_read(&sdp->open_cnt) < 1) /* no existing opens */ + sg_calc_sgat_param(sdp); sfp = sg_add_sfp(sdp); /* increments sdp->d_ref */ if (IS_ERR(sfp)) { res = PTR_ERR(sfp); - goto out_undo; - } + goto out_undo; } filp->private_data = sfp; atomic_inc(&sdp->open_cnt); @@ -970,10 +967,43 @@ 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; } +/* + * Calculates sg_device::max_sgat_elems and sg_device::max_sgat_sz. It uses + * the device's request queue. If q not available sets max_sgat_elems to 1 + * and max_sgat_sz to PAGE_SIZE. If potential max_sgat_sz is greater than + * 2^30 scales down the implied max_segment_size so the product of the + * max_segment_size and max_sgat_elems is less than or equal to 2^30 . + */ +static void +sg_calc_sgat_param(struct sg_device *sdp) +{ + int sz; + u64 m; + struct scsi_device *sdev = sdp->device; + struct request_queue *q = sdev ? sdev->request_queue : NULL; + + clear_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm); + if (!q) { + sdp->max_sgat_elems = 1; + sdp->max_sgat_sz = PAGE_SIZE; + return; + } + sdp->max_sgat_elems = queue_max_segments(q); + m = (u64)queue_max_segment_size(q) * queue_max_segments(q); + if (m < PAGE_SIZE) { + sdp->max_sgat_elems = 1; + sdp->max_sgat_sz = PAGE_SIZE; + return; + } + sz = (int)min_t(u64, m, 1 << 30); + if (sz == (1 << 30)) /* round down so: sz = elems * elem_sz */ + sz = ((1 << 30) / sdp->max_sgat_elems) * sdp->max_sgat_elems; + sdp->max_sgat_sz = sz; +} + static void sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) { @@ -1037,7 +1067,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, } srp->orphan = 1; spin_unlock_irq(&sfp->rq_list_lock); - return res; /* -ERESTARTSYS because signal hit process */ + return res; } static int @@ -1048,8 +1078,7 @@ sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) sfp->res_in_use) { return -EBUSY; } - - sg_remove_scat(sfp, &sfp->reserve); + sg_remove_sgat(sfp, &sfp->reserve); sg_build_reserve(sfp, want_rsv_sz); } return 0; @@ -1493,8 +1522,18 @@ sg_rq_end_io_usercontext(struct work_struct *work) { struct sg_request *srp = container_of(work, struct sg_request, ew_orph.work); - struct sg_fd *sfp = srp->parentfp; + struct sg_fd *sfp; + if (!srp) { + WARN_ONCE(1, "%s: srp unexpectedly NULL\n", __func__); + return; + } + sfp = srp->parentfp; + if (!sfp) { + WARN_ONCE(1, "%s: sfp unexpectedly NULL\n", __func__); + return; + } + SG_LOG(3, sfp, "%s: srp=0x%p\n", __func__, srp); sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); @@ -1624,7 +1663,6 @@ static bool sg_sysfs_valid; static struct sg_device * sg_add_device_helper(struct scsi_device *scsidp) { - struct request_queue *q = scsidp->request_queue; struct sg_device *sdp; unsigned long iflags; int error; @@ -1662,7 +1700,7 @@ sg_add_device_helper(struct scsi_device *scsidp) init_waitqueue_head(&sdp->open_wait); clear_bit(SG_FDEV_DETACHING, sdp->fdev_bm); rwlock_init(&sdp->sfd_lock); - sdp->max_sgat_sz = queue_max_segments(q); + sg_calc_sgat_param(sdp); sdp->index = k; kref_init(&sdp->d_ref); error = 0; @@ -1854,24 +1892,24 @@ init_sg(void) { int rc; - if (scatter_elem_sz < PAGE_SIZE) { + if (scatter_elem_sz < (int)PAGE_SIZE) scatter_elem_sz = PAGE_SIZE; - scatter_elem_sz_prev = scatter_elem_sz; - } + else if (!is_power_of_2(scatter_elem_sz)) + scatter_elem_sz = roundup_pow_of_two(scatter_elem_sz); if (def_reserved_size >= 0) sg_big_buff = def_reserved_size; else def_reserved_size = sg_big_buff; - rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), + rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); if (rc) return rc; sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); if ( IS_ERR(sg_sysfs_class) ) { rc = PTR_ERR(sg_sysfs_class); - goto err_out; - } + goto err_out_unreg; + } sg_sysfs_valid = true; rc = scsi_register_interface(&sg_interface); if (0 == rc) { @@ -1880,7 +1918,7 @@ init_sg(void) } class_destroy(sg_sysfs_class); register_sg_sysctls(); -err_out: +err_out_unreg: unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); return rc; } @@ -1971,7 +2009,7 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) mutex_unlock(&sfp->f_mutex); return res; } else { - res = sg_build_indirect(req_schp, sfp, dxfer_len); + res = sg_mk_sgat(req_schp, sfp, dxfer_len); if (res) { mutex_unlock(&sfp->f_mutex); return res; @@ -2031,113 +2069,100 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) if (srp->res_used) sg_unlink_reserve(sfp, srp); else - sg_remove_scat(sfp, req_schp); + sg_remove_sgat(sfp, req_schp); } static int -sg_build_sgat(struct sg_scatter_hold *schp, const struct sg_fd *sfp, - int tablesize) +sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) { - int sg_buflen = tablesize * sizeof(struct page *); - gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; - - schp->pages = kzalloc(sg_buflen, gfp_flags); - if (!schp->pages) - return -ENOMEM; - schp->sglist_len = sg_buflen; - return tablesize; /* number of scat_gath elements allocated */ -} + int j, k, rem_sz, align_sz, order, o_order; + int mx_sgat_elems = sfp->parentdp->max_sgat_elems; + unsigned int elem_sz; + const size_t ptr_sz = sizeof(struct page *); + gfp_t mask_ap = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; + gfp_t mask_kz = GFP_ATOMIC | __GFP_NOWARN; -static int -sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, - int buff_size) -{ - int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; - int max_sgat_sz = sfp->parentdp->max_sgat_sz; - int blk_size = buff_size, order; - gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; - - if (blk_size < 0) - return -EFAULT; - if (0 == blk_size) - ++blk_size; /* don't know why */ - /* round request up to next highest SG_DEF_SECTOR_SZ byte boundary */ - blk_size = ALIGN(blk_size, SG_DEF_SECTOR_SZ); - SG_LOG(4, sfp, "%s: buff_size=%d, blk_size=%d\n", __func__, buff_size, - blk_size); - - /* N.B. ret_sz carried into this block ... */ - mx_sc_elems = sg_build_sgat(schp, sfp, max_sgat_sz); - if (mx_sc_elems < 0) - return mx_sc_elems; /* most likely -ENOMEM */ - - num = scatter_elem_sz; - if (unlikely(num != scatter_elem_sz_prev)) { - if (num < PAGE_SIZE) { - scatter_elem_sz = PAGE_SIZE; - scatter_elem_sz_prev = PAGE_SIZE; - } else - scatter_elem_sz_prev = num; + if (unlikely(minlen <= 0)) { + if (minlen < 0) + return -EFAULT; + ++minlen; /* don't remember why */ } + /* round request up to next highest SG_DEF_SECTOR_SZ byte boundary */ + align_sz = ALIGN(minlen, SG_DEF_SECTOR_SZ); - order = get_order(num); -retry: - ret_sz = 1 << (PAGE_SHIFT + order); - - for (k = 0, rem_sz = blk_size; rem_sz > 0 && k < mx_sc_elems; - k++, rem_sz -= ret_sz) { - - num = (rem_sz > scatter_elem_sz_prev) ? - scatter_elem_sz_prev : rem_sz; - - schp->pages[k] = alloc_pages(gfp_mask, order); + schp->pages = kcalloc(mx_sgat_elems, ptr_sz, mask_kz); + SG_LOG(4, sfp, "%s: minlen=%d, align_sz=%d [sz=%zu, 0x%p ++]\n", + __func__, minlen, align_sz, mx_sgat_elems * ptr_sz, + schp->pages); + if (unlikely(!schp->pages)) + return -ENOMEM; + elem_sz = sfp->sgat_elem_sz; /* power of 2 and >= PAGE_SIZE */ + o_order = get_order(elem_sz); + order = o_order; + +again: + for (k = 0, rem_sz = align_sz; rem_sz > 0 && k < mx_sgat_elems; + ++k, rem_sz -= elem_sz) { + schp->pages[k] = alloc_pages(mask_ap, order); if (!schp->pages[k]) - goto out; - - if (num == scatter_elem_sz_prev) { - if (unlikely(ret_sz > scatter_elem_sz_prev)) { - scatter_elem_sz = ret_sz; - scatter_elem_sz_prev = ret_sz; - } - } - SG_LOG(5, sfp, "%s: k=%d, num=%d, ret_sz=%d\n", __func__, k, - num, ret_sz); - } /* end of for loop */ - + goto err_out; + SG_LOG(5, sfp, "%s: k=%d, order=%d [0x%p ++]\n", __func__, k, + order, schp->pages[k]); + } schp->page_order = order; schp->num_sgat = k; - SG_LOG(5, sfp, "%s: num_sgat=%d, order=%d\n", __func__, k, order); - schp->buflen = blk_size; - if (rem_sz > 0) /* must have failed */ - return -ENOMEM; + SG_LOG(((order != o_order || rem_sz > 0) ? 2 : 5), sfp, + "%s: num_sgat=%d, order=%d,%d\n", __func__, k, o_order, order); + if (unlikely(rem_sz > 0)) { /* hit mx_sgat_elems */ + order = 0; /* force exit */ + goto err_out; + } + schp->buflen = align_sz; return 0; -out: - for (i = 0; i < k; i++) - __free_pages(schp->pages[i], order); - - if (--order >= 0) - goto retry; +err_out: + for (j = 0; j < k; ++j) + __free_pages(schp->pages[j], order); + if (--order >= 0) { + elem_sz >>= 1; + goto again; + } + kfree(schp->pages); + schp->pages = NULL; return -ENOMEM; } static void -sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) +sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp) { - SG_LOG(4, sfp, "%s: num_sgat=%d\n", __func__, schp->num_sgat); - if (schp->pages && schp->sglist_len > 0) { - if (!schp->dio_in_use) { - int k; + int k; + void *p; - for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { - SG_LOG(5, sfp, "%s: pg[%d]=0x%p --\n", - __func__, k, schp->pages[k]); - __free_pages(schp->pages[k], schp->page_order); - } - kfree(schp->pages); - } + if (!schp->pages) + return; + for (k = 0; k < schp->num_sgat; ++k) { + p = schp->pages[k]; + SG_LOG(5, sfp, "%s: pg[%d]=0x%p --\n", __func__, k, p); + if (unlikely(!p)) + continue; + __free_pages(p, schp->page_order); } - memset(schp, 0, sizeof (*schp)); + SG_LOG(5, sfp, "%s: pg_order=%u, free pgs=0x%p --\n", __func__, + schp->page_order, schp->pages); + kfree(schp->pages); +} + +/* Remove the data (possibly a sgat list) held by srp, not srp itself */ +static void +sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp) +{ + SG_LOG(4, sfp, "%s: num_sgat=%d%s\n", __func__, schp->num_sgat, + ((sfp ? (&sfp->reserve == schp) : false) ? + " [rsv]" : "")); + if (!schp->dio_in_use) + sg_remove_sgat_helper(sfp, schp); + + memset(schp, 0, sizeof(*schp)); /* zeros buflen and dlen */ } /* @@ -2159,12 +2184,12 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { if (num > num_xfer) { if (copy_to_user(outp, page_address(schp->pages[k]), - num_xfer)) + num_xfer)) return -EFAULT; break; } else { if (copy_to_user(outp, page_address(schp->pages[k]), - num)) + num)) return -EFAULT; num_xfer -= num; if (num_xfer <= 0) @@ -2184,10 +2209,10 @@ sg_build_reserve(struct sg_fd *sfp, int req_size) do { if (req_size < PAGE_SIZE) req_size = PAGE_SIZE; - if (0 == sg_build_indirect(schp, sfp, req_size)) + if (sg_mk_sgat(schp, sfp, req_size) == 0) return; else - sg_remove_scat(sfp, schp); + sg_remove_sgat(sfp, schp); req_size >>= 1; /* divide by 2 */ } while (req_size > (PAGE_SIZE / 2)); } @@ -2329,8 +2354,8 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) static struct sg_fd * sg_add_sfp(struct sg_device *sdp) { + int rbuf_len; unsigned long iflags; - int bufflen; struct sg_fd *sfp; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); @@ -2347,6 +2372,14 @@ sg_add_sfp(struct sg_device *sdp) sfp->force_packid = SG_DEF_FORCE_PACK_ID; sfp->cmd_q = SG_DEF_COMMAND_Q; sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; + /* + * SG_SCATTER_SZ initializes scatter_elem_sz but different value may + * be given as driver/module parameter (e.g. 'scatter_elem_sz=8192'). + * Any user provided number will be changed to be PAGE_SIZE as a + * minimum, otherwise it will be rounded down (if required) to a + * power of 2. So it will always be a power of 2. + */ + sfp->sgat_elem_sz = scatter_elem_sz; sfp->parentdp = sdp; atomic_set(&sfp->submitted, 0); atomic_set(&sfp->waiting, 0); @@ -2363,14 +2396,13 @@ sg_add_sfp(struct sg_device *sdp) if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; - bufflen = min_t(int, sg_big_buff, - max_sectors_bytes(sdp->device->request_queue)); - sg_build_reserve(sfp, bufflen); - SG_LOG(3, sfp, "%s: bufflen=%d, num_sgat=%d\n", __func__, - sfp->reserve.buflen, sfp->reserve.num_sgat); + rbuf_len = min_t(int, sg_big_buff, sdp->max_sgat_sz); + if (rbuf_len > 0) + sg_build_reserve(sfp, rbuf_len); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); + SG_LOG(3, sfp, "%s: success, sfp=0x%p ++\n", __func__, sfp); return sfp; } @@ -2401,16 +2433,14 @@ sg_remove_sfp_usercontext(struct work_struct *work) if (sfp->reserve.buflen > 0) { SG_LOG(6, sfp, "%s: buflen=%d, num_sgat=%d\n", __func__, (int)sfp->reserve.buflen, (int)sfp->reserve.num_sgat); - sg_remove_scat(sfp, &sfp->reserve); + sg_remove_sgat(sfp, &sfp->reserve); } SG_LOG(6, sfp, "%s: sfp=0x%p\n", __func__, sfp); kfree(sfp); - if (sdp) { - scsi_device_put(sdp->device); - kref_put(&sdp->d_ref, sg_device_destroy); - } + scsi_device_put(sdp->device); + kref_put(&sdp->d_ref, sg_device_destroy); module_put(THIS_MODULE); } From patchwork Mon Oct 24 03:20:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016571 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1B4B5C3A59D for ; Mon, 24 Oct 2022 03:26:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230358AbiJXD0K (ORCPT ); Sun, 23 Oct 2022 23:26:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230260AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6A9D35FAD3 for ; Sun, 23 Oct 2022 20:21:29 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 01EE52041E3; Mon, 24 Oct 2022 05:21:29 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id K1Hq-LJDd4HT; Mon, 24 Oct 2022 05:21:28 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id CD2C72041AF; Mon, 24 Oct 2022 05:21:27 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 18/44] sg: introduce request state machine Date: Sun, 23 Oct 2022 23:20:32 -0400 Message-Id: <20221024032058.14077-19-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org The introduced request state machine is not wired in so that the size of one of the following patches is reduced. Bit operation defines for the request and file descriptor level are also introduced. Minor rework og sg_read_append() function. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 176 +++++++++++++++++++++++++++++++--------------- 1 file changed, 120 insertions(+), 56 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 68950cdae163..18881e551702 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -77,7 +77,40 @@ static char *sg_version_date = "20190606"; */ #define SG_MAX_CDB_SIZE 252 +/* Following enum contains the states of sg_request::rq_st */ +enum sg_rq_state { + SG_RS_INACTIVE = 0, /* request not in use (e.g. on fl) */ + SG_RS_INFLIGHT, /* active: cmd/req issued, no response yet */ + SG_RS_AWAIT_RCV, /* have response from LLD, awaiting receive */ + SG_RS_BUSY, /* temporary state should rarely be seen */ +}; + +#define SG_TIME_UNIT_MS 0 /* milliseconds */ +#define SG_DEF_TIME_UNIT SG_TIME_UNIT_MS #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) +#define SG_FD_Q_AT_HEAD 0 +#define SG_DEFAULT_Q_AT SG_FD_Q_AT_HEAD /* for backward compatibility */ +#define SG_FL_MMAP_DIRECT (SG_FLAG_MMAP_IO | SG_FLAG_DIRECT_IO) + +/* Only take lower 4 bits of driver byte, all host byte and sense byte */ +#define SG_ML_RESULT_MSK 0x0fff00ff /* mid-level's 32 bit result value */ + +#define SG_PACK_ID_WILDCARD (-1) + +#define SG_ADD_RQ_MAX_RETRIES 40 /* to stop infinite _trylock(s) */ + +/* Bit positions (flags) for sg_request::frq_bm bitmask follow */ +#define SG_FRQ_IS_ORPHAN 1 /* owner of request gone */ +#define SG_FRQ_SYNC_INVOC 2 /* synchronous (blocking) invocation */ +#define SG_FRQ_NO_US_XFER 3 /* no user space transfer of data */ +#define SG_FRQ_DEACT_ORPHAN 6 /* not keeping orphan so de-activate */ + +/* Bit positions (flags) for sg_fd::ffd_bm bitmask follow */ +#define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ +#define SG_FFD_CMD_Q 1 /* clear: only 1 active req per fd */ +#define SG_FFD_KEEP_ORPHAN 2 /* policy for this fd */ +#define SG_FFD_MMAP_CALLED 3 /* mmap(2) system call made on fd */ +#define SG_FFD_Q_AT_TAIL 5 /* set: queue reqs at tail of blk q */ /* Bit positions (flags) for sg_device::fdev_bm bitmask follow */ #define SG_FDEV_EXCLUDE 0 /* have fd open with O_EXCL */ @@ -85,12 +118,11 @@ static char *sg_version_date = "20190606"; #define SG_FDEV_LOG_SENSE 2 /* set by ioctl(SG_SET_DEBUG) */ static int sg_big_buff = SG_DEF_RESERVED_SIZE; -/* N.B. This variable is readable and writeable via - /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer - of this size (or less if there is not enough memory) will be reserved - for use by this file descriptor. [Deprecated usage: this variable is also - readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into - the kernel (i.e. it is not a module).] */ +/* + * This variable is accessible via /proc/scsi/sg/def_reserved_size . Each + * time sg_open() is called a sg_request of this size (or less if there is + * not enough memory) will be reserved for use by this file descriptor. + */ static int def_reserved_size = -1; /* picks up init parameter */ static int sg_allow_dio = SG_ALLOW_DIO_DEF; @@ -134,6 +166,7 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ /* done protected by rq_list_lock */ char done; /* 0->before bh, 1->before read, 2->read */ + atomic_t rq_st; /* request state, holds a enum sg_rq_state */ struct request *rq; /* released in sg_rq_end_io(), bio kept */ struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ struct execute_work ew_orph; /* harvest orphan request */ @@ -212,10 +245,11 @@ static void sg_remove_sfp(struct kref *); static struct sg_request *sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy); static struct sg_request *sg_setup_req(struct sg_fd *sfp); -static int sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); +static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); static void sg_calc_sgat_param(struct sg_device *sdp); +static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ #define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ @@ -223,6 +257,8 @@ static void sg_calc_sgat_param(struct sg_device *sdp); #define SG_IS_DETACHING(sdp) test_bit(SG_FDEV_DETACHING, (sdp)->fdev_bm) #define SG_HAVE_EXCLUDE(sdp) test_bit(SG_FDEV_EXCLUDE, (sdp)->fdev_bm) +#define SG_RS_ACTIVE(srp) (atomic_read(&(srp)->rq_st) != SG_RS_INACTIVE) +#define SG_RS_AWAIT_READ(srp) (atomic_read(&(srp)->rq_st) == SG_RS_AWAIT_RCV) /* * Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages. @@ -381,15 +417,6 @@ sg_open(struct inode *inode, struct file *filp) res = sg_allow_if_err_recovery(sdp, non_block); if (res) goto error_out; - /* scsi_block_when_processing_errors() may block so bypass - * check if O_NONBLOCK. Permits SCSI commands to be issued - * during error recovery. Tread carefully. */ - if (!((op_flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) { - res = -ENXIO; - /* we are in error recovery for this device */ - goto error_out; - } mutex_lock(&sdp->open_rel_lock); if (op_flags & O_NONBLOCK) { @@ -488,12 +515,12 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; + u8 cmnd[SG_MAX_CDB_SIZE]; struct sg_header ov2hdr; struct sg_io_hdr v3hdr; struct sg_header *ohp = &ov2hdr; struct sg_io_hdr *h3p = &v3hdr; struct sg_comm_wr_t cwr; - u8 cmnd[SG_MAX_CDB_SIZE]; res = sg_check_file_access(filp, __func__); if (res) @@ -1283,7 +1310,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, case SG_GET_LOW_DMA: SG_LOG(3, sfp, "%s: SG_GET_LOW_DMA\n", __func__); return put_user(0, ip); - case SG_NEXT_CMD_LEN: + case SG_NEXT_CMD_LEN: /* active only in v2 interface */ SG_LOG(3, sfp, "%s: SG_NEXT_CMD_LEN\n", __func__); result = get_user(val, ip); if (result) @@ -2173,48 +2200,37 @@ sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp) static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) { + int k, num, res; + struct page *pgp; struct sg_scatter_hold *schp = &srp->data; - int k, num; SG_LOG(4, srp->parentfp, "%s: num_xfer=%d\n", __func__, num_xfer); - if (!outp || num_xfer <= 0) - return 0; + if (unlikely(!outp || num_xfer <= 0)) + return (num_xfer == 0 && outp) ? 0 : -EINVAL; num = 1 << (PAGE_SHIFT + schp->page_order); - for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { + for (k = 0, res = 0; k < schp->num_sgat; ++k) { + pgp = schp->pages[k]; + if (unlikely(!pgp)) { + res = -ENXIO; + break; + } if (num > num_xfer) { - if (copy_to_user(outp, page_address(schp->pages[k]), - num_xfer)) - return -EFAULT; + if (__copy_to_user(outp, page_address(pgp), num_xfer)) + res = -EFAULT; break; } else { - if (copy_to_user(outp, page_address(schp->pages[k]), - num)) - return -EFAULT; + if (__copy_to_user(outp, page_address(pgp), num)) { + res = -EFAULT; + break; + } num_xfer -= num; if (num_xfer <= 0) break; outp += num; } } - return 0; -} - -static void -sg_build_reserve(struct sg_fd *sfp, int req_size) -{ - struct sg_scatter_hold *schp = &sfp->reserve; - - SG_LOG(3, sfp, "%s: buflen=%d\n", __func__, req_size); - do { - if (req_size < PAGE_SIZE) - req_size = PAGE_SIZE; - if (sg_mk_sgat(schp, sfp, req_size) == 0) - return; - else - sg_remove_sgat(sfp, schp); - req_size >>= 1; /* divide by 2 */ - } while (req_size > (PAGE_SIZE / 2)); + return res; } static void @@ -2292,6 +2308,22 @@ sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) return NULL; } +static void +sg_build_reserve(struct sg_fd *sfp, int req_size) +{ + struct sg_scatter_hold *schp = &sfp->reserve; + + SG_LOG(3, sfp, "%s: buflen=%d\n", __func__, req_size); + do { + if (req_size < PAGE_SIZE) + req_size = PAGE_SIZE; + if (sg_mk_sgat(schp, sfp, req_size) == 0) + return; + sg_remove_sgat(sfp, schp); + req_size >>= 1; /* divide by 2 */ + } while (req_size > (PAGE_SIZE / 2)); +} + /* always adds to end of list */ static struct sg_request * sg_setup_req(struct sg_fd *sfp) @@ -2323,20 +2355,19 @@ sg_setup_req(struct sg_fd *sfp) return NULL; } -/* Return of 1 for found; 0 for not found */ -static int +static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) { unsigned long iflags; - int res = 0; - if (!sfp || !srp || list_empty(&sfp->rq_list)) - return res; + if (WARN_ON(!sfp || !srp)) + return; + if (list_empty(&sfp->rq_list)) + return; spin_lock_irqsave(&sfp->rq_list_lock, iflags); if (!list_empty(&srp->entry)) { list_del(&srp->entry); srp->parentfp = NULL; - res = 1; } spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); @@ -2347,8 +2378,6 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) */ if (unlikely(SG_IS_DETACHING(sfp->parentdp))) wake_up_interruptible_all(&sfp->read_wait); - - return res; } static struct sg_fd * @@ -2406,6 +2435,15 @@ sg_add_sfp(struct sg_device *sdp) return sfp; } +/* + * A successful call to sg_release() will result, at some later time, to this + * function being invoked. All requests associated with this file descriptor + * should be completed or cancelled when this function is called (due to + * sfp->f_ref). Also the file descriptor itself has not been accessible since + * it was list_del()-ed by the preceding sg_remove_sfp() call. So no locking + * is required. sdp should never be NULL but to make debugging more robust, + * this function will not blow up in that case. + */ static void sg_remove_sfp_usercontext(struct work_struct *work) { @@ -2499,6 +2537,31 @@ sg_get_dev(int dev) return sdp; } +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) +static const char * +sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) +{ + switch (rq_st) { /* request state */ + case SG_RS_INACTIVE: + return long_str ? "inactive" : "ina"; + case SG_RS_INFLIGHT: + return long_str ? "inflight" : "act"; + case SG_RS_AWAIT_RCV: + return long_str ? "await_receive" : "rcv"; + case SG_RS_BUSY: + return long_str ? "busy" : "bsy"; + default: + return long_str ? "unknown" : "unk"; + } +} +#else +static const char * +sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) +{ + return ""; +} +#endif + #if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* long, almost to end of file */ static int sg_proc_seq_show_int(struct seq_file *s, void *v); @@ -2795,8 +2858,9 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) jiffies_to_msecs(fp->timeout)), (ms > hp->duration ? ms - hp->duration : 0)); } - seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, - (int) srp->data.cmd_opcode); + seq_printf(s, "ms sgat=%d op=0x%02x dummy: %s\n", usg, + (int)srp->data.cmd_opcode, + sg_rq_st_str(SG_RS_INACTIVE, false)); } if (list_empty(&fp->rq_list)) seq_puts(s, " No requests active\n"); From patchwork Mon Oct 24 03:20:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016545 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 74F0AC3A59D for ; Mon, 24 Oct 2022 03:25:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230385AbiJXDZA (ORCPT ); Sun, 23 Oct 2022 23:25:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229714AbiJXDW5 (ORCPT ); Sun, 23 Oct 2022 23:22:57 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 7C5A75E64D for ; Sun, 23 Oct 2022 20:21:30 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 56BE92041F1; Mon, 24 Oct 2022 05:21:30 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id T7ofHgstHUVV; Mon, 24 Oct 2022 05:21:30 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 2C93C2041AF; Mon, 24 Oct 2022 05:21:29 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 19/44] sg: sg_find_srp_by_id Date: Sun, 23 Oct 2022 23:20:33 -0400 Message-Id: <20221024032058.14077-20-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Replace sg_get_rq_mark() with sg_find_srp_by_id() and sg_get_ready_srp(). Add sg_chk_mmap() to check flags and reserve buffer available for mmap() based requests. Add sg_copy_sense() and sg_rec_state_v3() which is just refactoring. Add sg_calc_rq_dur() and sg_get_dur() in preparation for optional nanosecond duration timing. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 345 ++++++++++++++++++++++++++++++---------------- 1 file changed, 229 insertions(+), 116 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 18881e551702..91bcb4d199b1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -157,16 +157,19 @@ struct sg_fd; struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct list_head entry; /* list entry */ - struct sg_fd *parentfp; /* NULL -> not in use */ struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ struct sg_io_hdr header; /* scsi command+info, see */ u8 sense_b[SCSI_SENSE_BUFFERSIZE]; + u32 duration; /* cmd duration in milliseconds */ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ /* done protected by rq_list_lock */ char done; /* 0->before bh, 1->before read, 2->read */ atomic_t rq_st; /* request state, holds a enum sg_rq_state */ + u64 start_ns; /* starting point of command duration calc */ + unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ + struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ struct request *rq; /* released in sg_rq_end_io(), bio kept */ struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ struct execute_work ew_orph; /* harvest orphan request */ @@ -232,7 +235,7 @@ static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, struct sg_request **o_srp); -static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwp); +static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp); static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer); static void sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp); @@ -242,8 +245,8 @@ static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, static void sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp); static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); -static struct sg_request *sg_get_rq_mark(struct sg_fd *sfp, int pack_id, - bool *busy); +static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, + bool *busy); static struct sg_request *sg_setup_req(struct sg_fd *sfp); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); @@ -446,7 +449,8 @@ sg_open(struct inode *inode, struct file *filp) sfp = sg_add_sfp(sdp); /* increments sdp->d_ref */ if (IS_ERR(sfp)) { res = PTR_ERR(sfp); - goto out_undo; } + goto out_undo; + } filp->private_data = sfp; atomic_inc(&sdp->open_cnt); @@ -509,7 +513,6 @@ sg_release(struct inode *inode, struct file *filp) static ssize_t sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) { - bool blocking = !(filp->f_flags & O_NONBLOCK); int mxsize, cmd_size, input_size, res; u8 opcode; struct sg_device *sdp; @@ -611,21 +614,19 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) } /* * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV, - * but is is possible that the app intended SG_DXFER_TO_DEV, because there - * is a non-zero input_size, so emit a warning. + * but it is possible that the app intended SG_DXFER_TO_DEV, because + * there is a non-zero input_size, so emit a warning. */ if (h3p->dxfer_direction == SG_DXFER_TO_FROM_DEV) { - printk_ratelimited(KERN_WARNING - "sg_write: data in/out %d/%d bytes " - "for SCSI command 0x%x-- guessing " - "data in;\n program %s not setting " - "count and/or reply_len properly\n", - ohp->reply_len - (int)SZ_SG_HEADER, - input_size, (unsigned int) cmnd[0], - current->comm); + printk_ratelimited + (KERN_WARNING + "%s: data in/out %d/%d bytes for SCSI command 0x%x-- guessing data in;\n" + " program %s not setting count and/or reply_len properly\n", + __func__, ohp->reply_len - (int)SZ_SG_HEADER, + input_size, (unsigned int)cmnd[0], current->comm); } cwr.timeout = sfp->timeout; - cwr.blocking = blocking; + cwr.blocking = !(filp->f_flags & O_NONBLOCK); cwr.srp = srp; cwr.cmnd = cmnd; res = sg_common_write(sfp, &cwr); @@ -654,6 +655,18 @@ sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, return 0; } +static inline int +sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) +{ + if (len > sfp->reserve.buflen) + return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ + if (rq_flags & SG_FLAG_DIRECT_IO) + return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ + if (sfp->res_in_use) + return -EBUSY; /* reserve buffer already being used */ + return 0; +} + static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, @@ -687,17 +700,10 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, return -ENOSYS; } if (hp->flags & SG_FLAG_MMAP_IO) { - if (hp->dxfer_len > sfp->reserve.buflen) { + res = sg_chk_mmap(sfp, hp->flags, hp->dxfer_len); + if (res) { sg_deact_request(sfp, srp); - return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ - } - if (hp->flags & SG_FLAG_DIRECT_IO) { - sg_deact_request(sfp, srp); - return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ - } - if (sfp->res_in_use) { - sg_deact_request(sfp, srp); - return -EBUSY; /* reserve buffer already being used */ + return res; } } ul_timeout = msecs_to_jiffies(srp->header.timeout); @@ -719,6 +725,12 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, return count; } +/* + * All writes and submits converge on this function to launch the SCSI + * command/request (via blk_execute_rq_nowait). Returns a pointer to a + * sg_request object holding the request just issued or a negated errno + * value twisted by ERR_PTR. + */ static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) { @@ -778,58 +790,69 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) return 0; } -static int -get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) -{ - struct sg_header __user *old_hdr = buf; - int reply_len; - - if (count >= SZ_SG_HEADER) { - /* negative reply_len means v3 format, otherwise v1/v2 */ - if (get_user(reply_len, &old_hdr->reply_len)) - return -EFAULT; - - if (reply_len >= 0) - return get_user(*pack_id, &old_hdr->pack_id); +/* + * This function is called by wait_event_interruptible in sg_read() and + * sg_ctl_ioreceive(). wait_event_interruptible will return if this one + * returns true (or an event like a signal (e.g. control-C) occurs). + */ - if (in_compat_syscall() && - count >= sizeof(struct compat_sg_io_hdr)) { - struct compat_sg_io_hdr __user *hp = buf; +static inline bool +sg_get_ready_srp(struct sg_fd *sfp, struct sg_request **srpp, int pack_id, + bool *busy) +{ + struct sg_request *srp = sg_find_srp_by_id(sfp, pack_id, busy); - return get_user(*pack_id, &hp->pack_id); - } + *srpp = srp; + return !!srp; +} - if (count >= sizeof(struct sg_io_hdr)) { - struct sg_io_hdr __user *hp = buf; +/* + * Returns number of bytes copied to user space provided sense buffer or + * negated errno value. + */ +static int +sg_copy_sense(struct sg_request *srp) +{ + int sb_len_ret = 0; + struct sg_io_hdr *hp = &srp->header; - return get_user(*pack_id, &hp->pack_id); - } + /* If need be, copy the sense buffer to the user space */ + if ((CHECK_CONDITION & hp->masked_status) || + (srp->sense_b[0] & 0x70) == 0x70) { + int sb_len = SCSI_SENSE_BUFFERSIZE; + void __user *up = hp->sbp; + + sb_len = min_t(int, hp->mx_sb_len, sb_len); + /* Additional sense length field */ + sb_len_ret = 8 + (int)srp->sense_b[7]; + sb_len_ret = min_t(int, sb_len_ret, sb_len); + if (copy_to_user(up, srp->sense_b, sb_len_ret)) + return -EFAULT; + hp->sb_len_wr = sb_len_ret; } - - /* no valid header was passed, so ignore the pack_id */ - *pack_id = -1; - return 0; + return sb_len_ret; } static int -srp_done(struct sg_fd *sfp, struct sg_request *srp) +sg_rec_state_v3(struct sg_fd *sfp, struct sg_request *srp) { - unsigned long flags; - int ret; + int sb_len_wr; - spin_lock_irqsave(&sfp->rq_list_lock, flags); - ret = srp->done; - spin_unlock_irqrestore(&sfp->rq_list_lock, flags); - return ret; + sb_len_wr = sg_copy_sense(srp); + if (sb_len_wr < 0) + return sb_len_wr; + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) + return -ENODEV; + return 0; } + static ssize_t -sg_receive_v3(struct sg_fd *sfp, char __user *buf, size_t count, - struct sg_request *srp) +sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, + void __user *buf) { - struct sg_io_hdr *hp = &srp->header; int err = 0; - int len; + struct sg_io_hdr *hp = &srp->header; if (in_compat_syscall()) { if (count < sizeof(struct compat_sg_io_hdr)) { @@ -840,22 +863,9 @@ sg_receive_v3(struct sg_fd *sfp, char __user *buf, size_t count, err = -EINVAL; goto err_out; } - hp->sb_len_wr = 0; - if ((hp->mx_sb_len > 0) && hp->sbp) { - if ((CHECK_CONDITION & hp->masked_status) || - (srp->sense_b[0] & 0x70) == 0x70) { - int sb_len = SCSI_SENSE_BUFFERSIZE; - sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; - len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */ - len = (len > sb_len) ? sb_len : len; - if (copy_to_user(hp->sbp, srp->sense_b, len)) { - err = -EFAULT; - goto err_out; - } - hp->driver_status = DRIVER_SENSE; - hp->sb_len_wr = len; - } - } + err = sg_rec_state_v3(sfp, srp); + if (err < 0) + goto err_out; if (hp->masked_status || hp->host_status || hp->driver_status) hp->info |= SG_INFO_CHECK; err = put_sg_io_hdr(hp, buf); @@ -940,6 +950,39 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, return res; } +static int +get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) +{ + struct sg_header __user *old_hdr = buf; + int reply_len; + + if (count >= SZ_SG_HEADER) { + /* negative reply_len means v3 format, otherwise v1/v2 */ + if (get_user(reply_len, &old_hdr->reply_len)) + return -EFAULT; + + if (reply_len >= 0) + return get_user(*pack_id, &old_hdr->pack_id); + + if (in_compat_syscall() && + count >= sizeof(struct compat_sg_io_hdr)) { + struct compat_sg_io_hdr __user *hp = buf; + + return get_user(*pack_id, &hp->pack_id); + } + + if (count >= sizeof(struct sg_io_hdr)) { + struct sg_io_hdr __user *hp = buf; + + return get_user(*pack_id, &hp->pack_id); + } + } + + /* no valid header was passed, so ignore the pack_id */ + *pack_id = -1; + return 0; +} + static ssize_t sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { @@ -968,12 +1011,12 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) if (ret) return ret; - srp = sg_get_rq_mark(sfp, req_pack_id, &busy); + srp = sg_find_srp_by_id(sfp, req_pack_id, &busy); if (!srp) { /* now wait on packet to arrive */ if (non_block) return -EAGAIN; ret = wait_event_interruptible(sfp->read_wait, - ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || + ((srp = sg_find_srp_by_id(sfp, req_pack_id, &busy)) || (!busy && SG_IS_DETACHING(sdp)))); if (!srp) /* signal or detaching */ @@ -982,7 +1025,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) if (srp->header.interface_id == '\0') ret = sg_read_v1v2(buf, (int)count, sfp, srp); else - ret = sg_receive_v3(sfp, buf, count, srp); + ret = sg_receive_v3(sfp, srp, count, buf); if (ret < 0) SG_LOG(1, sfp, "%s: negated errno: %d\n", __func__, ret); return ret < 0 ? ret : (int)count; @@ -1031,6 +1074,51 @@ sg_calc_sgat_param(struct sg_device *sdp) sdp->max_sgat_sz = sz; } +static u32 +sg_calc_rq_dur(const struct sg_request *srp) +{ + ktime_t ts0 = ns_to_ktime(srp->start_ns); + ktime_t now_ts; + s64 diff; + + if (ts0 == 0) + return 0; + if (unlikely(ts0 == S64_MAX)) /* _prior_ to issuing req */ + return 999999999; /* eye catching */ + now_ts = ktime_get_boottime(); + if (unlikely(ts0 > now_ts)) + return 999999998; + /* unlikely req duration will exceed 2**32 milliseconds */ + diff = ktime_ms_delta(now_ts, ts0); + return (diff > (s64)U32_MAX) ? 3999999999U : (u32)diff; +} + +/* Return of U32_MAX means srp is inactive */ +static u32 +sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, + bool *is_durp) +{ + bool is_dur = false; + u32 res = U32_MAX; + + switch (sr_stp ? *sr_stp : atomic_read(&srp->rq_st)) { + case SG_RS_INFLIGHT: + case SG_RS_BUSY: + res = sg_calc_rq_dur(srp); + break; + case SG_RS_AWAIT_RCV: + case SG_RS_INACTIVE: + res = srp->duration; + is_dur = true; /* completion has occurred, timing finished */ + break; + default: + break; + } + if (is_durp) + *is_durp = is_dur; + return res; +} + static void sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) { @@ -1047,6 +1135,7 @@ sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) srp->header.masked_status & srp->header.host_status & srp->header.driver_status; + rinfo[val].duration = sg_get_dur(srp, NULL, NULL); /* dummy */ if (srp->done) rinfo[val].duration = srp->header.duration; @@ -1064,13 +1153,25 @@ sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) } } +static int +srp_done(struct sg_fd *sfp, struct sg_request *srp) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sfp->rq_list_lock, flags); + ret = srp->done; + spin_unlock_irqrestore(&sfp->rq_list_lock, flags); + return ret; +} + /* * Handles ioctl(SG_IO) for blocking (sync) usage of v3 or v4 interface. * Returns 0 on success else a negated errno. */ static int sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, - void __user *p) + void __user *buf) { bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE); int res; @@ -1079,7 +1180,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, res = sg_allow_if_err_recovery(sdp, false); if (res) return res; - res = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only, + res = sg_submit(sfp, filp, buf, SZ_SG_IO_HDR, true, read_only, true, &srp); if (res < 0) return res; @@ -1089,7 +1190,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, if (srp->done) { srp->done = 2; spin_unlock_irq(&sfp->rq_list_lock); - res = sg_receive_v3(sfp, p, SZ_SG_IO_HDR, srp); + res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, buf); return (res < 0) ? res : 0; } srp->orphan = 1; @@ -1317,7 +1418,9 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return result; if (val > SG_MAX_CDB_SIZE) return -ENOMEM; - sfp->next_cmd_len = (val > 0) ? val : 0; + mutex_lock(&sfp->f_mutex); + sfp->next_cmd_len = max_t(int, val, 0); + mutex_unlock(&sfp->f_mutex); return 0; case SG_GET_ACCESS_COUNT: SG_LOG(3, sfp, "%s: SG_GET_ACCESS_COUNT\n", __func__); @@ -2233,6 +2336,45 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) return res; } +/* + * If there are multiple requests outstanding, the speed of this function is + * important. SG_PACK_ID_WILDCARD is -1 and that case is typically the fast + * path. This function is only used in the non-blocking cases. Returns pointer + * to (first) matching sg_request or NULL. If found, sg_request state is moved + * from SG_RS_AWAIT_RCV to SG_RS_BUSY. + * The 'busy' pointer is only set to true when no requests are found in the + * SG_RS_AWAIT_RCV state and SG_IS_DETACHING() is true and there is at least + * one request in SG_RS_INFLIGHT state; in all other cases *busy is set false. + */ +static struct sg_request * +sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) +{ + struct sg_request *resp; + unsigned long iflags; + + *busy = false; + spin_lock_irqsave(&sfp->rq_list_lock, iflags); + list_for_each_entry(resp, &sfp->rq_list, entry) { + /* look for requests that are not SG_IO owned */ + if ((!resp->sg_io_owned) && + ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { + switch (resp->done) { + case 0: /* request active */ + *busy = true; + break; + case 1: /* request done; response ready to return */ + resp->done = 2; /* guard against other readers */ + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; + case 2: /* response already being returned */ + break; + } + } + } + spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return NULL; +} + static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) { @@ -2279,35 +2421,6 @@ sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) sfp->res_in_use = 0; } -static struct sg_request * -sg_get_rq_mark(struct sg_fd *sfp, int pack_id, bool *busy) -{ - struct sg_request *resp; - unsigned long iflags; - - *busy = false; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - list_for_each_entry(resp, &sfp->rq_list, entry) { - /* look for requests that are not SG_IO owned */ - if ((!resp->sg_io_owned) && - ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { - switch (resp->done) { - case 0: /* request active */ - *busy = true; - break; - case 1: /* request done; response ready to return */ - resp->done = 2; /* guard against other readers */ - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; - case 2: /* response already being returned */ - break; - } - } - } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return NULL; -} - static void sg_build_reserve(struct sg_fd *sfp, int req_size) { From patchwork Mon Oct 24 03:20:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016553 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1F1D9C433FE for ; Mon, 24 Oct 2022 03:25:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230132AbiJXDZW (ORCPT ); Sun, 23 Oct 2022 23:25:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230314AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B20C75EDD6 for ; Sun, 23 Oct 2022 20:21:32 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id CFC2A204201; Mon, 24 Oct 2022 05:21:31 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EoArXJaGRo6L; Mon, 24 Oct 2022 05:21:31 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 7FD712041AF; Mon, 24 Oct 2022 05:21:30 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 20/44] sg: sg_fill_request_element Date: Sun, 23 Oct 2022 23:20:34 -0400 Message-Id: <20221024032058.14077-21-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Replace sg_fill_request_table() with sg_fill_request_element(). Reduce the size of the sg_rq_end_io() function by breaking out some sense buffer checks into sg_check_sense(). Reduce the size of the sg_start_req() function with sg_set_map_data() helper. All code refactoring, no logical change. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 211 +++++++++++++++++++++++++--------------------- 1 file changed, 117 insertions(+), 94 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 91bcb4d199b1..94a07c8c7dd7 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -163,6 +163,7 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ u32 duration; /* cmd duration in milliseconds */ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ + u32 rq_result; /* packed scsi request result from LLD */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ /* done protected by rq_list_lock */ char done; /* 0->before bh, 1->before read, 2->read */ @@ -633,6 +634,18 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) return (res < 0) ? res : count; } +static inline int +sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) +{ + if (len > sfp->reserve.buflen) + return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ + if (rq_flags & SG_FLAG_DIRECT_IO) + return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ + if (sfp->res_in_use) + return -EBUSY; /* reserve buffer already being used */ + return 0; +} + static int sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, int len, u8 *cdbp) @@ -655,18 +668,6 @@ sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, return 0; } -static inline int -sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) -{ - if (len > sfp->reserve.buflen) - return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ - if (rq_flags & SG_FLAG_DIRECT_IO) - return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ - if (sfp->res_in_use) - return -EBUSY; /* reserve buffer already being used */ - return 0; -} - static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, size_t count, bool blocking, bool read_only, bool sg_io_owned, @@ -875,6 +876,11 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, return err; } +/* + * Completes a v3 request/command. Called from sg_read {v2 or v3}, + * ioctl(SG_IO) {for v3}, or from ioctl(SG_IORECEIVE) when its + * completing a v3 request/command. + */ static int sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, struct sg_request *srp) @@ -1120,37 +1126,28 @@ sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, } static void -sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) +sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, + struct sg_req_info *rip) { - struct sg_request *srp; - int val; unsigned int ms; - val = 0; - list_for_each_entry(srp, &sfp->rq_list, entry) { - if (val >= SG_MAX_QUEUE) - break; - rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; - rinfo[val].duration = sg_get_dur(srp, NULL, NULL); /* dummy */ - if (srp->done) - rinfo[val].duration = - srp->header.duration; - else { - ms = jiffies_to_msecs(jiffies); - rinfo[val].duration = - (ms > srp->header.duration) ? + rip->req_state = srp->done + 1; + rip->problem = srp->header.masked_status & + srp->header.host_status & + srp->header.driver_status; + rip->duration = sg_get_dur(srp, NULL, NULL); /* dummy */ + if (srp->done) { + rip->duration = srp->header.duration; + } else { + ms = jiffies_to_msecs(jiffies); + rip->duration = (ms > srp->header.duration) ? (ms - srp->header.duration) : 0; - } - rinfo[val].orphan = srp->orphan; - rinfo[val].sg_io_owned = srp->sg_io_owned; - rinfo[val].pack_id = srp->header.pack_id; - rinfo[val].usr_ptr = srp->header.usr_ptr; - val++; } + rip->orphan = srp->orphan; + rip->sg_io_owned = srp->sg_io_owned; + rip->pack_id = srp->header.pack_id; + rip->usr_ptr = srp->header.usr_ptr; + } static int @@ -1242,28 +1239,35 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o, static int sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p) { - int result; + int result, val; unsigned long iflags; - sg_req_info_t *rinfo; + struct sg_request *srp; + sg_req_info_t *rinfop; - rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, - GFP_KERNEL); - if (!rinfo) + rinfop = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, + GFP_KERNEL); + if (!rinfop) return -ENOMEM; spin_lock_irqsave(&sfp->rq_list_lock, iflags); - sg_fill_request_table(sfp, rinfo); + val = 0; + list_for_each_entry(srp, &sfp->rq_list, entry) { + if (val >= SG_MAX_QUEUE) + break; + sg_fill_request_element(sfp, srp, rinfop + val); + val++; + } spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); #ifdef CONFIG_COMPAT if (in_compat_syscall()) - result = put_compat_request_table(p, rinfo); + result = put_compat_request_table(p, rinfop); else - result = copy_to_user(p, rinfo, + result = copy_to_user(p, rinfop, SZ_SG_REQ_INFO * SG_MAX_QUEUE); #else - result = copy_to_user(p, rinfo, + result = copy_to_user(p, rinfop, SZ_SG_REQ_INFO * SG_MAX_QUEUE); #endif - kfree(rinfo); + kfree(rinfop); return result > 0 ? -EFAULT : result; /* treat short copy as error */ } @@ -1318,7 +1322,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return result; sfp->force_packid = val ? 1 : 0; return 0; - case SG_GET_PACK_ID: + case SG_GET_PACK_ID: /* or tag of oldest "read"-able, -1 if none */ val = -1; spin_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { @@ -1669,6 +1673,39 @@ sg_rq_end_io_usercontext(struct work_struct *work) kref_put(&sfp->f_ref, sg_remove_sfp); } +static void +sg_check_sense(struct sg_device *sdp, struct sg_request *srp, int sense_len) +{ + int driver_stat; + u32 rq_res = srp->rq_result; + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(srp->rq); + u8 *sbp = scmd ? scmd->sense_buffer : NULL; + + if (!sbp) + return; + driver_stat = driver_byte(rq_res); + if (driver_stat & DRIVER_SENSE) { + struct scsi_sense_hdr ssh; + + if (scsi_normalize_sense(sbp, sense_len, &ssh)) { + if (!scsi_sense_is_deferred(&ssh)) { + if (ssh.sense_key == UNIT_ATTENTION) { + if (sdp->device->removable) + sdp->device->changed = 1; + } + } + } + } + if (test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm) > 0) { + int scsi_stat = rq_res & 0xff; + + if (scsi_stat == SAM_STAT_CHECK_CONDITION || + scsi_stat == SAM_STAT_COMMAND_TERMINATED) + __scsi_print_sense(sdp->device, __func__, sbp, + sense_len); + } +} + /* * This function is a "bottom half" handler that is called by the mid * level when a command is completed (or has failed). @@ -1682,8 +1719,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) struct sg_fd *sfp; unsigned long iflags; unsigned int ms; - char *sense; - int result, resid, done = 1; + int resid, slen; + int done = 1; if (WARN_ON(srp->done != 0)) return RQ_END_IO_NONE; @@ -1696,44 +1733,20 @@ sg_rq_end_io(struct request *rq, blk_status_t status) if (unlikely(SG_IS_DETACHING(sdp))) pr_info("%s: device detaching\n", __func__); - sense = scmd->sense_buffer; - result = scmd->result; resid = scmd->resid_len; - srp->header.resid = resid; + + slen = min_t(int, scmd->sense_len, SCSI_SENSE_BUFFERSIZE); + SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, - srp->header.pack_id, result); + srp->header.pack_id, srp->rq_result); ms = jiffies_to_msecs(jiffies); srp->header.duration = (ms > srp->header.duration) ? (ms - srp->header.duration) : 0; - if (0 != result) { - struct scsi_sense_hdr sshdr; - - srp->header.status = 0xff & result; - srp->header.masked_status = status_byte(result); - srp->header.msg_status = COMMAND_COMPLETE; - srp->header.host_status = host_byte(result); - srp->header.driver_status = driver_byte(result); - if (test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm) && - (srp->header.masked_status == CHECK_CONDITION || - srp->header.masked_status == COMMAND_TERMINATED)) - __scsi_print_sense(sdp->device, __func__, sense, - SCSI_SENSE_BUFFERSIZE); - - /* Following if statement is a patch supplied by Eric Youngdale */ - if (driver_byte(result) != 0 - && scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr) - && !scsi_sense_is_deferred(&sshdr) - && sshdr.sense_key == UNIT_ATTENTION - && sdp->device->removable) { - /* Detected possible disc change. Set the bit - this */ - /* may be used if there are filesystems using this device */ - sdp->device->changed = 1; - } - } - - if (scmd->sense_len) - memcpy(srp->sense_b, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); + if (srp->rq_result != 0 && slen > 0) + sg_check_sense(sdp, srp, slen); + if (slen > 0) + memcpy(srp->sense_b, scmd->sense_buffer, slen); /* Rely on write phase to clean out srp status values, so no "else" */ @@ -1790,6 +1803,7 @@ static struct class *sg_sysfs_class; static bool sg_sysfs_valid; +/* Returns valid pointer to sg_device or negated errno twisted by ERR_PTR */ static struct sg_device * sg_add_device_helper(struct scsi_device *scsidp) { @@ -2022,6 +2036,7 @@ init_sg(void) { int rc; + /* check scatter_elem_sz module parameter, change if inappropriate */ if (scatter_elem_sz < (int)PAGE_SIZE) scatter_elem_sz = PAGE_SIZE; else if (!is_power_of_2(scatter_elem_sz)) @@ -2035,8 +2050,11 @@ init_sg(void) SG_MAX_DEVS, "sg"); if (rc) return rc; - sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); - if ( IS_ERR(sg_sysfs_class) ) { + pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n", + "sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR, + sg_version_date); + sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); + if (IS_ERR(sg_sysfs_class)) { rc = PTR_ERR(sg_sysfs_class); goto err_out_unreg; } @@ -2067,6 +2085,18 @@ exit_sg(void) idr_destroy(&sg_index_idr); } +static void +sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, + struct rq_map_data *mdp) +{ + memset(mdp, 0, sizeof(*mdp)); + mdp->pages = schp->pages; + mdp->page_order = schp->page_order; + mdp->nr_entries = schp->num_sgat; + mdp->offset = 0; + mdp->null_mapped = !up_valid; +} + static int sg_start_req(struct sg_request *srp, unsigned char *cmd) { @@ -2147,15 +2177,8 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) } mutex_unlock(&sfp->f_mutex); - md->pages = req_schp->pages; - md->page_order = req_schp->page_order; - md->nr_entries = req_schp->num_sgat; - md->offset = 0; - md->null_mapped = hp->dxferp ? 0 : 1; - if (dxfer_dir == SG_DXFER_TO_FROM_DEV) - md->from_user = 1; - else - md->from_user = 0; + sg_set_map_data(req_schp, !!hp->dxferp, md); + md->from_user = (dxfer_dir == SG_DXFER_TO_FROM_DEV); } res = blk_rq_map_user_io(rq, md, hp->dxferp, hp->dxfer_len, From patchwork Mon Oct 24 03:20:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016548 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDB95FA373F for ; Mon, 24 Oct 2022 03:25:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230266AbiJXDZO (ORCPT ); Sun, 23 Oct 2022 23:25:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55176 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230298AbiJXDW6 (ORCPT ); Sun, 23 Oct 2022 23:22:58 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 67B565F230 for ; Sun, 23 Oct 2022 20:21:33 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 2D72B2041C0; Mon, 24 Oct 2022 05:21:33 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id glBZTbSPMTRO; Mon, 24 Oct 2022 05:21:33 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 03E772041AF; Mon, 24 Oct 2022 05:21:31 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 21/44] sg: printk change %p to %pK Date: Sun, 23 Oct 2022 23:20:35 -0400 Message-Id: <20221024032058.14077-22-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This driver does a lot of buffer juggling in an attempt to take some of that chore away from its users. When debugging problems associated with that buffer juggling getting sensible pointer values is a major aid. So change %p to %pK. The system administrator can choose to obfuscate %pK pointers. The "pK" is also easier to search for in the code if further changes are required. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 94a07c8c7dd7..d9688bb8f864 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -864,6 +864,7 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, err = -EINVAL; goto err_out; } + SG_LOG(3, sfp, "%s: srp=0x%pK\n", __func__, srp); err = sg_rec_state_v3(sfp, srp); if (err < 0) goto err_out; @@ -1619,7 +1620,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return -ENXIO; } req_sz = vma->vm_end - vma->vm_start; - SG_LOG(3, sfp, "%s: vm_start=%p, len=%d\n", __func__, + SG_LOG(3, sfp, "%s: vm_start=%pK, len=%d\n", __func__, (void *)vma->vm_start, (int)req_sz); if (vma->vm_pgoff) return -EINVAL; /* only an offset of 0 accepted */ @@ -1667,7 +1668,7 @@ sg_rq_end_io_usercontext(struct work_struct *work) WARN_ONCE(1, "%s: sfp unexpectedly NULL\n", __func__); return; } - SG_LOG(3, sfp, "%s: srp=0x%p\n", __func__, srp); + SG_LOG(3, sfp, "%s: srp=0x%pK\n", __func__, srp); sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); @@ -1836,7 +1837,7 @@ sg_add_device_helper(struct scsi_device *scsidp) k = error; SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, - "%s: dev=%d, sdp=0x%p ++\n", __func__, k, sdp)); + "%s: dev=%d, sdp=0x%pK ++\n", __func__, k, sdp)); sprintf(sdp->name, "sg%d", k); sdp->device = scsidp; mutex_init(&sdp->open_rel_lock); @@ -1935,7 +1936,7 @@ sg_device_destroy(struct kref *kref) struct sg_device *sdp = container_of(kref, struct sg_device, d_ref); unsigned long flags; - SCSI_LOG_TIMEOUT(1, pr_info("[tid=%d] %s: sdp idx=%d, sdp=0x%p --\n", + SCSI_LOG_TIMEOUT(1, pr_info("[tid=%d] %s: sdp idx=%d, sdp=0x%pK --\n", (current ? current->pid : -1), __func__, sdp->index, sdp)); /* @@ -1966,7 +1967,7 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) return; /* only want to do following once per device */ SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, sdp->device, - "%s: 0x%p\n", __func__, sdp)); + "%s: 0x%pK\n", __func__, sdp)); read_lock_irqsave(&sdp->sfd_lock, iflags); list_for_each_entry(sfp, &sdp->sfds, sfd_entry) { @@ -2202,7 +2203,7 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) struct sg_fd *sfp = srp->parentfp; struct sg_scatter_hold *req_schp = &srp->data; - SG_LOG(4, sfp, "%s: srp=0x%p%s\n", __func__, srp, + SG_LOG(4, sfp, "%s: srp=0x%pK%s\n", __func__, srp, (srp->res_used) ? " rsv" : ""); if (!srp->sg_io_owned) { atomic_dec(&sfp->submitted); @@ -2244,7 +2245,7 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) align_sz = ALIGN(minlen, SG_DEF_SECTOR_SZ); schp->pages = kcalloc(mx_sgat_elems, ptr_sz, mask_kz); - SG_LOG(4, sfp, "%s: minlen=%d, align_sz=%d [sz=%zu, 0x%p ++]\n", + SG_LOG(4, sfp, "%s: minlen=%d, align_sz=%d [sz=%zu, 0x%pK ++]\n", __func__, minlen, align_sz, mx_sgat_elems * ptr_sz, schp->pages); if (unlikely(!schp->pages)) @@ -2259,7 +2260,7 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) schp->pages[k] = alloc_pages(mask_ap, order); if (!schp->pages[k]) goto err_out; - SG_LOG(5, sfp, "%s: k=%d, order=%d [0x%p ++]\n", __func__, k, + SG_LOG(5, sfp, "%s: k=%d, order=%d [0x%pK ++]\n", __func__, k, order, schp->pages[k]); } schp->page_order = order; @@ -2295,12 +2296,12 @@ sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp) return; for (k = 0; k < schp->num_sgat; ++k) { p = schp->pages[k]; - SG_LOG(5, sfp, "%s: pg[%d]=0x%p --\n", __func__, k, p); + SG_LOG(5, sfp, "%s: pg[%d]=0x%pK --\n", __func__, k, p); if (unlikely(!p)) continue; __free_pages(p, schp->page_order); } - SG_LOG(5, sfp, "%s: pg_order=%u, free pgs=0x%p --\n", __func__, + SG_LOG(5, sfp, "%s: pg_order=%u, free pgs=0x%pK --\n", __func__, schp->page_order, schp->pages); kfree(schp->pages); } @@ -2557,7 +2558,7 @@ sg_add_sfp(struct sg_device *sdp) } list_add_tail(&sfp->sfd_entry, &sdp->sfds); write_unlock_irqrestore(&sdp->sfd_lock, iflags); - SG_LOG(3, sfp, "%s: sfp=0x%p\n", __func__, sfp); + SG_LOG(3, sfp, "%s: sfp=0x%pK\n", __func__, sfp); if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; @@ -2567,7 +2568,7 @@ sg_add_sfp(struct sg_device *sdp) kref_get(&sdp->d_ref); __module_get(THIS_MODULE); - SG_LOG(3, sfp, "%s: success, sfp=0x%p ++\n", __func__, sfp); + SG_LOG(3, sfp, "%s: success, sfp=0x%pK ++\n", __func__, sfp); return sfp; } @@ -2610,7 +2611,7 @@ sg_remove_sfp_usercontext(struct work_struct *work) sg_remove_sgat(sfp, &sfp->reserve); } - SG_LOG(6, sfp, "%s: sfp=0x%p\n", __func__, sfp); + SG_LOG(6, sfp, "%s: sfp=0x%pK\n", __func__, sfp); kfree(sfp); scsi_device_put(sdp->device); From patchwork Mon Oct 24 03:20:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016568 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 52AF0FA373D for ; Mon, 24 Oct 2022 03:26:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230467AbiJXD0E (ORCPT ); Sun, 23 Oct 2022 23:26:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34598 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230263AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 1D2795FAFC for ; Sun, 23 Oct 2022 20:21:35 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 7E4C12041CE; Mon, 24 Oct 2022 05:21:34 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Shk854XNwKh7; Mon, 24 Oct 2022 05:21:34 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 5744A2041AF; Mon, 24 Oct 2022 05:21:33 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 22/44] sg: xarray for fds in device Date: Sun, 23 Oct 2022 23:20:36 -0400 Message-Id: <20221024032058.14077-23-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Add xarray in each sg_device object holding pointers to children. The children are sg_fd objects, each associated with an open file descriptor. The xarray replaces a doubly linked list and its access lock. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 109 +++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d9688bb8f864..c545f4ae8532 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -43,6 +43,7 @@ static char *sg_version_date = "20190606"; #include #include /* for sg_check_file_access() */ #include +#include #include #include @@ -57,7 +58,6 @@ static char *sg_version_date = "20190606"; #include "scsi_logging.h" - #define SG_ALLOW_DIO_DEF 0 #define SG_MAX_DEVS 32768 @@ -177,13 +177,13 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ }; struct sg_fd { /* holds the state of a file descriptor */ - struct list_head sfd_entry; /* member sg_device::sfds list */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ spinlock_t rq_list_lock; /* protect access to list in req_arr */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + u32 idx; /* my index within parent's sfp_arr */ atomic_t submitted; /* number inflight or awaiting read */ atomic_t waiting; /* number of requests awaiting read */ int sgat_elem_sz; /* initialized to scatter_elem_sz */ @@ -206,7 +206,6 @@ struct sg_device { /* holds the state of each scsi generic device */ wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ struct mutex open_rel_lock; /* held when in open() or release() */ struct list_head sfds; - rwlock_t sfd_lock; /* protect access to sfd list */ int max_sgat_elems; /* adapter's max number of elements in sgat */ int max_sgat_sz; /* max number of bytes in sgat list */ u32 index; /* device index number */ @@ -214,6 +213,7 @@ struct sg_device { /* holds the state of each scsi generic device */ unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ char name[DISK_NAME_LEN]; struct cdev *cdev; + struct xarray sfp_arr; struct kref d_ref; }; @@ -252,7 +252,6 @@ static struct sg_request *sg_setup_req(struct sg_fd *sfp); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); -static void sg_calc_sgat_param(struct sg_device *sdp); static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ @@ -262,7 +261,6 @@ static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #define SG_IS_DETACHING(sdp) test_bit(SG_FDEV_DETACHING, (sdp)->fdev_bm) #define SG_HAVE_EXCLUDE(sdp) test_bit(SG_FDEV_EXCLUDE, (sdp)->fdev_bm) #define SG_RS_ACTIVE(srp) (atomic_read(&(srp)->rq_st) != SG_RS_INACTIVE) -#define SG_RS_AWAIT_READ(srp) (atomic_read(&(srp)->rq_st) == SG_RS_AWAIT_RCV) /* * Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages. @@ -396,6 +394,7 @@ sg_open(struct inode *inode, struct file *filp) int min_dev = iminor(inode); int op_flags = filp->f_flags; int res; + __maybe_unused int o_count; struct sg_device *sdp; struct sg_fd *sfp; @@ -445,20 +444,18 @@ sg_open(struct inode *inode, struct file *filp) if (o_excl) set_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm); - if (atomic_read(&sdp->open_cnt) < 1) /* no existing opens */ - sg_calc_sgat_param(sdp); + o_count = atomic_inc_return(&sdp->open_cnt); sfp = sg_add_sfp(sdp); /* increments sdp->d_ref */ if (IS_ERR(sfp)) { + atomic_dec(&sdp->open_cnt); res = PTR_ERR(sfp); goto out_undo; } filp->private_data = sfp; - atomic_inc(&sdp->open_cnt); mutex_unlock(&sdp->open_rel_lock); - SG_LOG(3, sfp, "%s: minor=%d, op_flags=0x%x; %s count prior=%d%s\n", - __func__, min_dev, op_flags, "device open", - atomic_read(&sdp->open_cnt), + SG_LOG(3, sfp, "%s: minor=%d, op_flags=0x%x; %s count after=%d%s\n", + __func__, min_dev, op_flags, "device open", o_count, ((op_flags & O_NONBLOCK) ? " O_NONBLOCK" : "")); res = 0; @@ -486,26 +483,28 @@ sg_open(struct inode *inode, struct file *filp) static int sg_release(struct inode *inode, struct file *filp) { + int o_count; struct sg_device *sdp; struct sg_fd *sfp; sfp = filp->private_data; - sdp = sfp->parentdp; - SG_LOG(3, sfp, "%s: device open count prior=%d\n", __func__, - atomic_read(&sdp->open_cnt)); - if (!sdp) + sdp = sfp ? sfp->parentdp : NULL; + if (unlikely(!sdp)) return -ENXIO; mutex_lock(&sdp->open_rel_lock); + o_count = atomic_read(&sdp->open_cnt); + SG_LOG(3, sfp, "%s: open count before=%d\n", __func__, o_count); scsi_autopm_put_device(sdp->device); kref_put(&sfp->f_ref, sg_remove_sfp); - atomic_dec(&sdp->open_cnt); - /* possibly many open()s waiting on exclude clearing, start many; - * only open(O_EXCL)s wait on 0==open_cnt so only start one */ + /* + * Possibly many open()s waiting on exclude clearing, start many; + * only open(O_EXCL)'s wait when open_cnt<2 and only start one. + */ if (test_and_clear_bit(SG_FDEV_EXCLUDE, sdp->fdev_bm)) wake_up_interruptible_all(&sdp->open_wait); - else if (atomic_read(&sdp->open_cnt) == 0) + else if (o_count < 2) wake_up_interruptible(&sdp->open_wait); mutex_unlock(&sdp->open_rel_lock); return 0; @@ -1444,6 +1443,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, if (result) return result; assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, val); + if (val == 0) /* user can force recalculation */ + sg_calc_sgat_param(sdp); return 0; case BLKSECTGET: SG_LOG(3, sfp, "%s: BLKSECTGET\n", __func__); @@ -1841,11 +1842,9 @@ sg_add_device_helper(struct scsi_device *scsidp) sprintf(sdp->name, "sg%d", k); sdp->device = scsidp; mutex_init(&sdp->open_rel_lock); - INIT_LIST_HEAD(&sdp->sfds); + xa_init_flags(&sdp->sfp_arr, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); init_waitqueue_head(&sdp->open_wait); clear_bit(SG_FDEV_DETACHING, sdp->fdev_bm); - rwlock_init(&sdp->sfd_lock); - sg_calc_sgat_param(sdp); sdp->index = k; kref_init(&sdp->d_ref); error = 0; @@ -1911,6 +1910,7 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) } else pr_warn("%s: sg_sys Invalid\n", __func__); + sg_calc_sgat_param(sdp); sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d " "type %d\n", sdp->index, scsidp->type); @@ -1945,6 +1945,7 @@ sg_device_destroy(struct kref *kref) * any other cleanup. */ + xa_destroy(&sdp->sfp_arr); write_lock_irqsave(&sg_index_lock, flags); idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, flags); @@ -1957,7 +1958,7 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); struct sg_device *sdp = dev_get_drvdata(cl_dev); - unsigned long iflags; + unsigned long idx; struct sg_fd *sfp; if (!sdp) @@ -1969,13 +1970,13 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, sdp->device, "%s: 0x%pK\n", __func__, sdp)); - read_lock_irqsave(&sdp->sfd_lock, iflags); - list_for_each_entry(sfp, &sdp->sfds, sfd_entry) { + xa_for_each(&sdp->sfp_arr, idx, sfp) { + if (!sfp) + continue; wake_up_interruptible_all(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); } wake_up_interruptible_all(&sdp->open_wait); - read_unlock_irqrestore(&sdp->sfd_lock, iflags); sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index)); @@ -2520,9 +2521,11 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) static struct sg_fd * sg_add_sfp(struct sg_device *sdp) { - int rbuf_len; + int rbuf_len, res; + u32 idx; unsigned long iflags; struct sg_fd *sfp; + struct xa_limit xal; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); if (!sfp) @@ -2550,14 +2553,10 @@ sg_add_sfp(struct sg_device *sdp) atomic_set(&sfp->submitted, 0); atomic_set(&sfp->waiting, 0); - write_lock_irqsave(&sdp->sfd_lock, iflags); if (SG_IS_DETACHING(sdp)) { - write_unlock_irqrestore(&sdp->sfd_lock, iflags); kfree(sfp); return ERR_PTR(-ENODEV); } - list_add_tail(&sfp->sfd_entry, &sdp->sfds); - write_unlock_irqrestore(&sdp->sfd_lock, iflags); SG_LOG(3, sfp, "%s: sfp=0x%pK\n", __func__, sfp); if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; @@ -2566,6 +2565,20 @@ sg_add_sfp(struct sg_device *sdp) if (rbuf_len > 0) sg_build_reserve(sfp, rbuf_len); + xa_lock_irqsave(&sdp->sfp_arr, iflags); + xal.min = 0; + xal.max = atomic_read(&sdp->open_cnt); + res = __xa_alloc(&sdp->sfp_arr, &idx, sfp, xal, GFP_KERNEL); + xa_unlock_irqrestore(&sdp->sfp_arr, iflags); + if (res < 0) { + pr_warn("%s: xa_alloc(sdp) bad, o_count=%d, errno=%d\n", + __func__, xal.max, -res); + if (rbuf_len > 0) + sg_remove_sgat(sfp, &sfp->reserve); + kfree(sfp); + return ERR_PTR(res); + } + sfp->idx = idx; kref_get(&sdp->d_ref); __module_get(THIS_MODULE); SG_LOG(3, sfp, "%s: success, sfp=0x%pK ++\n", __func__, sfp); @@ -2584,9 +2597,11 @@ sg_add_sfp(struct sg_device *sdp) static void sg_remove_sfp_usercontext(struct work_struct *work) { + __maybe_unused int o_count; unsigned long iflags; - struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); struct sg_device *sdp; + struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); + struct sg_fd *e_sfp; struct sg_request *srp; if (!sfp) { @@ -2611,7 +2626,15 @@ sg_remove_sfp_usercontext(struct work_struct *work) sg_remove_sgat(sfp, &sfp->reserve); } - SG_LOG(6, sfp, "%s: sfp=0x%pK\n", __func__, sfp); + xa_lock_irqsave(&sdp->sfp_arr, iflags); + e_sfp = __xa_erase(&sdp->sfp_arr, sfp->idx); + xa_unlock_irqrestore(&sdp->sfp_arr, iflags); + if (unlikely(sfp != e_sfp)) + SG_LOG(1, sfp, "%s: xa_erase() return unexpected\n", + __func__); + o_count = atomic_dec_return(&sdp->open_cnt); + SG_LOG(3, sfp, "%s: dev o_count after=%d: sfp=0x%pK --\n", __func__, + o_count, sfp); kfree(sfp); scsi_device_put(sdp->device); @@ -2622,13 +2645,7 @@ sg_remove_sfp_usercontext(struct work_struct *work) static void sg_remove_sfp(struct kref *kref) { - unsigned long iflags; struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref); - struct sg_device *sdp = sfp->parentdp; - - write_lock_irqsave(&sdp->sfd_lock, iflags); - list_del(&sfp->sfd_entry); - write_unlock_irqrestore(&sdp->sfd_lock, iflags); INIT_WORK(&sfp->ew_fd.work, sg_remove_sfp_usercontext); schedule_work(&sfp->ew_fd.work); @@ -2943,6 +2960,7 @@ static void sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) { int k, new_interface, blen, usg; + unsigned long idx; struct sg_request *srp; struct sg_fd *fp; const struct sg_io_hdr *hp; @@ -2950,13 +2968,14 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) unsigned int ms; k = 0; - list_for_each_entry(fp, &sdp->sfds, sfd_entry) { + xa_for_each(&sdp->sfp_arr, idx, fp) { + if (!fp) + continue; k++; spin_lock(&fp->rq_list_lock); /* irqs already disabled */ - seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d\n", + seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d idx=%lu\n", k, jiffies_to_msecs(fp->timeout), - fp->reserve.buflen, - (int)fp->reserve.num_sgat); + fp->reserve.buflen, (int)fp->reserve.num_sgat, idx); seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d\n", (int)fp->cmd_q, (int)fp->force_packid, (int)fp->keep_orphan); @@ -3020,8 +3039,7 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) sdp = it ? sg_lookup_dev(it->index) : NULL; if (NULL == sdp) goto skip; - read_lock(&sdp->sfd_lock); - if (!list_empty(&sdp->sfds)) { + if (!xa_empty(&sdp->sfp_arr)) { seq_printf(s, " >>> device=%s ", sdp->name); if (SG_IS_DETACHING(sdp)) seq_puts(s, "detaching pending close "); @@ -3039,7 +3057,6 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) atomic_read(&sdp->open_cnt)); sg_proc_debug_helper(s, sdp); } - read_unlock(&sdp->sfd_lock); skip: read_unlock_irqrestore(&sg_index_lock, iflags); return 0; From patchwork Mon Oct 24 03:20:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016552 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C09CC3A59D for ; Mon, 24 Oct 2022 03:25:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230298AbiJXDZV (ORCPT ); Sun, 23 Oct 2022 23:25:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38340 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230320AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 232D65FDEB for ; Sun, 23 Oct 2022 20:21:36 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id D7ABC204237; Mon, 24 Oct 2022 05:21:35 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id G9PlU4rcN6Xz; Mon, 24 Oct 2022 05:21:35 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id AA6962041AF; Mon, 24 Oct 2022 05:21:34 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 23/44] sg: xarray for reqs in fd Date: Sun, 23 Oct 2022 23:20:37 -0400 Message-Id: <20221024032058.14077-24-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Replace the linked list and the fixed array of requests (max 16) with an xarray. The xarray (srp_arr) has two marks: one for INACTIVE state (i.e. available for re-use) requests; the other is AWAIT state which is after the internal completion point of a request and before the user space has fetched the response. Of the five states in sg_request::rq_st, two are marked. They are SG_RS_INACTIVE and SG_RS_AWAIT_RCV. This allows the request xarray (sg_fd::srp_arr) to be searched (with xa_for_each_mark) on two embedded sub-lists. The SG_RS_INACTIVE sub-list replaces the free list. The SG_RS_AWAIT_RCV sub-list contains requests that have reached their internal completion point but have not been read/ received by the user space. Add support functions for this and partially wire them up. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 329 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 232 insertions(+), 97 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c545f4ae8532..ffcd2d396c3a 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -78,7 +78,7 @@ static char *sg_version_date = "20190606"; #define SG_MAX_CDB_SIZE 252 /* Following enum contains the states of sg_request::rq_st */ -enum sg_rq_state { +enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ SG_RS_INACTIVE = 0, /* request not in use (e.g. on fl) */ SG_RS_INFLIGHT, /* active: cmd/req issued, no response yet */ SG_RS_AWAIT_RCV, /* have response from LLD, awaiting receive */ @@ -117,6 +117,11 @@ enum sg_rq_state { #define SG_FDEV_DETACHING 1 /* may be unexpected device removal */ #define SG_FDEV_LOG_SENSE 2 /* set by ioctl(SG_SET_DEBUG) */ +/* xarray 'mark's allow sub-lists within main array/list. */ +#define SG_XA_RQ_FREE XA_MARK_0 /* xarray sets+clears */ +#define SG_XA_RQ_INACTIVE XA_MARK_1 +#define SG_XA_RQ_AWAIT XA_MARK_2 + static int sg_big_buff = SG_DEF_RESERVED_SIZE; /* * This variable is accessible via /proc/scsi/sg/def_reserved_size . Each @@ -156,11 +161,11 @@ struct sg_device; /* forward declarations */ struct sg_fd; struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ - struct list_head entry; /* list entry */ struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ struct sg_io_hdr header; /* scsi command+info, see */ u8 sense_b[SCSI_SENSE_BUFFERSIZE]; u32 duration; /* cmd duration in milliseconds */ + u32 rq_idx; /* my index within parent's srp_arr */ char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ u32 rq_result; /* packed scsi request result from LLD */ @@ -179,24 +184,23 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct sg_fd { /* holds the state of a file descriptor */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ - spinlock_t rq_list_lock; /* protect access to list in req_arr */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ u32 idx; /* my index within parent's sfp_arr */ - atomic_t submitted; /* number inflight or awaiting read */ - atomic_t waiting; /* number of requests awaiting read */ + atomic_t submitted; /* number inflight or awaiting receive */ + atomic_t waiting; /* number of requests awaiting receive */ + atomic_t req_cnt; /* number of requests */ int sgat_elem_sz; /* initialized to scatter_elem_sz */ struct sg_scatter_hold reserve; /* buffer for this file descriptor */ - struct list_head rq_list; /* head of request list */ - struct fasync_struct *async_qp; /* used by asynchronous notification */ - struct sg_request req_arr[SG_MAX_QUEUE];/* use as singly-linked list */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ char mmap_called; /* 0 -> mmap() never called on this fd */ char res_in_use; /* 1 -> 'reserve' array in use */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + struct xarray srp_arr; struct kref f_ref; struct execute_work ew_fd; /* harvest all fd resources and lists */ }; @@ -276,6 +280,7 @@ static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #if IS_ENABLED(CONFIG_SCSI_LOGGING) && IS_ENABLED(CONFIG_SG_DEBUG) #define SG_LOG_BUFF_SZ 48 +#define CONFIG_SG_LOG_ACTIVE 1 #define SG_LOG(depth, sfp, fmt, a...) \ do { \ @@ -295,7 +300,7 @@ static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); } while (0) #else #define SG_LOG(depth, sfp, fmt, a...) do { } while (0) -#endif /* end of CONFIG_SCSI_LOGGING && SG_DEBUG conditional */ +#endif /* end of CONFIG_SCSI_LOGGING && CONFIG_SG_DEBUG conditional */ /* @@ -725,6 +730,115 @@ sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, return count; } +#if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) +static void +sg_rq_state_fail_msg(struct sg_fd *sfp, enum sg_rq_state exp_old_st, + enum sg_rq_state want_st, enum sg_rq_state act_old_st, + const char *fromp) +{ + const char *eaw_rs = "expected_old,actual_old,wanted rq_st"; + + if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) + SG_LOG(1, sfp, "%s: %s: %s: %s,%s,%s\n", + __func__, fromp, eaw_rs, + sg_rq_st_str(exp_old_st, false), + sg_rq_st_str(act_old_st, false), + sg_rq_st_str(want_st, false)); + else + pr_info("sg: %s: %s: %s: %d,%d,%d\n", __func__, fromp, eaw_rs, + (int)exp_old_st, (int)act_old_st, (int)want_st); +} +#endif + +static void +sg_rq_state_force(struct sg_request *srp, enum sg_rq_state new_st) +{ + bool prev, want; + struct xarray *xafp = &srp->parentfp->srp_arr; + + atomic_set(&srp->rq_st, new_st); + want = (new_st == SG_RS_AWAIT_RCV); + prev = xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_AWAIT); + if (prev != want) { + if (want) + __xa_set_mark(xafp, srp->rq_idx, SG_XA_RQ_AWAIT); + else + __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_AWAIT); + } + want = (new_st == SG_RS_INACTIVE); + prev = xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + if (prev != want) { + if (want) + __xa_set_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + else + __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + } +} + +static void +sg_rq_state_helper(struct xarray *xafp, struct sg_request *srp, int indic) +{ + if (indic & 1) /* from inactive state */ + __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + else if (indic & 2) /* to inactive state */ + __xa_set_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + + if (indic & 4) /* from await state */ + __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_AWAIT); + else if (indic & 8) /* to await state */ + __xa_set_mark(xafp, srp->rq_idx, SG_XA_RQ_AWAIT); +} + +/* Following array indexed by enum sg_rq_state, 0 means no xa mark change */ +static const int sg_rq_state_arr[] = {1, 0, 4, 0, 0}; +static const int sg_rq_state_mul2arr[] = {2, 0, 8, 0, 0}; + +/* + * This function keeps the srp->rq_st state and associated marks on the + * owning xarray's element in sync. If force is true then new_st is stored + * in srp->rq_st and xarray marks are set accordingly (and old_st is + * ignored); and 0 is returned. + * If force is false, then atomic_cmpxchg() is called. If the actual + * srp->rq_st is not old_st, then -EPROTOTYPE is returned. If the actual + * srp->rq_st is old_st then it is replaced by new_st and the xarray marks + * are setup accordingly and 0 is returned. This assumes srp_arr xarray + * spinlock is held. + */ +static int +sg_rq_state_chg(struct sg_request *srp, enum sg_rq_state old_st, + enum sg_rq_state new_st, bool force, const char *fromp) +{ + enum sg_rq_state act_old_st; + int indic; + unsigned long iflags; + struct xarray *xafp = &srp->parentfp->srp_arr; + + if (force) { + xa_lock_irqsave(xafp, iflags); + sg_rq_state_force(srp, new_st); + xa_unlock_irqrestore(xafp, iflags); + return 0; + } + indic = sg_rq_state_arr[(int)old_st] + + sg_rq_state_mul2arr[(int)new_st]; + act_old_st = (enum sg_rq_state)atomic_cmpxchg(&srp->rq_st, old_st, + new_st); + if (act_old_st != old_st) { +#if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) + if (fromp) + sg_rq_state_fail_msg(srp->parentfp, old_st, new_st, + act_old_st, fromp); +#endif + return -EPROTOTYPE; /* only used for this error type */ + } + if (indic) { + xa_lock_irqsave(xafp, iflags); + sg_rq_state_helper(xafp, srp, indic); + xa_unlock_irqrestore(xafp, iflags); + } + return 0; +} + /* * All writes and submits converge on this function to launch the SCSI * command/request (via blk_execute_rq_nowait). Returns a pointer to a @@ -763,16 +877,8 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) sg_deact_request(sfp, srp); return k; /* probably out of space --> ENOMEM */ } - if (SG_IS_DETACHING(sdp)) { - if (srp->bio) { - blk_mq_free_request(srp->rq); - srp->rq = NULL; - } - - sg_finish_scsi_blk_rq(srp); - sg_deact_request(sfp, srp); - return -ENODEV; - } + if (SG_IS_DETACHING(sdp)) + goto err_out; hp->duration = jiffies_to_msecs(jiffies); if (hp->interface_id != '\0' && /* v3 (or later) interface */ @@ -788,6 +894,21 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) srp->rq->end_io = sg_rq_end_io; blk_execute_rq_nowait(srp->rq, at_head); return 0; +err_out: + if (srp->bio) { + blk_mq_free_request(srp->rq); + srp->rq = NULL; + } + sg_finish_scsi_blk_rq(srp); + sg_deact_request(sfp, srp); + return -ENODEV; +} + +static inline int +sg_rstate_chg(struct sg_request *srp, enum sg_rq_state old_st, + enum sg_rq_state new_st) +{ + return sg_rq_state_chg(srp, old_st, new_st, false, __func__); } /* @@ -1153,12 +1274,9 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, static int srp_done(struct sg_fd *sfp, struct sg_request *srp) { - unsigned long flags; int ret; - spin_lock_irqsave(&sfp->rq_list_lock, flags); ret = srp->done; - spin_unlock_irqrestore(&sfp->rq_list_lock, flags); return ret; } @@ -1181,17 +1299,13 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, true, &srp); if (res < 0) return res; - res = wait_event_interruptible - (sfp->read_wait, srp_done(sfp, srp)); - spin_lock_irq(&sfp->rq_list_lock); + res = wait_event_interruptible(sfp->read_wait, srp_done(sfp, srp)); if (srp->done) { srp->done = 2; - spin_unlock_irq(&sfp->rq_list_lock); res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, buf); return (res < 0) ? res : 0; } srp->orphan = 1; - spin_unlock_irq(&sfp->rq_list_lock); return res; } @@ -1240,7 +1354,7 @@ static int sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p) { int result, val; - unsigned long iflags; + unsigned long idx; struct sg_request *srp; sg_req_info_t *rinfop; @@ -1248,15 +1362,17 @@ sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p) GFP_KERNEL); if (!rinfop) return -ENOMEM; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); val = 0; - list_for_each_entry(srp, &sfp->rq_list, entry) { + xa_for_each(&sfp->srp_arr, idx, srp) { + if (!srp) + continue; + if (xa_get_mark(&sfp->srp_arr, idx, SG_XA_RQ_AWAIT)) + continue; if (val >= SG_MAX_QUEUE) break; sg_fill_request_element(sfp, srp, rinfop + val); val++; } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); #ifdef CONFIG_COMPAT if (in_compat_syscall()) result = put_compat_request_table(p, rinfop); @@ -1301,7 +1417,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, int __user *ip = p; struct sg_request *srp; struct scsi_device *sdev; - unsigned long iflags; + unsigned long idx; __maybe_unused const char *pmlp = ", pass to mid-level"; SG_LOG(6, sfp, "%s: cmd=0x%x, O_NONBLOCK=%d\n", __func__, cmd_in, @@ -1324,14 +1440,15 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return 0; case SG_GET_PACK_ID: /* or tag of oldest "read"-able, -1 if none */ val = -1; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - list_for_each_entry(srp, &sfp->rq_list, entry) { + srp = NULL; + xa_for_each_marked(&sfp->srp_arr, idx, srp, SG_XA_RQ_AWAIT) { + if (!srp) + continue; if ((1 == srp->done) && (!srp->sg_io_owned)) { val = srp->header.pack_id; break; } } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); SG_LOG(3, sfp, "%s: SG_GET_PACK_ID=%d\n", __func__, val); return put_user(val, ip); case SG_GET_NUM_WAITING: @@ -1719,10 +1836,10 @@ sg_rq_end_io(struct request *rq, blk_status_t status) struct sg_request *srp = rq->end_io_data; struct sg_device *sdp; struct sg_fd *sfp; - unsigned long iflags; unsigned int ms; int resid, slen; int done = 1; + unsigned long iflags; if (WARN_ON(srp->done != 0)) return RQ_END_IO_NONE; @@ -1763,7 +1880,6 @@ sg_rq_end_io(struct request *rq, blk_status_t status) srp->rq = NULL; blk_mq_free_request(rq); - spin_lock_irqsave(&sfp->rq_list_lock, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan) srp->sg_io_owned = 0; @@ -1771,12 +1887,14 @@ sg_rq_end_io(struct request *rq, blk_status_t status) done = 0; } srp->done = done; - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (likely(done)) { /* Now wake up any sg_read() that is waiting for this * packet. */ + xa_lock_irqsave(&sfp->srp_arr, iflags); + __xa_set_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_AWAIT); + xa_unlock_irqrestore(&sfp->srp_arr, iflags); wake_up_interruptible(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kref_put(&sfp->f_ref, sg_remove_sfp); @@ -2371,15 +2489,17 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) * SG_RS_AWAIT_RCV state and SG_IS_DETACHING() is true and there is at least * one request in SG_RS_INFLIGHT state; in all other cases *busy is set false. */ + static struct sg_request * sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) { + unsigned long idx; struct sg_request *resp; - unsigned long iflags; *busy = false; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - list_for_each_entry(resp, &sfp->rq_list, entry) { + xa_for_each_marked(&sfp->srp_arr, idx, resp, SG_XA_RQ_AWAIT) { + if (!resp) + continue; /* look for requests that are not SG_IO owned */ if ((!resp->sg_io_owned) && ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { @@ -2389,14 +2509,12 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) break; case 1: /* request done; response ready to return */ resp->done = 2; /* guard against other readers */ - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return resp; case 2: /* response already being returned */ break; } } } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); return NULL; } @@ -2466,31 +2584,51 @@ sg_build_reserve(struct sg_fd *sfp, int req_size) static struct sg_request * sg_setup_req(struct sg_fd *sfp) { - int k; - unsigned long iflags; - struct sg_request *rp = sfp->req_arr; - - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - if (!list_empty(&sfp->rq_list)) { - if (!sfp->cmd_q) - goto out_unlock; - - for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { - if (!rp->parentfp) - break; + bool found = false; + int res; + unsigned long idx, iflags; + struct sg_request *rp; + struct xarray *xafp = &sfp->srp_arr; + + if (!xa_empty(xafp)) { + xa_for_each_marked(xafp, idx, rp, SG_XA_RQ_INACTIVE) { + if (!rp) + continue; + if (sg_rstate_chg(rp, SG_RS_INACTIVE, SG_RS_BUSY)) + continue; + memset(rp, 0, sizeof(*rp)); + rp->rq_idx = idx; + xa_lock_irqsave(xafp, iflags); + __xa_clear_mark(xafp, idx, SG_XA_RQ_INACTIVE); + xa_unlock_irqrestore(xafp, iflags); + found = true; + break; } - if (k >= SG_MAX_QUEUE) - goto out_unlock; } - memset(rp, 0, sizeof(struct sg_request)); + if (!found) { + rp = kzalloc(sizeof(*rp), GFP_KERNEL); + if (!rp) + return NULL; + } rp->parentfp = sfp; rp->header.duration = jiffies_to_msecs(jiffies); - list_add_tail(&rp->entry, &sfp->rq_list); - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); + if (!found) { + u32 n_idx; + struct xa_limit xal = { .max = 0, .min = 0 }; + + atomic_set(&rp->rq_st, SG_RS_BUSY); + xa_lock_irqsave(xafp, iflags); + xal.max = atomic_inc_return(&sfp->req_cnt); + res = __xa_alloc(xafp, &n_idx, rp, xal, GFP_KERNEL); + xa_unlock_irqrestore(xafp, iflags); + if (res < 0) { + pr_warn("%s: don't expect xa_alloc() to fail, errno=%d\n", + __func__, -res); + return NULL; + } + rp->rq_idx = n_idx; + } return rp; -out_unlock: - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return NULL; } static void @@ -2500,22 +2638,10 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) if (WARN_ON(!sfp || !srp)) return; - if (list_empty(&sfp->rq_list)) - return; - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - if (!list_empty(&srp->entry)) { - list_del(&srp->entry); - srp->parentfp = NULL; - } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - - /* - * If the device is detaching, wakeup any readers in case we just - * removed the last response, which would leave nothing for them to - * return other than -ENODEV. - */ - if (unlikely(SG_IS_DETACHING(sfp->parentdp))) - wake_up_interruptible_all(&sfp->read_wait); + xa_lock_irqsave(&sfp->srp_arr, iflags); + __xa_set_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_INACTIVE); + xa_unlock_irqrestore(&sfp->srp_arr, iflags); + atomic_set(&srp->rq_st, SG_RS_INACTIVE); } static struct sg_fd * @@ -2532,8 +2658,7 @@ sg_add_sfp(struct sg_device *sdp) return ERR_PTR(-ENOMEM); init_waitqueue_head(&sfp->read_wait); - spin_lock_init(&sfp->rq_list_lock); - INIT_LIST_HEAD(&sfp->rq_list); + xa_init_flags(&sfp->srp_arr, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); kref_init(&sfp->f_ref); mutex_init(&sfp->f_mutex); sfp->timeout = SG_DEFAULT_TIMEOUT; @@ -2552,6 +2677,7 @@ sg_add_sfp(struct sg_device *sdp) sfp->parentdp = sdp; atomic_set(&sfp->submitted, 0); atomic_set(&sfp->waiting, 0); + atomic_set(&sfp->req_cnt, 0); if (SG_IS_DETACHING(sdp)) { kfree(sfp); @@ -2598,11 +2724,13 @@ static void sg_remove_sfp_usercontext(struct work_struct *work) { __maybe_unused int o_count; - unsigned long iflags; + unsigned long idx, iflags; struct sg_device *sdp; struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); struct sg_fd *e_sfp; struct sg_request *srp; + struct sg_request *e_srp; + struct xarray *xafp = &sfp->srp_arr; if (!sfp) { pr_warn("sg: %s: sfp is NULL\n", __func__); @@ -2611,15 +2739,20 @@ sg_remove_sfp_usercontext(struct work_struct *work) sdp = sfp->parentdp; /* Cleanup any responses which were never read(). */ - spin_lock_irqsave(&sfp->rq_list_lock, iflags); - while (!list_empty(&sfp->rq_list)) { - srp = list_first_entry(&sfp->rq_list, struct sg_request, entry); - sg_finish_scsi_blk_rq(srp); - list_del(&srp->entry); - srp->parentfp = NULL; - } - spin_unlock_irqrestore(&sfp->rq_list_lock, iflags); - + xa_for_each(xafp, idx, srp) { + if (!srp) + continue; + if (!xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE)) + sg_finish_scsi_blk_rq(srp); + xa_lock_irqsave(xafp, iflags); + e_srp = __xa_erase(xafp, srp->rq_idx); + xa_unlock_irqrestore(xafp, iflags); + if (srp != e_srp) + SG_LOG(1, sfp, "%s: xa_erase() return unexpected\n", + __func__); + kfree(srp); + } + xa_destroy(xafp); if (sfp->reserve.buflen > 0) { SG_LOG(6, sfp, "%s: buflen=%d, num_sgat=%d\n", __func__, (int)sfp->reserve.buflen, (int)sfp->reserve.num_sgat); @@ -2708,7 +2841,9 @@ sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) return long_str ? "unknown" : "unk"; } } -#else + +#elif IS_ENABLED(CONFIG_SG_LOG_ACTIVE) + static const char * sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) { @@ -2960,7 +3095,7 @@ static void sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) { int k, new_interface, blen, usg; - unsigned long idx; + unsigned long idx, idx2; struct sg_request *srp; struct sg_fd *fp; const struct sg_io_hdr *hp; @@ -2972,7 +3107,6 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) if (!fp) continue; k++; - spin_lock(&fp->rq_list_lock); /* irqs already disabled */ seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d idx=%lu\n", k, jiffies_to_msecs(fp->timeout), fp->reserve.buflen, (int)fp->reserve.num_sgat, idx); @@ -2982,7 +3116,9 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) seq_printf(s, " submitted=%d waiting=%d\n", atomic_read(&fp->submitted), atomic_read(&fp->waiting)); - list_for_each_entry(srp, &fp->rq_list, entry) { + xa_for_each(&fp->srp_arr, idx2, srp) { + if (!srp) + continue; hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; if (srp->res_used) { @@ -3018,9 +3154,8 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) (int)srp->data.cmd_opcode, sg_rq_st_str(SG_RS_INACTIVE, false)); } - if (list_empty(&fp->rq_list)) + if (xa_empty(&fp->srp_arr)) seq_puts(s, " No requests active\n"); - spin_unlock(&fp->rq_list_lock); } } From patchwork Mon Oct 24 03:20:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016549 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 64981C3A59D for ; Mon, 24 Oct 2022 03:25:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229872AbiJXDZH (ORCPT ); Sun, 23 Oct 2022 23:25:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55460 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230253AbiJXDW5 (ORCPT ); Sun, 23 Oct 2022 23:22:57 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id DBEA45D709 for ; Sun, 23 Oct 2022 20:21:38 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 90E95204238; Mon, 24 Oct 2022 05:21:37 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id T37haa1Q5uvN; Mon, 24 Oct 2022 05:21:37 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 0C4112041AF; Mon, 24 Oct 2022 05:21:35 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, kbuild test robot Subject: [PATCH v25 24/44] sg: replace rq array with xarray Date: Sun, 23 Oct 2022 23:20:38 -0400 Message-Id: <20221024032058.14077-25-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Remove the fixed size array of 16 request elements per file descriptor and replace with the xarray added in the previous patch. All sg_request objects are now kept, available for re-use, until their owning file descriptor is closed. The sg_request deletions are in sg_remove_sfp_usercontext(). Each active sg_request object has an associated block request and a scsi_request object but they have different lifetimes. The block request and the scsi_request object are released much earlier; their lifetime is the same as it was in the v3 sg driver. The lifetime of the bio is also the same (but is stretched in a later patch). Collect various flags into bit maps: one for requests (SG_FRQ_*) and the other for file descriptors (SG_FFD_*). They join a per sg_device bit map (SG_FDEV_*) added in an earlier patch. Prior to a new sg_request object being (re-)built, information that will be placed in it uses a new struct sg_comm_wr_t object. Since the above changes touch almost every function and low level structures, this patch is big. Reviewed-by: Hannes Reinecke Reported-by: kbuild test robot Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 1479 ++++++++++++++++++++++++++++----------------- 1 file changed, 917 insertions(+), 562 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ffcd2d396c3a..7b9606d029e7 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -146,36 +146,51 @@ static struct class_interface sg_interface = { .remove_dev = sg_remove_device, }; +/* Subset of sg_io_hdr found in , has only [i] and [i->o] fields */ +struct sg_slice_hdr3 { + int interface_id; + int dxfer_direction; + u8 cmd_len; + u8 mx_sb_len; + u16 iovec_count; + unsigned int dxfer_len; + void __user *dxferp; + u8 __user *cmdp; + void __user *sbp; + unsigned int timeout; + unsigned int flags; + int pack_id; + void __user *usr_ptr; +}; + struct sg_scatter_hold { /* holding area for scsi scatter gather info */ struct page **pages; /* num_sgat element array of struct page* */ int buflen; /* capacity in bytes (dlen<=buflen) */ int dlen; /* current valid data length of this req */ u16 page_order; /* byte_len = (page_size*(2**page_order)) */ u16 num_sgat; /* actual number of scatter-gather segments */ - unsigned int sglist_len; /* size of malloc'd scatter-gather list ++ */ - char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ - u8 cmd_opcode; /* first byte of command */ }; struct sg_device; /* forward declarations */ struct sg_fd; -struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ - struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ - struct sg_io_hdr header; /* scsi command+info, see */ +struct sg_request { /* active SCSI command or inactive request */ + struct sg_scatter_hold sgat_h; /* hold buffer, perhaps scatter list */ + struct sg_slice_hdr3 s_hdr3; /* subset of sg_io_hdr */ u8 sense_b[SCSI_SENSE_BUFFERSIZE]; u32 duration; /* cmd duration in milliseconds */ + u32 rq_flags; /* hold user supplied flags */ u32 rq_idx; /* my index within parent's srp_arr */ - char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ - char orphan; /* 1 -> drop on sight, 0 -> normal */ + u32 rq_info; /* info supplied by v3 and v4 interfaces */ u32 rq_result; /* packed scsi request result from LLD */ - char sg_io_owned; /* 1 -> packet belongs to SG_IO */ - /* done protected by rq_list_lock */ - char done; /* 0->before bh, 1->before read, 2->read */ + int in_resid; /* requested-actual byte count on data-in */ + int pack_id; /* user provided packet identifier field */ + int sense_len; /* actual sense buffer length (data-in) */ atomic_t rq_st; /* request state, holds a enum sg_rq_state */ + u8 cmd_opcode; /* first byte of SCSI cdb */ u64 start_ns; /* starting point of command duration calc */ - unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ - struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ + unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ + struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ struct request *rq; /* released in sg_rq_end_io(), bio kept */ struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ struct execute_work ew_orph; /* harvest orphan request */ @@ -184,7 +199,7 @@ struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct sg_fd { /* holds the state of a file descriptor */ struct sg_device *parentdp; /* owning device */ wait_queue_head_t read_wait; /* queue read until command done */ - struct mutex f_mutex; /* protect against changes in this fd */ + struct mutex f_mutex; /* serialize ioctls on this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ u32 idx; /* my index within parent's sfp_arr */ @@ -192,15 +207,12 @@ struct sg_fd { /* holds the state of a file descriptor */ atomic_t waiting; /* number of requests awaiting receive */ atomic_t req_cnt; /* number of requests */ int sgat_elem_sz; /* initialized to scatter_elem_sz */ - struct sg_scatter_hold reserve; /* buffer for this file descriptor */ - char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ - char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ + unsigned long ffd_bm[1]; /* see SG_FFD_* defines above */ + pid_t tid; /* thread id when opened */ u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ - char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ - char mmap_called; /* 0 -> mmap() never called on this fd */ - char res_in_use; /* 1 -> 'reserve' array in use */ - struct fasync_struct *async_qp; /* used by asynchronous notification */ - struct xarray srp_arr; + struct sg_request *rsv_srp;/* one reserve request per fd */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + struct xarray srp_arr; /* xarray of sg_request object pointers */ struct kref f_ref; struct execute_work ew_fd; /* harvest all fd resources and lists */ }; @@ -223,8 +235,8 @@ struct sg_device { /* holds the state of each scsi generic device */ struct sg_comm_wr_t { /* arguments to sg_common_write() */ int timeout; - int blocking; - struct sg_request *srp; + unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ + struct sg_io_hdr *h3p; u8 *cmnd; }; @@ -232,32 +244,34 @@ struct sg_comm_wr_t { /* arguments to sg_common_write() */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); -static int sg_start_req(struct sg_request *srp, u8 *cmd); +static int sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, + int dxfer_dir); static void sg_finish_scsi_blk_rq(struct sg_request *srp); -static int sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, - int minlen); -static ssize_t sg_submit(struct sg_fd *sfp, struct file *filp, - const char __user *buf, size_t count, bool blocking, - bool read_only, bool sg_io_owned, - struct sg_request **o_srp); -static int sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp); +static int sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen); +static void sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp); +static int sg_submit(struct file *filp, struct sg_fd *sfp, + struct sg_io_hdr *hp, bool sync, + struct sg_request **o_srp); +static struct sg_request *sg_common_write(struct sg_fd *sfp, + struct sg_comm_wr_t *cwrp); static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer); -static void sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp); -static void sg_build_reserve(struct sg_fd *sfp, int req_size); -static void sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, - int size); -static void sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp); +static void sg_remove_sgat(struct sg_request *srp); static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy); -static struct sg_request *sg_setup_req(struct sg_fd *sfp); +static struct sg_request *sg_setup_req(struct sg_fd *sfp, int dxfr_len, + struct sg_comm_wr_t *cwrp); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int dev); static void sg_device_destroy(struct kref *kref); +static struct sg_request *sg_mk_srp_sgat(struct sg_fd *sfp, bool first, + int db_len); static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); +#define SG_WRITE_COUNT_LIMIT (32 * 1024 * 1024) + #define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ #define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ #define SZ_SG_REQ_INFO ((int)sizeof(struct sg_req_info)) @@ -518,6 +532,7 @@ sg_release(struct inode *inode, struct file *filp) static ssize_t sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) { + bool get_v3_hdr; int mxsize, cmd_size, input_size, res; u8 opcode; struct sg_device *sdp; @@ -540,37 +555,61 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) res = sg_allow_if_err_recovery(sdp, !!(filp->f_flags & O_NONBLOCK)); if (res) return res; - - if (count < SZ_SG_HEADER) + if (count < SZ_SG_HEADER || count > SG_WRITE_COUNT_LIMIT) return -EIO; - if (copy_from_user(ohp, p, SZ_SG_HEADER)) - return -EFAULT; - if (ohp->reply_len < 0) { /* assume this is v3 */ - struct sg_io_hdr *reinter_2p = (struct sg_io_hdr *)ohp; +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) + get_v3_hdr = (count == sizeof(struct compat_sg_io_hdr)); + else + get_v3_hdr = (count == sizeof(struct sg_io_hdr)); +#else + get_v3_hdr = (count == sizeof(struct sg_io_hdr)); +#endif + if (get_v3_hdr) { + if (get_sg_io_hdr(h3p, p)) + return -EFAULT; + } else { + if (copy_from_user(ohp, p, SZ_SG_HEADER)) + return -EFAULT; + if (ohp->reply_len < 0) { /* not v2, may be v3 */ + bool lt = false; - if (count < SZ_SG_IO_HDR) - return -EIO; - if (reinter_2p->interface_id != 'S') { +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) + lt = (count < sizeof(struct compat_sg_io_hdr)); + else + lt = (count < sizeof(struct sg_io_hdr)); +#else + lt = (count < sizeof(struct sg_io_hdr)); +#endif + if (lt) + return -EIO; + get_v3_hdr = true; + if (get_sg_io_hdr(h3p, p)) + return -EFAULT; + } + } + if (get_v3_hdr) { + /* v3 dxfer_direction_s are all negative values by design */ + if (h3p->dxfer_direction >= 0) { /* so it is not v3 */ + memcpy(ohp, h3p, count); + goto to_v2; + } + if (h3p->interface_id != 'S') { pr_info_once("sg: %s: v3 interface only here\n", __func__); return -EPERM; } - return sg_submit(sfp, filp, p, count, - !(filp->f_flags & O_NONBLOCK), false, false, - NULL); + res = sg_submit(filp, sfp, h3p, false, NULL); + return res < 0 ? res : (int)count; } +to_v2: + /* v1 and v2 interfaces processed below this point */ if (count < (SZ_SG_HEADER + 6)) - return -EIO; /* The minimum scsi command length is 6 bytes. */ - + return -EIO; /* minimum scsi command length is 6 bytes */ p += SZ_SG_HEADER; if (get_user(opcode, p)) return -EFAULT; - - srp = sg_setup_req(sfp); - if (!srp) { - SG_LOG(1, sfp, "%s: queue full\n", __func__); - return -EDOM; - } mutex_lock(&sfp->f_mutex); if (sfp->next_cmd_len > 0) { cmd_size = sfp->next_cmd_len; @@ -587,12 +626,10 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) mxsize = max_t(int, input_size, ohp->reply_len); mxsize -= SZ_SG_HEADER; input_size -= SZ_SG_HEADER; - if (input_size < 0) { - sg_deact_request(sfp, srp); - return -EIO; /* User did not pass enough bytes for this command. */ - } - h3p = &srp->header; - h3p->interface_id = '\0'; /* indicator of old interface tunnelled */ + if (input_size < 0) + return -EIO; /* Insufficient bytes passed for this command. */ + memset(h3p, 0, sizeof(*h3p)); + h3p->interface_id = '\0';/* indicate v1 or v2 interface (tunnelled) */ h3p->cmd_len = (u8)cmd_size; h3p->iovec_count = 0; h3p->mx_sb_len = 0; @@ -613,10 +650,9 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) h3p->flags = input_size; /* structure abuse ... */ h3p->pack_id = ohp->pack_id; h3p->usr_ptr = NULL; - if (copy_from_user(cmnd, p, cmd_size)) { - sg_deact_request(sfp, srp); + cmnd[0] = opcode; + if (copy_from_user(cmnd + 1, p + 1, cmd_size - 1)) return -EFAULT; - } /* * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV, * but it is possible that the app intended SG_DXFER_TO_DEV, because @@ -630,23 +666,23 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) __func__, ohp->reply_len - (int)SZ_SG_HEADER, input_size, (unsigned int)cmnd[0], current->comm); } + cwr.frq_bm[0] = 0; /* initial state clear for all req flags */ + cwr.h3p = h3p; cwr.timeout = sfp->timeout; - cwr.blocking = !(filp->f_flags & O_NONBLOCK); - cwr.srp = srp; cwr.cmnd = cmnd; - res = sg_common_write(sfp, &cwr); - return (res < 0) ? res : count; + srp = sg_common_write(sfp, &cwr); + return (IS_ERR(srp)) ? PTR_ERR(srp) : (int)count; } static inline int sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) { - if (len > sfp->reserve.buflen) - return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ + if (!xa_empty(&sfp->srp_arr)) + return -EBUSY; /* already active requests on fd */ + if (len > sfp->rsv_srp->sgat_h.buflen) + return -ENOMEM; /* MMAP_IO size must fit in reserve */ if (rq_flags & SG_FLAG_DIRECT_IO) - return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ - if (sfp->res_in_use) - return -EBUSY; /* reserve buffer already being used */ + return -EINVAL; /* not both MMAP_IO and DIRECT_IO */ return 0; } @@ -672,62 +708,40 @@ sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, return 0; } -static ssize_t -sg_submit(struct sg_fd *sfp, struct file *filp, const char __user *buf, - size_t count, bool blocking, bool read_only, bool sg_io_owned, - struct sg_request **o_srp) +static int +sg_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, + bool sync, struct sg_request **o_srp) { - int k, res, timeout; + int res, timeout; + unsigned long ul_timeout; struct sg_request *srp; - struct sg_io_hdr *hp; struct sg_comm_wr_t cwr; u8 cmnd[SG_MAX_CDB_SIZE]; - unsigned long ul_timeout; - if (count < SZ_SG_IO_HDR) - return -EINVAL; - - sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ - srp = sg_setup_req(sfp); - if (!srp) { - SG_LOG(1, sfp, "%s: queue full\n", __func__); - return -EDOM; - } - srp->sg_io_owned = sg_io_owned; - hp = &srp->header; - /* get_sg_io_hdr() is defined in block/scsi_ioctl.c */ - if (get_sg_io_hdr(hp, buf)) { - sg_deact_request(sfp, srp); - return -EFAULT; - } - if (hp->interface_id != 'S') { - sg_deact_request(sfp, srp); - return -ENOSYS; - } + /* now doing v3 blocking (sync) or non-blocking submission */ if (hp->flags & SG_FLAG_MMAP_IO) { res = sg_chk_mmap(sfp, hp->flags, hp->dxfer_len); - if (res) { - sg_deact_request(sfp, srp); + if (res) return res; - } } - ul_timeout = msecs_to_jiffies(srp->header.timeout); - timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX; + /* when v3 seen, allow cmd_q on this fd (def: no cmd_q) */ + set_bit(SG_FFD_CMD_Q, sfp->ffd_bm); + ul_timeout = msecs_to_jiffies(hp->timeout); + timeout = min_t(unsigned long, ul_timeout, INT_MAX); res = sg_fetch_cmnd(filp, sfp, hp->cmdp, hp->cmd_len, cmnd); - if (res) { - sg_deact_request(sfp, srp); + if (res) return res; - } + cwr.frq_bm[0] = 0; + __assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); + cwr.h3p = hp; cwr.timeout = timeout; - cwr.blocking = blocking; - cwr.srp = srp; cwr.cmnd = cmnd; - k = sg_common_write(sfp, &cwr); - if (k < 0) - return k; + srp = sg_common_write(sfp, &cwr); + if (IS_ERR(srp)) + return PTR_ERR(srp); if (o_srp) - *o_srp = cwr.srp; - return count; + *o_srp = srp; + return 0; } #if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) @@ -845,70 +859,69 @@ sg_rq_state_chg(struct sg_request *srp, enum sg_rq_state old_st, * sg_request object holding the request just issued or a negated errno * value twisted by ERR_PTR. */ -static int +static struct sg_request * sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) { bool at_head; - int k; + int res = 0; + int dxfr_len, dir, cmd_len; + int pack_id = SG_PACK_ID_WILDCARD; + u32 rq_flags; struct sg_device *sdp = sfp->parentdp; - struct sg_request *srp = cwrp->srp; - struct sg_io_hdr *hp = &srp->header; - - srp->data.cmd_opcode = cwrp->cmnd[0]; /* hold opcode of command */ - hp->status = 0; - hp->masked_status = 0; - hp->msg_status = 0; - hp->info = 0; - hp->host_status = 0; - hp->driver_status = 0; - hp->resid = 0; - SG_LOG(4, sfp, "%s: opcode=0x%02x, cmd_sz=%d\n", __func__, - (int)cwrp->cmnd[0], hp->cmd_len); - - if (hp->dxfer_len >= SZ_256M) { - sg_deact_request(sfp, srp); - return -EINVAL; - } - - k = sg_start_req(srp, cwrp->cmnd); - if (k) { - SG_LOG(1, sfp, "%s: start_req err=%d\n", __func__, k); - sg_finish_scsi_blk_rq(srp); - sg_deact_request(sfp, srp); - return k; /* probably out of space --> ENOMEM */ - } - if (SG_IS_DETACHING(sdp)) + struct sg_request *srp; + struct sg_io_hdr *hi_p; + + hi_p = cwrp->h3p; + dir = hi_p->dxfer_direction; + dxfr_len = hi_p->dxfer_len; + rq_flags = hi_p->flags; + pack_id = hi_p->pack_id; + if (dxfr_len >= SZ_256M) + return ERR_PTR(-EINVAL); + + srp = sg_setup_req(sfp, dxfr_len, cwrp); + if (IS_ERR(srp)) + return srp; + srp->rq_flags = rq_flags; + srp->pack_id = pack_id; + + cmd_len = hi_p->cmd_len; + memcpy(&srp->s_hdr3, hi_p, sizeof(srp->s_hdr3)); + srp->cmd_opcode = cwrp->cmnd[0];/* hold opcode of command for debug */ + SG_LOG(4, sfp, "%s: opcode=0x%02x, cdb_sz=%d, pack_id=%d\n", __func__, + (int)cwrp->cmnd[0], cmd_len, pack_id); + + res = sg_start_req(srp, cwrp->cmnd, cmd_len, dir); + if (res < 0) /* probably out of space --> -ENOMEM */ goto err_out; - - hp->duration = jiffies_to_msecs(jiffies); - if (hp->interface_id != '\0' && /* v3 (or later) interface */ - (SG_FLAG_Q_AT_TAIL & hp->flags)) - at_head = false; - else - at_head = true; - - if (!srp->sg_io_owned) - atomic_inc(&sfp->submitted); + if (unlikely(SG_IS_DETACHING(sdp))) { + res = -ENODEV; + goto err_out; + } srp->rq->timeout = cwrp->timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ + res = sg_rq_state_chg(srp, SG_RS_BUSY, SG_RS_INFLIGHT, false, + __func__); + if (res) + goto err_out; + srp->start_ns = ktime_get_boottime_ns(); + srp->duration = 0; + + if (srp->s_hdr3.interface_id == '\0') + at_head = true; /* backward compatibility: v1+v2 interfaces */ + else if (test_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm)) + /* cmd flags can override sfd setting */ + at_head = !!(srp->rq_flags & SG_FLAG_Q_AT_HEAD); + else /* this sfd is defaulting to head */ + at_head = !(srp->rq_flags & SG_FLAG_Q_AT_TAIL); + if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) + atomic_inc(&sfp->submitted); srp->rq->end_io = sg_rq_end_io; blk_execute_rq_nowait(srp->rq, at_head); - return 0; + return srp; err_out: - if (srp->bio) { - blk_mq_free_request(srp->rq); - srp->rq = NULL; - } - sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); - return -ENODEV; -} - -static inline int -sg_rstate_chg(struct sg_request *srp, enum sg_rq_state old_st, - enum sg_rq_state new_st) -{ - return sg_rq_state_chg(srp, old_st, new_st, false, __func__); + return ERR_PTR(res); } /* @@ -935,21 +948,26 @@ static int sg_copy_sense(struct sg_request *srp) { int sb_len_ret = 0; - struct sg_io_hdr *hp = &srp->header; + int scsi_stat; /* If need be, copy the sense buffer to the user space */ - if ((CHECK_CONDITION & hp->masked_status) || + scsi_stat = srp->rq_result & 0xff; + if ((scsi_stat & SAM_STAT_CHECK_CONDITION) || (srp->sense_b[0] & 0x70) == 0x70) { - int sb_len = SCSI_SENSE_BUFFERSIZE; - void __user *up = hp->sbp; - - sb_len = min_t(int, hp->mx_sb_len, sb_len); - /* Additional sense length field */ - sb_len_ret = 8 + (int)srp->sense_b[7]; - sb_len_ret = min_t(int, sb_len_ret, sb_len); - if (copy_to_user(up, srp->sense_b, sb_len_ret)) - return -EFAULT; - hp->sb_len_wr = sb_len_ret; + int sb_len = min_t(int, SCSI_SENSE_BUFFERSIZE, srp->sense_len); + int mx_sb_len = srp->s_hdr3.mx_sb_len; + void __user *up = srp->s_hdr3.sbp; + + if (up && mx_sb_len > 0) { + sb_len = min_t(int, mx_sb_len, sb_len); + /* Additional sense length field */ + sb_len_ret = 8 + (int)srp->sense_b[7]; + sb_len_ret = min_t(int, sb_len_ret, sb_len); + if (copy_to_user(up, srp->sense_b, sb_len_ret)) + sb_len_ret = -EFAULT; + } else { + sb_len_ret = 0; + } } return sb_len_ret; } @@ -958,22 +976,26 @@ static int sg_rec_state_v3(struct sg_fd *sfp, struct sg_request *srp) { int sb_len_wr; + u32 rq_res = srp->rq_result; sb_len_wr = sg_copy_sense(srp); if (sb_len_wr < 0) return sb_len_wr; + if (rq_res & SG_ML_RESULT_MSK) + srp->rq_info |= SG_INFO_CHECK; if (unlikely(SG_IS_DETACHING(sfp->parentdp))) - return -ENODEV; + srp->rq_info |= SG_INFO_DEVICE_DETACHING; return 0; } - static ssize_t sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, void __user *buf) { - int err = 0; - struct sg_io_hdr *hp = &srp->header; + int err, err2; + int rq_result = srp->rq_result; + struct sg_io_hdr hdr3; + struct sg_io_hdr *hp = &hdr3; if (in_compat_syscall()) { if (count < sizeof(struct compat_sg_io_hdr)) { @@ -986,11 +1008,23 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, } SG_LOG(3, sfp, "%s: srp=0x%pK\n", __func__, srp); err = sg_rec_state_v3(sfp, srp); - if (err < 0) - goto err_out; - if (hp->masked_status || hp->host_status || hp->driver_status) - hp->info |= SG_INFO_CHECK; - err = put_sg_io_hdr(hp, buf); + memset(hp, 0, sizeof(*hp)); + memcpy(hp, &srp->s_hdr3, sizeof(srp->s_hdr3)); + hp->sb_len_wr = srp->sense_len; + hp->info = srp->rq_info; + hp->resid = srp->in_resid; + hp->duration = srp->duration; + hp->status = rq_result & 0xff; + hp->masked_status = status_byte(rq_result); + hp->msg_status = COMMAND_COMPLETE; + hp->host_status = host_byte(rq_result); + hp->driver_status = driver_byte(rq_result); + err2 = put_sg_io_hdr(hp, buf); + err = err ? err : err2; + err2 = sg_rq_state_chg(srp, atomic_read(&srp->rq_st), SG_RS_INACTIVE, + false, __func__); + if (err2) + err = err ? err : err2; err_out: sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); @@ -1007,27 +1041,28 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, struct sg_request *srp) { int res = 0; - struct sg_io_hdr *sh3p = &srp->header; + u32 rq_result = srp->rq_result; struct sg_header *h2p; + struct sg_slice_hdr3 *sh3p; struct sg_header a_v2hdr; h2p = &a_v2hdr; memset(h2p, 0, SZ_SG_HEADER); + sh3p = &srp->s_hdr3; h2p->reply_len = (int)sh3p->timeout; h2p->pack_len = h2p->reply_len; /* old, strange behaviour */ h2p->pack_id = sh3p->pack_id; - h2p->twelve_byte = (srp->data.cmd_opcode >= 0xc0 && - sh3p->cmd_len == 12); - h2p->target_status = sh3p->masked_status; - h2p->host_status = sh3p->host_status; - h2p->driver_status = sh3p->driver_status; - if ((CHECK_CONDITION & h2p->target_status) || + h2p->twelve_byte = (srp->cmd_opcode >= 0xc0 && sh3p->cmd_len == 12); + h2p->target_status = status_byte(rq_result); + h2p->host_status = host_byte(rq_result); + h2p->driver_status = driver_byte(rq_result); + if ((CHECK_CONDITION & status_byte(rq_result)) || (srp->sense_b[0] & 0x70) == 0x70) { h2p->driver_status = DRIVER_SENSE; memcpy(h2p->sense_buffer, srp->sense_b, sizeof(h2p->sense_buffer)); } - switch (h2p->host_status) { + switch (host_byte(rq_result)) { /* * This following setting of 'result' is for backward compatibility * and is best ignored by the user who should use target, host and @@ -1051,7 +1086,7 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, h2p->result = EIO; break; case DID_ERROR: - h2p->result = (h2p->target_status == GOOD) ? 0 : EIO; + h2p->result = (status_byte(rq_result) == GOOD) ? 0 : EIO; break; default: h2p->result = EIO; @@ -1133,23 +1168,28 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: read() count=%d\n", __func__, (int)count); - if (sfp->force_packid) + if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm)) { ret = get_sg_io_pack_id(&req_pack_id, buf, count); - if (ret) - return ret; + if (ret) + return ret; + } srp = sg_find_srp_by_id(sfp, req_pack_id, &busy); if (!srp) { /* now wait on packet to arrive */ - if (non_block) + if (unlikely(!busy && SG_IS_DETACHING(sdp))) + return -ENODEV; + if (non_block) /* O_NONBLOCK or v3::flags & SGV4_FLAG_IMMED */ return -EAGAIN; ret = wait_event_interruptible(sfp->read_wait, ((srp = sg_find_srp_by_id(sfp, req_pack_id, &busy)) || (!busy && SG_IS_DETACHING(sdp)))); - if (!srp) - /* signal or detaching */ - return ret ? ret : -ENODEV; + if (unlikely(SG_IS_DETACHING(sdp))) + return -ENODEV; + if (ret) /* -ERESTARTSYS as signal hit process */ + return ret; + /* otherwise srp should be valid */ } - if (srp->header.interface_id == '\0') + if (srp->s_hdr3.interface_id == '\0') ret = sg_read_v1v2(buf, (int)count, sfp, srp); else ret = sg_receive_v3(sfp, srp, count, buf); @@ -1220,7 +1260,7 @@ sg_calc_rq_dur(const struct sg_request *srp) return (diff > (s64)U32_MAX) ? 3999999999U : (u32)diff; } -/* Return of U32_MAX means srp is inactive */ +/* Return of U32_MAX means srp is inactive state */ static u32 sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, bool *is_durp) @@ -1233,7 +1273,6 @@ sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, case SG_RS_BUSY: res = sg_calc_rq_dur(srp); break; - case SG_RS_AWAIT_RCV: case SG_RS_INACTIVE: res = srp->duration; is_dur = true; /* completion has occurred, timing finished */ @@ -1250,34 +1289,63 @@ static void sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, struct sg_req_info *rip) { - unsigned int ms; + unsigned long iflags; - rip->req_state = srp->done + 1; - rip->problem = srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; - rip->duration = sg_get_dur(srp, NULL, NULL); /* dummy */ - if (srp->done) { - rip->duration = srp->header.duration; - } else { - ms = jiffies_to_msecs(jiffies); - rip->duration = (ms > srp->header.duration) ? - (ms - srp->header.duration) : 0; - } - rip->orphan = srp->orphan; - rip->sg_io_owned = srp->sg_io_owned; - rip->pack_id = srp->header.pack_id; - rip->usr_ptr = srp->header.usr_ptr; + xa_lock_irqsave(&sfp->srp_arr, iflags); + rip->duration = sg_get_dur(srp, NULL, NULL); + if (rip->duration == U32_MAX) + rip->duration = 0; + rip->orphan = test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm); + rip->sg_io_owned = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); + rip->problem = !!(srp->rq_result & SG_ML_RESULT_MSK); + rip->pack_id = srp->pack_id; + rip->usr_ptr = srp->s_hdr3.usr_ptr; + xa_unlock_irqrestore(&sfp->srp_arr, iflags); +} +static inline bool +sg_rq_landed(struct sg_device *sdp, struct sg_request *srp) +{ + return atomic_read(&srp->rq_st) != SG_RS_INFLIGHT || + unlikely(SG_IS_DETACHING(sdp)); } +/* + * This is a blocking wait for a specific srp. When h4p is non-NULL, it is + * the blocking multiple request case + */ static int -srp_done(struct sg_fd *sfp, struct sg_request *srp) +sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, + struct sg_request *srp) { - int ret; + int res; + enum sg_rq_state sr_st; + struct sg_device *sdp = sfp->parentdp; - ret = srp->done; - return ret; + SG_LOG(3, sfp, "%s: about to wait_event...()\n", __func__); + /* usually will be woken up by sg_rq_end_io() callback */ + res = wait_event_interruptible(sfp->read_wait, + sg_rq_landed(sdp, srp)); + if (unlikely(res)) { /* -ERESTARTSYS because signal hit thread */ + set_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm); + /* orphans harvested when sfp->keep_orphan is false */ + atomic_set(&srp->rq_st, SG_RS_INFLIGHT); + SG_LOG(1, sfp, "%s: wait_event_interruptible gave %d\n", + __func__, res); + return res; + } + if (unlikely(SG_IS_DETACHING(sdp))) { + atomic_set(&srp->rq_st, SG_RS_INACTIVE); + return -ENODEV; + } + sr_st = atomic_read(&srp->rq_st); + if (unlikely(sr_st != SG_RS_AWAIT_RCV)) + return -EPROTO; /* Logic error */ + res = sg_rq_state_chg(srp, sr_st, SG_RS_BUSY, false, __func__); + if (unlikely(res)) + return res; + res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, p); + return (res < 0) ? res : 0; } /* @@ -1288,38 +1356,69 @@ static int sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, void __user *buf) { - bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE); int res; - struct sg_request *srp; + struct sg_request *srp = NULL; + u8 hu8arr[SZ_SG_IO_HDR]; + struct sg_io_hdr *h3p = (struct sg_io_hdr *)hu8arr; + SG_LOG(3, sfp, "%s: SG_IO%s\n", __func__, + ((filp->f_flags & O_NONBLOCK) ? " O_NONBLOCK ignored" : "")); res = sg_allow_if_err_recovery(sdp, false); if (res) return res; - res = sg_submit(sfp, filp, buf, SZ_SG_IO_HDR, true, read_only, - true, &srp); - if (res < 0) + if (get_sg_io_hdr(h3p, buf)) + return -EFAULT; + if (h3p->interface_id == 'S') + res = sg_submit(filp, sfp, h3p, true, &srp); + else + return -EPERM; + if (unlikely(res < 0)) + return res; + if (!srp) /* mrq case: already processed all responses */ return res; - res = wait_event_interruptible(sfp->read_wait, srp_done(sfp, srp)); - if (srp->done) { - srp->done = 2; - res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, buf); - return (res < 0) ? res : 0; - } - srp->orphan = 1; + res = sg_wait_event_srp(filp, sfp, buf, srp); + if (res) + SG_LOG(1, sfp, "%s: %s=0x%pK state: %s\n", __func__, + "unexpected srp", srp, + sg_rq_st_str(atomic_read(&srp->rq_st), false)); return res; } +/* + * First normalize want_rsv_sz to be >= sfp->sgat_elem_sz and + * <= max_segment_size. Exit if that is the same as old size; otherwise + * create a new sg_scatter_hold object and swap it with existing reserve + * request's sg_scatter_hold object. + */ static int sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) { - if (want_rsv_sz != sfp->reserve.buflen) { - if (sfp->mmap_called || - sfp->res_in_use) { - return -EBUSY; - } - sg_remove_sgat(sfp, &sfp->reserve); - sg_build_reserve(sfp, want_rsv_sz); - } + int new_sz, blen, res; + unsigned long iflags; + struct sg_scatter_hold n_schp, o_schp; + struct sg_request *srp; + struct xarray *xafp = &sfp->srp_arr; + + srp = sfp->rsv_srp; + if (!srp) + return -EPROTO; + new_sz = min_t(int, want_rsv_sz, sfp->parentdp->max_sgat_sz); + new_sz = max_t(int, new_sz, sfp->sgat_elem_sz); + blen = srp->sgat_h.buflen; + SG_LOG(3, sfp, "%s: was=%d, ask=%d, new=%d (sgat_elem_sz=%d)\n", + __func__, blen, want_rsv_sz, new_sz, sfp->sgat_elem_sz); + if (blen == new_sz) + return 0; + res = sg_mk_sgat(&n_schp, sfp, new_sz); + if (res) + return res; + + xa_lock_irqsave(xafp, iflags); + /* (ugly) structured assignment swap */ + o_schp = srp->sgat_h; + srp->sgat_h = n_schp; + xa_unlock_irqrestore(xafp, iflags); + sg_remove_sgat_helper(sfp, &o_schp); return 0; } @@ -1350,26 +1449,43 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o, } #endif +/* + * For backward compatibility, output SG_MAX_QUEUE sg_req_info objects. First + * fetch from the active list then, if there is still room, from the free + * list. Some of the trailing elements may be empty which is indicated by all + * fields being zero. Any requests beyond SG_MAX_QUEUE are ignored. + */ static int sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p) { - int result, val; + int k, result, val; unsigned long idx; struct sg_request *srp; - sg_req_info_t *rinfop; + struct sg_req_info *rinfop; - rinfop = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, - GFP_KERNEL); + SG_LOG(3, sfp, "%s: SG_GET_REQUEST_TABLE\n", __func__); + k = SG_MAX_QUEUE; + rinfop = kcalloc(k, SZ_SG_REQ_INFO, GFP_KERNEL); if (!rinfop) return -ENOMEM; val = 0; xa_for_each(&sfp->srp_arr, idx, srp) { if (!srp) continue; - if (xa_get_mark(&sfp->srp_arr, idx, SG_XA_RQ_AWAIT)) + if (val >= SG_MAX_QUEUE) + break; + if (xa_get_mark(&sfp->srp_arr, idx, SG_XA_RQ_INACTIVE)) + continue; + sg_fill_request_element(sfp, srp, rinfop + val); + val++; + } + xa_for_each(&sfp->srp_arr, idx, srp) { + if (!srp) continue; if (val >= SG_MAX_QUEUE) break; + if (!xa_get_mark(&sfp->srp_arr, idx, SG_XA_RQ_INACTIVE)) + continue; sg_fill_request_element(sfp, srp, rinfop + val); val++; } @@ -1436,16 +1552,13 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, result = get_user(val, ip); if (result) return result; - sfp->force_packid = val ? 1 : 0; + assign_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm, !!val); return 0; case SG_GET_PACK_ID: /* or tag of oldest "read"-able, -1 if none */ val = -1; - srp = NULL; xa_for_each_marked(&sfp->srp_arr, idx, srp, SG_XA_RQ_AWAIT) { - if (!srp) - continue; - if ((1 == srp->done) && (!srp->sg_io_owned)) { - val = srp->header.pack_id; + if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) { + val = srp->pack_id; break; } } @@ -1458,44 +1571,48 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, sdp->max_sgat_sz); return put_user(sdp->max_sgat_sz, ip); case SG_SET_RESERVED_SIZE: - mutex_lock(&sfp->f_mutex); result = get_user(val, ip); if (!result) { if (val >= 0 && val <= (1024 * 1024 * 1024)) { + mutex_lock(&sfp->f_mutex); result = sg_set_reserved_sz(sfp, val); + mutex_unlock(&sfp->f_mutex); } else { SG_LOG(3, sfp, "%s: invalid size\n", __func__); result = -EINVAL; } } - mutex_unlock(&sfp->f_mutex); return result; case SG_GET_RESERVED_SIZE: - val = min_t(int, sfp->reserve.buflen, - max_sectors_bytes(sdev->request_queue)); + mutex_lock(&sfp->f_mutex); + val = min_t(int, sfp->rsv_srp->sgat_h.buflen, + sdp->max_sgat_sz); + mutex_unlock(&sfp->f_mutex); SG_LOG(3, sfp, "%s: SG_GET_RESERVED_SIZE=%d\n", __func__, val); - return put_user(val, ip); + result = put_user(val, ip); + return result; case SG_SET_COMMAND_Q: SG_LOG(3, sfp, "%s: SG_SET_COMMAND_Q\n", __func__); result = get_user(val, ip); if (result) return result; - sfp->cmd_q = val ? 1 : 0; + assign_bit(SG_FFD_CMD_Q, sfp->ffd_bm, !!val); return 0; case SG_GET_COMMAND_Q: SG_LOG(3, sfp, "%s: SG_GET_COMMAND_Q\n", __func__); - return put_user((int) sfp->cmd_q, ip); + return put_user(test_bit(SG_FFD_CMD_Q, sfp->ffd_bm), ip); case SG_SET_KEEP_ORPHAN: SG_LOG(3, sfp, "%s: SG_SET_KEEP_ORPHAN\n", __func__); result = get_user(val, ip); if (result) return result; - sfp->keep_orphan = val; + assign_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm, !!val); return 0; case SG_GET_KEEP_ORPHAN: SG_LOG(3, sfp, "%s: SG_GET_KEEP_ORPHAN\n", __func__); - return put_user((int) sfp->keep_orphan, ip); + return put_user(test_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm), + ip); case SG_GET_VERSION_NUM: SG_LOG(3, sfp, "%s: SG_GET_VERSION_NUM\n", __func__); return put_user(sg_version_num, ip); @@ -1550,6 +1667,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return put_user(val, ip); case SG_EMULATED_HOST: SG_LOG(3, sfp, "%s: SG_EMULATED_HOST\n", __func__); + if (unlikely(SG_IS_DETACHING(sdp))) + return -ENODEV; return put_user(sdev->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: SG_LOG(3, sfp, "%s: SCSI_IOCTL_SEND_COMMAND\n", __func__); @@ -1559,7 +1678,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, result = get_user(val, ip); if (result) return result; - assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, val); + assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, !!val); if (val == 0) /* user can force recalculation */ sg_calc_sgat_param(sdp); return 0; @@ -1603,7 +1722,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, break; } result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY); - if (result) + if (unlikely(result)) return result; return -ENOIOCTLCMD; } @@ -1643,7 +1762,7 @@ sg_poll(struct file *filp, poll_table * wait) if (unlikely(SG_IS_DETACHING(sfp->parentdp))) p_res |= EPOLLHUP; - else if (likely(sfp->cmd_q)) + else if (likely(test_bit(SG_FFD_CMD_Q, sfp->ffd_bm))) p_res |= EPOLLOUT | EPOLLWRNORM; else if (atomic_read(&sfp->submitted) == 0) p_res |= EPOLLOUT | EPOLLWRNORM; @@ -1665,9 +1784,10 @@ static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { int k, length; - unsigned long offset, len, sa; + unsigned long offset, len, sa, iflags; struct vm_area_struct *vma = vmf->vma; struct sg_scatter_hold *rsv_schp; + struct sg_request *srp; struct sg_device *sdp; struct sg_fd *sfp; const char *nbp = "==NULL, bad"; @@ -1686,12 +1806,18 @@ sg_vma_fault(struct vm_fault *vmf) SG_LOG(1, sfp, "%s: device detaching\n", __func__); goto out_err; } - rsv_schp = &sfp->reserve; + srp = sfp->rsv_srp; + if (!srp) { + SG_LOG(1, sfp, "%s: srp%s\n", __func__, nbp); + goto out_err; + } + xa_lock_irqsave(&sfp->srp_arr, iflags); + rsv_schp = &srp->sgat_h; offset = vmf->pgoff << PAGE_SHIFT; if (offset >= (unsigned int)rsv_schp->buflen) { SG_LOG(1, sfp, "%s: offset[%lu] >= rsv.buflen\n", __func__, offset); - goto out_err; + goto out_err_unlock; } sa = vma->vm_start; SG_LOG(3, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, sa, offset); @@ -1704,6 +1830,7 @@ sg_vma_fault(struct vm_fault *vmf) struct page *pp; pp = rsv_schp->pages[k]; + xa_unlock_irqrestore(&sfp->srp_arr, iflags); page = nth_page(pp, offset >> PAGE_SHIFT); get_page(page); /* increment page count */ vmf->page = page; @@ -1712,6 +1839,8 @@ sg_vma_fault(struct vm_fault *vmf) sa += len; offset -= len; } +out_err_unlock: + xa_unlock_irqrestore(&sfp->srp_arr, iflags); out_err: return VM_FAULT_SIGBUS; } @@ -1726,9 +1855,10 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) { int k, length; int ret = 0; - unsigned long req_sz, len, sa; + unsigned long req_sz, len, sa, iflags; struct sg_scatter_hold *rsv_schp; struct sg_fd *sfp; + struct sg_request *srp; if (!filp || !vma) return -ENXIO; @@ -1744,11 +1874,13 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return -EINVAL; /* only an offset of 0 accepted */ /* Check reserve request is inactive and has large enough buffer */ mutex_lock(&sfp->f_mutex); - if (sfp->res_in_use) { + srp = sfp->rsv_srp; + xa_lock_irqsave(&sfp->srp_arr, iflags); + if (SG_RS_ACTIVE(srp)) { ret = -EBUSY; goto out; } - rsv_schp = &sfp->reserve; + rsv_schp = &srp->sgat_h; if (req_sz > (unsigned long)rsv_schp->buflen) { ret = -ENOMEM; goto out; @@ -1761,11 +1893,12 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) sa += len; } - sfp->mmap_called = 1; + set_bit(SG_FFD_MMAP_CALLED, sfp->ffd_bm); vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; out: + xa_unlock_irqrestore(&sfp->srp_arr, iflags); mutex_unlock(&sfp->f_mutex); return ret; } @@ -1787,8 +1920,10 @@ sg_rq_end_io_usercontext(struct work_struct *work) return; } SG_LOG(3, sfp, "%s: srp=0x%pK\n", __func__, srp); - sg_finish_scsi_blk_rq(srp); - sg_deact_request(sfp, srp); + if (test_bit(SG_FRQ_DEACT_ORPHAN, srp->frq_bm)) { + sg_finish_scsi_blk_rq(srp); /* clean up orphan case */ + sg_deact_request(sfp, srp); + } kref_put(&sfp->f_ref, sg_remove_sfp); } @@ -1826,83 +1961,88 @@ sg_check_sense(struct sg_device *sdp, struct sg_request *srp, int sense_len) } /* - * This function is a "bottom half" handler that is called by the mid - * level when a command is completed (or has failed). + * This "bottom half" (soft interrupt) handler is called by the mid-level + * when a request has completed or failed. This callback is registered in a + * blk_execute_rq_nowait() call in the sg_common_write(). For ioctl(SG_IO) + * (sync) usage, sg_ctl_sg_io() waits to be woken up by this callback. */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status) { + enum sg_rq_state rqq_state = SG_RS_AWAIT_RCV; + int a_resid, slen; struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); struct sg_request *srp = rq->end_io_data; struct sg_device *sdp; struct sg_fd *sfp; - unsigned int ms; - int resid, slen; - int done = 1; - unsigned long iflags; - if (WARN_ON(srp->done != 0)) + if (!srp) { + WARN_ONCE("%s: srp unexpectedly NULL\n", __func__); return RQ_END_IO_NONE; - + } + if (WARN_ON(atomic_read(&srp->rq_st) != SG_RS_INFLIGHT)) { + pr_warn("%s: bad rq_st=%d\n", __func__, + atomic_read(&srp->rq_st)); + goto early_err; + } sfp = srp->parentfp; - if (WARN_ON(sfp == NULL)) - return RQ_END_IO_NONE; - + if (unlikely(!sfp)) { + WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__); + goto early_err; + } sdp = sfp->parentdp; if (unlikely(SG_IS_DETACHING(sdp))) pr_info("%s: device detaching\n", __func__); - resid = scmd->resid_len; - srp->header.resid = resid; - + srp->rq_result = scmd->result; slen = min_t(int, scmd->sense_len, SCSI_SENSE_BUFFERSIZE); + a_resid = scmd->resid_len; + + if (a_resid) + srp->in_resid = a_resid; - SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, - srp->header.pack_id, srp->rq_result); - ms = jiffies_to_msecs(jiffies); - srp->header.duration = (ms > srp->header.duration) ? - (ms - srp->header.duration) : 0; - if (srp->rq_result != 0 && slen > 0) + SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->pack_id, + srp->rq_result); + srp->duration = sg_calc_rq_dur(srp); + if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0)) sg_check_sense(sdp, srp, slen); if (slen > 0) memcpy(srp->sense_b, scmd->sense_buffer, slen); - - /* Rely on write phase to clean out srp status values, so no "else" */ - - if (!srp->sg_io_owned) + srp->sense_len = slen; + if (unlikely(test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm))) { + if (test_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm)) { + clear_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); + } else { + rqq_state = SG_RS_BUSY; + set_bit(SG_FRQ_DEACT_ORPHAN, srp->frq_bm); + } + } + if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) atomic_inc(&sfp->waiting); + if (unlikely(sg_rq_state_chg(srp, SG_RS_INFLIGHT, rqq_state, + false, __func__))) + pr_warn("%s: can't set rq_st\n", __func__); /* - * Free the request as soon as it is complete so that its resources - * can be reused without waiting for userspace to read() the - * result. But keep the associated bio (if any) around until - * blk_rq_unmap_user() can be called from user context. + * Free the mid-level resources apart from the bio (if any). The bio's + * blk_rq_unmap_user() can be called later from user context. */ srp->rq = NULL; blk_mq_free_request(rq); - if (unlikely(srp->orphan)) { - if (sfp->keep_orphan) - srp->sg_io_owned = 0; - else - done = 0; - } - srp->done = done; - - if (likely(done)) { - /* Now wake up any sg_read() that is waiting for this - * packet. - */ - xa_lock_irqsave(&sfp->srp_arr, iflags); - __xa_set_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_AWAIT); - xa_unlock_irqrestore(&sfp->srp_arr, iflags); + if (likely(rqq_state == SG_RS_AWAIT_RCV)) { + /* Wake any sg_read()/ioctl(SG_IORECEIVE) awaiting this req */ wake_up_interruptible(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kref_put(&sfp->f_ref, sg_remove_sfp); - } else { + } else { /* clean up orphaned request that aren't being kept */ INIT_WORK(&srp->ew_orph.work, sg_rq_end_io_usercontext); schedule_work(&srp->ew_orph.work); } return RQ_END_IO_NONE; + +early_err: + srp->rq = NULL; + return RQ_END_IO_FREE; } static const struct file_operations sg_fops = { @@ -1928,9 +2068,9 @@ static struct sg_device * sg_add_device_helper(struct scsi_device *scsidp) { struct sg_device *sdp; - unsigned long iflags; int error; u32 k; + unsigned long iflags; sdp = kzalloc(sizeof(*sdp), GFP_KERNEL); if (!sdp) @@ -1948,7 +2088,7 @@ sg_add_device_helper(struct scsi_device *scsidp) error = -ENODEV; } else { sdev_printk(KERN_WARNING, scsidp, - "%s: idr alloc sg_device failure: %d\n", + "%s: idr allocation sg_device failure: %d\n", __func__, error); } goto out_unlock; @@ -2170,6 +2310,7 @@ init_sg(void) SG_MAX_DEVS, "sg"); if (rc) return rc; + pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n", "sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR, sg_version_date); @@ -2186,6 +2327,7 @@ init_sg(void) } class_destroy(sg_sysfs_class); register_sg_sysctls(); + err_out_unreg: unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); return rc; @@ -2205,6 +2347,17 @@ exit_sg(void) idr_destroy(&sg_index_idr); } +static inline bool +sg_chk_dio_allowed(struct sg_device *sdp, struct sg_request *srp, + int iov_count, int dir) +{ + if (sg_allow_dio && (srp->rq_flags & SG_FLAG_DIRECT_IO)) { + if (dir != SG_DXFER_UNKNOWN && !iov_count) + return true; + } + return false; +} + static void sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, struct rq_map_data *mdp) @@ -2218,24 +2371,33 @@ sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, } static int -sg_start_req(struct sg_request *srp, unsigned char *cmd) +sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, int dxfer_dir) { - int res; + bool reserved, us_xfer; + int res = 0; + int dxfer_len = 0; + int r0w = READ; + unsigned int iov_count = 0; + void __user *up; struct request *rq; - struct sg_fd *sfp = srp->parentfp; - struct sg_io_hdr *hp = &srp->header; - int dxfer_len = (int) hp->dxfer_len; - int dxfer_dir = hp->dxfer_direction; - unsigned int iov_count = hp->iovec_count; - struct sg_scatter_hold *req_schp = &srp->data; - struct sg_scatter_hold *rsv_schp = &sfp->reserve; - struct request_queue *q = sfp->parentdp->device->request_queue; - struct rq_map_data *md, map_data; - int r0w = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; struct scsi_cmnd *scmd; + struct sg_fd *sfp = srp->parentfp; + struct sg_device *sdp; + struct sg_scatter_hold *req_schp; + struct request_queue *q; + struct rq_map_data *md = (void *)srp; /* want any non-NULL value */ + __maybe_unused const char *cp = ""; + struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; + struct rq_map_data map_data; + sdp = sfp->parentdp; + up = sh3p->dxferp; + dxfer_len = (int)sh3p->dxfer_len; + iov_count = sh3p->iovec_count; + r0w = dxfer_dir == SG_DXFER_TO_DEV ? WRITE : READ; SG_LOG(4, sfp, "%s: dxfer_len=%d, data-%s\n", __func__, dxfer_len, (r0w ? "OUT" : "IN")); + q = sdp->device->request_queue; /* * NOTE @@ -2248,101 +2410,108 @@ sg_start_req(struct sg_request *srp, unsigned char *cmd) * do not want to use BLK_MQ_REQ_NOWAIT here because userspace might * not expect an EWOULDBLOCK from this condition. */ - rq = scsi_alloc_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ? - REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + rq = scsi_alloc_request(q, (r0w ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN), 0); if (IS_ERR(rq)) return PTR_ERR(rq); + /* current sg_request protected by SG_RS_BUSY state */ scmd = blk_mq_rq_to_pdu(rq); - - if (hp->cmd_len > sizeof(scmd->cmnd)) { - blk_mq_free_request(rq); - return -EINVAL; - } - - memcpy(scmd->cmnd, cmd, hp->cmd_len); - scmd->cmd_len = hp->cmd_len; - srp->rq = rq; - rq->end_io_data = srp; - scmd->allowed = SG_DEFAULT_RETRIES; - if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) - return 0; - - if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO && - dxfer_dir != SG_DXFER_UNKNOWN && !iov_count && - blk_rq_aligned(q, (unsigned long)hp->dxferp, dxfer_len)) + memcpy(scmd->cmnd, cmd, cmd_len); + scmd->cmd_len = cmd_len; + us_xfer = !(srp->rq_flags & SG_FLAG_NO_DXFER); + assign_bit(SG_FRQ_NO_US_XFER, srp->frq_bm, !us_xfer); + reserved = (sfp->rsv_srp == srp); + rq->end_io_data = srp; + scmd->retries = SG_DEFAULT_RETRIES; + req_schp = &srp->sgat_h; + + if (dxfer_len <= 0 || dxfer_dir == SG_DXFER_NONE) { + SG_LOG(4, sfp, "%s: no data xfer [0x%pK]\n", __func__, srp); + set_bit(SG_FRQ_NO_US_XFER, srp->frq_bm); + goto fini; /* path of reqs with no din nor dout */ + } else if (sg_chk_dio_allowed(sdp, srp, iov_count, dxfer_dir) && + blk_rq_aligned(q, (unsigned long)up, dxfer_len)) { + srp->rq_info |= SG_INFO_DIRECT_IO; md = NULL; - else + if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) + cp = "direct_io, "; + } else { /* normal IO and failed conditions for dio path */ md = &map_data; + } - if (md) { - mutex_lock(&sfp->f_mutex); - if (dxfer_len <= rsv_schp->buflen && - !sfp->res_in_use) { - sfp->res_in_use = 1; - sg_link_reserve(sfp, srp, dxfer_len); - } else if (hp->flags & SG_FLAG_MMAP_IO) { - res = -EBUSY; /* sfp->res_in_use == 1 */ - if (dxfer_len > rsv_schp->buflen) - res = -ENOMEM; - mutex_unlock(&sfp->f_mutex); - return res; - } else { - res = sg_mk_sgat(req_schp, sfp, dxfer_len); - if (res) { - mutex_unlock(&sfp->f_mutex); - return res; - } + if (likely(md)) { /* normal, "indirect" IO */ + if (unlikely((srp->rq_flags & SG_FLAG_MMAP_IO))) { + /* mmap IO must use and fit in reserve request */ + if (!reserved || dxfer_len > req_schp->buflen) + res = reserved ? -ENOMEM : -EBUSY; + } else if (req_schp->buflen == 0) { + int up_sz = max_t(int, dxfer_len, sfp->sgat_elem_sz); + + res = sg_mk_sgat(&srp->sgat_h, sfp, up_sz); } - mutex_unlock(&sfp->f_mutex); + if (res) + goto fini; - sg_set_map_data(req_schp, !!hp->dxferp, md); + sg_set_map_data(req_schp, !!up, md); md->from_user = (dxfer_dir == SG_DXFER_TO_FROM_DEV); } - res = blk_rq_map_user_io(rq, md, hp->dxferp, hp->dxfer_len, - GFP_ATOMIC, iov_count, iov_count, 1, r0w); - if (!res) { + res = blk_rq_map_user_io(rq, md, up, dxfer_len, GFP_ATOMIC, + iov_count, iov_count, 1, r0w); +fini: + if (unlikely(res)) { /* failure, free up resources */ + srp->rq = NULL; + SG_LOG(1, sfp, "%s: blk_rq_map_user_io() failed, iov_count=%d\n", + __func__, iov_count); + } else { srp->bio = rq->bio; - - if (!md) { - req_schp->dio_in_use = 1; - hp->info |= SG_INFO_DIRECT_IO; - } } + SG_LOG((res ? 1 : 4), sfp, "%s: %s res=%d [0x%pK]\n", __func__, cp, + res, srp); return res; } +/* + * Clean up mid-level and block layer resources of finished request. Sometimes + * blk_rq_unmap_user() returns -4 (-EINTR) and this is why: "If we're in a + * workqueue, the request is orphaned, so don't copy into a random user + * address space, just free and return -EINTR so user space doesn't expect + * any data." [block/bio.c] + */ static void sg_finish_scsi_blk_rq(struct sg_request *srp) { int ret; - struct sg_fd *sfp = srp->parentfp; - struct sg_scatter_hold *req_schp = &srp->data; + struct request *rq = srp->rq; SG_LOG(4, sfp, "%s: srp=0x%pK%s\n", __func__, srp, - (srp->res_used) ? " rsv" : ""); - if (!srp->sg_io_owned) { + (srp->parentfp->rsv_srp == srp) ? " rsv" : ""); + if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) { atomic_dec(&sfp->submitted); atomic_dec(&sfp->waiting); } if (srp->bio) { - ret = blk_rq_unmap_user(srp->bio); - if (ret) /* -EINTR (-4) can be ignored */ - SG_LOG(6, sfp, "%s: blk_rq_unmap_user() --> %d\n", - __func__, ret); + bool us_xfer = !test_bit(SG_FRQ_NO_US_XFER, srp->frq_bm); + + if (us_xfer) { + ret = blk_rq_unmap_user(srp->bio); + if (ret) { /* -EINTR (-4) can be ignored */ + SG_LOG(6, sfp, + "%s: blk_rq_unmap_user() --> %d\n", + __func__, ret); + } + } srp->bio = NULL; } + /* In worst case READ data returned to user space by this point */ - if (srp->rq) - blk_mq_free_request(srp->rq); - - if (srp->res_used) - sg_unlink_reserve(sfp, srp); - else - sg_remove_sgat(sfp, req_schp); + /* Expect blk_mq_free_request(rq) already called in sg_rq_end_io() */ + if (rq) { /* blk_get_request() may have failed */ + srp->rq = NULL; + blk_mq_free_request(rq); + } } static int @@ -2364,9 +2533,8 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) align_sz = ALIGN(minlen, SG_DEF_SECTOR_SZ); schp->pages = kcalloc(mx_sgat_elems, ptr_sz, mask_kz); - SG_LOG(4, sfp, "%s: minlen=%d, align_sz=%d [sz=%zu, 0x%pK ++]\n", - __func__, minlen, align_sz, mx_sgat_elems * ptr_sz, - schp->pages); + SG_LOG(4, sfp, "%s: minlen=%d [sz=%zu, 0x%pK ++]\n", __func__, minlen, + mx_sgat_elems * ptr_sz, schp->pages); if (unlikely(!schp->pages)) return -ENOMEM; elem_sz = sfp->sgat_elem_sz; /* power of 2 and >= PAGE_SIZE */ @@ -2427,13 +2595,15 @@ sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp) /* Remove the data (possibly a sgat list) held by srp, not srp itself */ static void -sg_remove_sgat(struct sg_fd *sfp, struct sg_scatter_hold *schp) +sg_remove_sgat(struct sg_request *srp) { + struct sg_scatter_hold *schp = &srp->sgat_h; /* care: remove own data */ + struct sg_fd *sfp = srp->parentfp; + SG_LOG(4, sfp, "%s: num_sgat=%d%s\n", __func__, schp->num_sgat, - ((sfp ? (&sfp->reserve == schp) : false) ? + ((srp->parentfp ? (sfp->rsv_srp == srp) : false) ? " [rsv]" : "")); - if (!schp->dio_in_use) - sg_remove_sgat_helper(sfp, schp); + sg_remove_sgat_helper(sfp, schp); memset(schp, 0, sizeof(*schp)); /* zeros buflen and dlen */ } @@ -2448,7 +2618,7 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) { int k, num, res; struct page *pgp; - struct sg_scatter_hold *schp = &srp->data; + struct sg_scatter_hold *schp = &srp->sgat_h; SG_LOG(4, srp->parentfp, "%s: num_xfer=%d\n", __func__, num_xfer); if (unlikely(!outp || num_xfer <= 0)) @@ -2462,11 +2632,11 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) break; } if (num > num_xfer) { - if (__copy_to_user(outp, page_address(pgp), num_xfer)) + if (copy_to_user(outp, page_address(pgp), num_xfer)) res = -EFAULT; break; } else { - if (__copy_to_user(outp, page_address(pgp), num)) { + if (copy_to_user(outp, page_address(pgp), num)) { res = -EFAULT; break; } @@ -2493,144 +2663,284 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) static struct sg_request * sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) { + __maybe_unused bool is_bad_st = false; + __maybe_unused enum sg_rq_state bad_sr_st = SG_RS_INACTIVE; + bool search_for_1 = (pack_id != SG_PACK_ID_WILDCARD); + int res; + int num_waiting = atomic_read(&sfp->waiting); unsigned long idx; - struct sg_request *resp; + struct sg_request *srp = NULL; + struct xarray *xafp = &sfp->srp_arr; *busy = false; - xa_for_each_marked(&sfp->srp_arr, idx, resp, SG_XA_RQ_AWAIT) { - if (!resp) - continue; - /* look for requests that are not SG_IO owned */ - if ((!resp->sg_io_owned) && - ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { - switch (resp->done) { - case 0: /* request active */ + if (num_waiting < 1) + return NULL; + if (unlikely(search_for_1)) { + xa_for_each_marked(xafp, idx, srp, SG_XA_RQ_AWAIT) { + if (test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) + continue; + if (srp->pack_id != pack_id) + continue; + res = sg_rq_state_chg(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY, + false, __func__); + if (likely(res == 0)) + goto good; + /* else another caller got it, move on */ + if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) { + is_bad_st = true; + bad_sr_st = atomic_read(&srp->rq_st); + } + break; + } + } else { /* search for any request is more likely */ + xa_for_each_marked(xafp, idx, srp, SG_XA_RQ_AWAIT) { + if (test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) + continue; + res = sg_rq_state_chg(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY, + false, __func__); + if (likely(res == 0)) + goto good; + } + } + /* here if one of above loops does _not_ find a match */ + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) { + xa_for_each(xafp, idx, srp) { + if (!srp) + continue; + if (atomic_read(&srp->rq_st) == SG_RS_INFLIGHT) { *busy = true; - break; - case 1: /* request done; response ready to return */ - resp->done = 2; /* guard against other readers */ - return resp; - case 2: /* response already being returned */ - break; + return NULL; } } } + if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) { + if (search_for_1) { + const char *cptp = "pack_id="; + + if (is_bad_st) + SG_LOG(1, sfp, "%s: %s%d wrong state: %s\n", + __func__, cptp, pack_id, + sg_rq_st_str(bad_sr_st, true)); + else + SG_LOG(6, sfp, "%s: %s%d not awaiting read\n", + __func__, cptp, pack_id); + } + } return NULL; +good: + SG_LOG(6, sfp, "%s: %s%d found [srp=0x%pK]\n", __func__, "pack_id=", + pack_id, srp); + return srp; } -static void -sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) +/* + * Makes a new sg_request object. If 'first' is set then use GFP_KERNEL which + * may take time but has improved chance of success, otherwise use GFP_ATOMIC. + * Note that basic initialization is done but srp is not added to either sfp + * list. On error returns twisted negated errno value (not NULL). + */ +static struct sg_request * +sg_mk_srp(struct sg_fd *sfp, bool first) { - struct sg_scatter_hold *req_schp = &srp->data; - struct sg_scatter_hold *rsv_schp = &sfp->reserve; - int k, num, rem; - - srp->res_used = 1; - SG_LOG(4, sfp, "%s: size=%d\n", __func__, size); - rem = size; - - num = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->num_sgat; k++) { - if (rem <= num) { - req_schp->num_sgat = k + 1; - req_schp->sglist_len = rsv_schp->sglist_len; - req_schp->pages = rsv_schp->pages; + struct sg_request *srp; + gfp_t gfp = __GFP_NOWARN; - req_schp->buflen = size; - req_schp->page_order = rsv_schp->page_order; - break; - } else - rem -= num; + if (first) /* prepared to wait if none already outstanding */ + srp = kzalloc(sizeof(*srp), gfp | GFP_KERNEL); + else + srp = kzalloc(sizeof(*srp), gfp | GFP_ATOMIC); + if (srp) { + atomic_set(&srp->rq_st, SG_RS_BUSY); + srp->parentfp = sfp; + return srp; + } else { + return ERR_PTR(-ENOMEM); } - - if (k >= rsv_schp->num_sgat) - SG_LOG(1, sfp, "%s: BAD size\n", __func__); } -static void -sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) +static struct sg_request * +sg_mk_srp_sgat(struct sg_fd *sfp, bool first, int db_len) { - struct sg_scatter_hold *req_schp = &srp->data; + int res; + struct sg_request *n_srp = sg_mk_srp(sfp, first); - SG_LOG(4, srp->parentfp, "%s: req->num_sgat=%d\n", __func__, - (int)req_schp->num_sgat); - req_schp->num_sgat = 0; - req_schp->buflen = 0; - req_schp->pages = NULL; - req_schp->page_order = 0; - req_schp->sglist_len = 0; - srp->res_used = 0; - /* Called without mutex lock to avoid deadlock */ - sfp->res_in_use = 0; + if (IS_ERR(n_srp)) + return n_srp; + if (db_len > 0) { + res = sg_mk_sgat(&n_srp->sgat_h, sfp, db_len); + if (res) { + kfree(n_srp); + return ERR_PTR(res); + } + } + return n_srp; } -static void -sg_build_reserve(struct sg_fd *sfp, int req_size) +/* + * Irrespective of the given reserve request size, the minimum size requested + * will be PAGE_SIZE (often 4096 bytes). Returns a pointer to reserve object or + * a negated errno value twisted by ERR_PTR() macro. The actual number of bytes + * allocated (maybe less than buflen) is in srp->sgat_h.buflen . Note that this + * function is only called in contexts where locking is not required. + */ +static struct sg_request * +sg_build_reserve(struct sg_fd *sfp, int buflen) { - struct sg_scatter_hold *schp = &sfp->reserve; + bool go_out = false; + int res; + struct sg_request *srp; - SG_LOG(3, sfp, "%s: buflen=%d\n", __func__, req_size); + SG_LOG(3, sfp, "%s: buflen=%d\n", __func__, buflen); + srp = sg_mk_srp(sfp, xa_empty(&sfp->srp_arr)); + if (IS_ERR(srp)) + return srp; + sfp->rsv_srp = srp; do { - if (req_size < PAGE_SIZE) - req_size = PAGE_SIZE; - if (sg_mk_sgat(schp, sfp, req_size) == 0) - return; - sg_remove_sgat(sfp, schp); - req_size >>= 1; /* divide by 2 */ - } while (req_size > (PAGE_SIZE / 2)); + if (buflen < (int)PAGE_SIZE) { + buflen = PAGE_SIZE; + go_out = true; + } + res = sg_mk_sgat(&srp->sgat_h, sfp, buflen); + if (res == 0) { + SG_LOG(4, sfp, "%s: final buflen=%d, srp=0x%pK ++\n", + __func__, buflen, srp); + return srp; + } + if (go_out) + return ERR_PTR(res); + /* failed so remove, halve buflen, try again */ + sg_remove_sgat(srp); + buflen >>= 1; /* divide by 2 */ + } while (true); } -/* always adds to end of list */ +/* + * Setup an active request (soon to carry a SCSI command) to the current file + * descriptor by creating a new one or re-using a request from the free + * list (fl). If successful returns a valid pointer in SG_RS_BUSY state. On + * failure returns a negated errno value twisted by ERR_PTR() macro. + */ static struct sg_request * -sg_setup_req(struct sg_fd *sfp) +sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) { + bool act_empty = false; bool found = false; + bool mk_new_srp = false; + bool try_harder = false; int res; - unsigned long idx, iflags; - struct sg_request *rp; + int num_inactive = 0; + unsigned long idx, last_idx, iflags; + struct sg_request *r_srp = NULL; /* request to return */ struct xarray *xafp = &sfp->srp_arr; - - if (!xa_empty(xafp)) { - xa_for_each_marked(xafp, idx, rp, SG_XA_RQ_INACTIVE) { - if (!rp) + __maybe_unused const char *cp; + +start_again: + cp = ""; + if (xa_empty(xafp)) { + act_empty = true; + mk_new_srp = true; + } else if (!try_harder && dxfr_len < SG_DEF_SECTOR_SZ) { + last_idx = ~0UL; + xa_for_each_marked(xafp, idx, r_srp, SG_XA_RQ_INACTIVE) { + if (!r_srp) continue; - if (sg_rstate_chg(rp, SG_RS_INACTIVE, SG_RS_BUSY)) + ++num_inactive; + if (dxfr_len < SG_DEF_SECTOR_SZ) { + last_idx = idx; continue; - memset(rp, 0, sizeof(*rp)); - rp->rq_idx = idx; - xa_lock_irqsave(xafp, iflags); - __xa_clear_mark(xafp, idx, SG_XA_RQ_INACTIVE); - xa_unlock_irqrestore(xafp, iflags); + } + } + /* If dxfr_len is small, use last inactive request */ + if (last_idx != ~0UL) { + idx = last_idx; + r_srp = xa_load(xafp, idx); + if (!r_srp) + goto start_again; + if (sg_rq_state_chg(r_srp, SG_RS_INACTIVE, SG_RS_BUSY, + false, __func__)) + goto start_again; /* gone to another thread */ + cp = "toward back of srp_arr"; found = true; - break; + } + } else { + xa_for_each_marked(xafp, idx, r_srp, SG_XA_RQ_INACTIVE) { + if (!r_srp) + continue; + if (r_srp->sgat_h.buflen >= dxfr_len) { + if (sg_rq_state_chg + (r_srp, SG_RS_INACTIVE, SG_RS_BUSY, + false, __func__)) + continue; + cp = "from front of srp_arr"; + found = true; + break; + } } } - if (!found) { - rp = kzalloc(sizeof(*rp), GFP_KERNEL); - if (!rp) - return NULL; + if (found) { + r_srp->in_resid = 0; + r_srp->rq_info = 0; + r_srp->sense_len = 0; + mk_new_srp = false; + } else { + mk_new_srp = true; } - rp->parentfp = sfp; - rp->header.duration = jiffies_to_msecs(jiffies); - if (!found) { + if (mk_new_srp) { + bool allow_cmd_q = test_bit(SG_FFD_CMD_Q, sfp->ffd_bm); u32 n_idx; struct xa_limit xal = { .max = 0, .min = 0 }; - atomic_set(&rp->rq_st, SG_RS_BUSY); + cp = "new"; + if (!allow_cmd_q && atomic_read(&sfp->submitted) > 0) { + r_srp = ERR_PTR(-EDOM); + SG_LOG(6, sfp, "%s: trying 2nd req but cmd_q=false\n", + __func__); + goto fini; + } + r_srp = sg_mk_srp_sgat(sfp, act_empty, dxfr_len); + if (IS_ERR(r_srp)) { + if (!try_harder && dxfr_len < SG_DEF_SECTOR_SZ && + num_inactive > 0) { + try_harder = true; + goto start_again; + } + goto fini; + } + atomic_set(&r_srp->rq_st, SG_RS_BUSY); xa_lock_irqsave(xafp, iflags); xal.max = atomic_inc_return(&sfp->req_cnt); - res = __xa_alloc(xafp, &n_idx, rp, xal, GFP_KERNEL); + res = __xa_alloc(xafp, &n_idx, r_srp, xal, GFP_KERNEL); xa_unlock_irqrestore(xafp, iflags); if (res < 0) { - pr_warn("%s: don't expect xa_alloc() to fail, errno=%d\n", - __func__, -res); - return NULL; + SG_LOG(1, sfp, "%s: xa_alloc() failed, errno=%d\n", + __func__, -res); + sg_remove_sgat(r_srp); + kfree(r_srp); + r_srp = ERR_PTR(-EPROTOTYPE); + goto fini; } - rp->rq_idx = n_idx; - } - return rp; + idx = n_idx; + r_srp->rq_idx = idx; + r_srp->parentfp = sfp; + SG_LOG(4, sfp, "%s: mk_new_srp=0x%pK ++\n", __func__, r_srp); + } + r_srp->frq_bm[0] = cwrp->frq_bm[0]; /* assumes <= 32 req flags */ + r_srp->sgat_h.dlen = dxfr_len;/* must be <= r_srp->sgat_h.buflen */ + r_srp->cmd_opcode = 0xff; /* set invalid opcode (VS), 0x0 is TUR */ +fini: + if (IS_ERR(r_srp)) + SG_LOG(1, sfp, "%s: err=%ld\n", __func__, PTR_ERR(r_srp)); + if (!IS_ERR(r_srp)) + SG_LOG(4, sfp, "%s: %s r_srp=0x%pK\n", __func__, cp, r_srp); + return r_srp; } +/* + * Moves a completed sg_request object to the free list and sets it to + * SG_RS_INACTIVE which makes it available for re-use. Requests with no data + * associated are appended to the tail of the free list while other requests + * are prepended to the head of the free list. + */ static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) { @@ -2638,34 +2948,43 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) if (WARN_ON(!sfp || !srp)) return; + atomic_set(&srp->rq_st, SG_RS_INACTIVE); xa_lock_irqsave(&sfp->srp_arr, iflags); __xa_set_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_INACTIVE); + __xa_clear_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_AWAIT); xa_unlock_irqrestore(&sfp->srp_arr, iflags); - atomic_set(&srp->rq_st, SG_RS_INACTIVE); } +/* Returns pointer to sg_fd object or negated errno twisted by ERR_PTR */ static struct sg_fd * sg_add_sfp(struct sg_device *sdp) { + bool reduced = false; int rbuf_len, res; u32 idx; + long err; unsigned long iflags; struct sg_fd *sfp; + struct sg_request *srp = NULL; + struct xarray *xadp = &sdp->sfp_arr; + struct xarray *xafp; struct xa_limit xal; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); if (!sfp) return ERR_PTR(-ENOMEM); - init_waitqueue_head(&sfp->read_wait); xa_init_flags(&sfp->srp_arr, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); + xafp = &sfp->srp_arr; kref_init(&sfp->f_ref); mutex_init(&sfp->f_mutex); sfp->timeout = SG_DEFAULT_TIMEOUT; sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER; - sfp->force_packid = SG_DEF_FORCE_PACK_ID; - sfp->cmd_q = SG_DEF_COMMAND_Q; - sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; + /* other bits in sfp->ffd_bm[1] cleared by kzalloc() above */ + __assign_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm, SG_DEF_FORCE_PACK_ID); + __assign_bit(SG_FFD_CMD_Q, sfp->ffd_bm, SG_DEF_COMMAND_Q); + __assign_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm, SG_DEF_KEEP_ORPHAN); + __assign_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm, SG_DEFAULT_Q_AT); /* * SG_SCATTER_SZ initializes scatter_elem_sz but different value may * be given as driver/module parameter (e.g. 'scatter_elem_sz=8192'). @@ -2679,28 +2998,64 @@ sg_add_sfp(struct sg_device *sdp) atomic_set(&sfp->waiting, 0); atomic_set(&sfp->req_cnt, 0); - if (SG_IS_DETACHING(sdp)) { + if (unlikely(SG_IS_DETACHING(sdp))) { + SG_LOG(1, sfp, "%s: detaching\n", __func__); kfree(sfp); return ERR_PTR(-ENODEV); } - SG_LOG(3, sfp, "%s: sfp=0x%pK\n", __func__, sfp); if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; rbuf_len = min_t(int, sg_big_buff, sdp->max_sgat_sz); - if (rbuf_len > 0) - sg_build_reserve(sfp, rbuf_len); - - xa_lock_irqsave(&sdp->sfp_arr, iflags); + if (rbuf_len > 0) { + struct xa_limit xalrq = { .max = 0, .min = 0 }; + + srp = sg_build_reserve(sfp, rbuf_len); + if (IS_ERR(srp)) { + err = PTR_ERR(srp); + SG_LOG(1, sfp, "%s: build reserve err=%ld\n", __func__, + -err); + kfree(sfp); + return ERR_PTR(err); + } + if (srp->sgat_h.buflen < rbuf_len) { + reduced = true; + SG_LOG(2, sfp, + "%s: reserve reduced from %d to buflen=%d\n", + __func__, rbuf_len, srp->sgat_h.buflen); + } + xa_lock_irqsave(xafp, iflags); + xalrq.max = atomic_inc_return(&sfp->req_cnt); + res = __xa_alloc(xafp, &idx, srp, xalrq, GFP_ATOMIC); + xa_unlock_irqrestore(xafp, iflags); + if (res < 0) { + SG_LOG(1, sfp, "%s: xa_alloc(srp) bad, errno=%d\n", + __func__, -res); + sg_remove_sgat(srp); + kfree(srp); + kfree(sfp); + return ERR_PTR(-EPROTOTYPE); + } + srp->rq_idx = idx; + srp->parentfp = sfp; + sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true, __func__); + } + if (!reduced) { + SG_LOG(4, sfp, "%s: built reserve buflen=%d\n", __func__, + rbuf_len); + } + xa_lock_irqsave(xadp, iflags); xal.min = 0; xal.max = atomic_read(&sdp->open_cnt); - res = __xa_alloc(&sdp->sfp_arr, &idx, sfp, xal, GFP_KERNEL); - xa_unlock_irqrestore(&sdp->sfp_arr, iflags); + res = __xa_alloc(xadp, &idx, sfp, xal, GFP_KERNEL); + xa_unlock_irqrestore(xadp, iflags); if (res < 0) { pr_warn("%s: xa_alloc(sdp) bad, o_count=%d, errno=%d\n", __func__, xal.max, -res); - if (rbuf_len > 0) - sg_remove_sgat(sfp, &sfp->reserve); + if (srp) { + sg_remove_sgat(srp); + kfree(srp); + } kfree(sfp); return ERR_PTR(res); } @@ -2731,12 +3086,14 @@ sg_remove_sfp_usercontext(struct work_struct *work) struct sg_request *srp; struct sg_request *e_srp; struct xarray *xafp = &sfp->srp_arr; + struct xarray *xadp; if (!sfp) { pr_warn("sg: %s: sfp is NULL\n", __func__); return; } sdp = sfp->parentdp; + xadp = &sdp->sfp_arr; /* Cleanup any responses which were never read(). */ xa_for_each(xafp, idx, srp) { @@ -2744,24 +3101,20 @@ sg_remove_sfp_usercontext(struct work_struct *work) continue; if (!xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE)) sg_finish_scsi_blk_rq(srp); + sg_remove_sgat(srp); xa_lock_irqsave(xafp, iflags); e_srp = __xa_erase(xafp, srp->rq_idx); xa_unlock_irqrestore(xafp, iflags); if (srp != e_srp) SG_LOG(1, sfp, "%s: xa_erase() return unexpected\n", __func__); + SG_LOG(6, sfp, "%s: kfree: srp=%pK --\n", __func__, srp); kfree(srp); } xa_destroy(xafp); - if (sfp->reserve.buflen > 0) { - SG_LOG(6, sfp, "%s: buflen=%d, num_sgat=%d\n", __func__, - (int)sfp->reserve.buflen, (int)sfp->reserve.num_sgat); - sg_remove_sgat(sfp, &sfp->reserve); - } - - xa_lock_irqsave(&sdp->sfp_arr, iflags); - e_sfp = __xa_erase(&sdp->sfp_arr, sfp->idx); - xa_unlock_irqrestore(&sdp->sfp_arr, iflags); + xa_lock_irqsave(xadp, iflags); + e_sfp = __xa_erase(xadp, sfp->idx); + xa_unlock_irqrestore(xadp, iflags); if (unlikely(sfp != e_sfp)) SG_LOG(1, sfp, "%s: xa_erase() return unexpected\n", __func__); @@ -3011,6 +3364,7 @@ sg_proc_seq_show_devhdr(struct seq_file *s, void *v) struct sg_proc_deviter { loff_t index; size_t max; + int fd_index; }; static void * @@ -3094,11 +3448,10 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) static void sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) { - int k, new_interface, blen, usg; + int k; unsigned long idx, idx2; struct sg_request *srp; struct sg_fd *fp; - const struct sg_io_hdr *hp; const char * cp; unsigned int ms; @@ -3109,50 +3462,52 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) k++; seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d idx=%lu\n", k, jiffies_to_msecs(fp->timeout), - fp->reserve.buflen, (int)fp->reserve.num_sgat, idx); - seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d\n", - (int)fp->cmd_q, (int)fp->force_packid, - (int)fp->keep_orphan); + fp->rsv_srp->sgat_h.buflen, + (int)fp->rsv_srp->sgat_h.num_sgat, idx); + seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", + (int)test_bit(SG_FFD_CMD_Q, fp->ffd_bm), + (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), + (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm)); seq_printf(s, " submitted=%d waiting=%d\n", atomic_read(&fp->submitted), atomic_read(&fp->waiting)); xa_for_each(&fp->srp_arr, idx2, srp) { + const struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; + bool is_v3 = (sh3p->interface_id != '\0'); + enum sg_rq_state rq_st = atomic_read(&srp->rq_st); + if (!srp) continue; - hp = &srp->header; - new_interface = (hp->interface_id == '\0') ? 0 : 1; - if (srp->res_used) { - if (new_interface && - (SG_FLAG_MMAP_IO & hp->flags)) + if (srp->parentfp->rsv_srp == srp) { + if (is_v3 && (SG_FLAG_MMAP_IO & sh3p->flags)) cp = " mmap>> "; else cp = " rb>> "; } else { - if (SG_INFO_DIRECT_IO_MASK & hp->info) + if (SG_INFO_DIRECT_IO_MASK & srp->rq_info) cp = " dio>> "; else cp = " "; } seq_puts(s, cp); - blen = srp->data.buflen; - usg = srp->data.num_sgat; - seq_puts(s, srp->done ? - ((1 == srp->done) ? "rcv:" : "fin:") - : "act:"); - seq_printf(s, " id=%d blen=%d", - srp->header.pack_id, blen); - if (srp->done) - seq_printf(s, " dur=%d", hp->duration); - else { - ms = jiffies_to_msecs(jiffies); - seq_printf(s, " t_o/elap=%d/%d", - (new_interface ? hp->timeout : - jiffies_to_msecs(fp->timeout)), - (ms > hp->duration ? ms - hp->duration : 0)); + seq_puts(s, sg_rq_st_str(rq_st, false)); + seq_printf(s, ": id=%d len/blen=%d/%d", + srp->pack_id, srp->sgat_h.dlen, + srp->sgat_h.buflen); + if (rq_st == SG_RS_AWAIT_RCV || + rq_st == SG_RS_INACTIVE) { + seq_printf(s, " dur=%d", srp->duration); + goto fin_line; } - seq_printf(s, "ms sgat=%d op=0x%02x dummy: %s\n", usg, - (int)srp->data.cmd_opcode, - sg_rq_st_str(SG_RS_INACTIVE, false)); + ms = jiffies_to_msecs(jiffies); + seq_printf(s, " t_o/elap=%d/%d", + (is_v3 ? sh3p->timeout : + jiffies_to_msecs(fp->timeout)), + (ms > srp->duration ? ms - srp->duration : + 0)); +fin_line: + seq_printf(s, "ms sgat=%d op=0x%02x\n", + srp->sgat_h.num_sgat, (int)srp->cmd_opcode); } if (xa_empty(&fp->srp_arr)) seq_puts(s, " No requests active\n"); From patchwork Mon Oct 24 03:20:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016572 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 50D16C433FE for ; Mon, 24 Oct 2022 03:26:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230470AbiJXD0O (ORCPT ); Sun, 23 Oct 2022 23:26:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38338 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230319AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id B9A4E3B703 for ; Sun, 23 Oct 2022 20:21:40 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id CE5E220423A; Mon, 24 Oct 2022 05:21:38 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hOEIDFEZToId; Mon, 24 Oct 2022 05:21:38 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id A816B2041AF; Mon, 24 Oct 2022 05:21:37 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 25/44] sg: sense buffer rework Date: Sun, 23 Oct 2022 23:20:39 -0400 Message-Id: <20221024032058.14077-26-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org The biggest single item in the sg_request object is the sense buffer array which is SCSI_SENSE_BUFFERSIZE bytes long. That constant started out at 18 bytes 20 years ago and is 96 bytes now and might grow in the future. On the other hand the sense buffer is only used by a small number of SCSI commands: those that fail and those that want to return more information other than a SCSI status of GOOD. Set up a small mempool called "sg_sense" that is only used as required and released back to the mempool as soon as practical. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 117 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 7b9606d029e7..f46b2e4a2e0f 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -77,6 +77,10 @@ static char *sg_version_date = "20190606"; */ #define SG_MAX_CDB_SIZE 252 +static struct kmem_cache *sg_sense_cache; +#define SG_MEMPOOL_MIN_NR 4 +static mempool_t *sg_sense_pool; + /* Following enum contains the states of sg_request::rq_st */ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ SG_RS_INACTIVE = 0, /* request not in use (e.g. on fl) */ @@ -177,7 +181,6 @@ struct sg_fd; struct sg_request { /* active SCSI command or inactive request */ struct sg_scatter_hold sgat_h; /* hold buffer, perhaps scatter list */ struct sg_slice_hdr3 s_hdr3; /* subset of sg_io_hdr */ - u8 sense_b[SCSI_SENSE_BUFFERSIZE]; u32 duration; /* cmd duration in milliseconds */ u32 rq_flags; /* hold user supplied flags */ u32 rq_idx; /* my index within parent's srp_arr */ @@ -190,6 +193,7 @@ struct sg_request { /* active SCSI command or inactive request */ u8 cmd_opcode; /* first byte of SCSI cdb */ u64 start_ns; /* starting point of command duration calc */ unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ + u8 *sense_bp; /* mempool alloc-ed sense buffer, as needed */ struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ struct request *rq; /* released in sg_rq_end_io(), bio kept */ struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ @@ -953,21 +957,24 @@ sg_copy_sense(struct sg_request *srp) /* If need be, copy the sense buffer to the user space */ scsi_stat = srp->rq_result & 0xff; if ((scsi_stat & SAM_STAT_CHECK_CONDITION) || - (srp->sense_b[0] & 0x70) == 0x70) { + (srp->sense_bp[0] & 0x70) == 0x70) { int sb_len = min_t(int, SCSI_SENSE_BUFFERSIZE, srp->sense_len); int mx_sb_len = srp->s_hdr3.mx_sb_len; + u8 *sbp = srp->sense_bp; void __user *up = srp->s_hdr3.sbp; - if (up && mx_sb_len > 0) { + srp->sense_bp = NULL; + if (up && mx_sb_len > 0 && sbp) { sb_len = min_t(int, mx_sb_len, sb_len); /* Additional sense length field */ - sb_len_ret = 8 + (int)srp->sense_b[7]; + sb_len_ret = 8 + (int)sbp[7]; sb_len_ret = min_t(int, sb_len_ret, sb_len); - if (copy_to_user(up, srp->sense_b, sb_len_ret)) + if (copy_to_user(up, sbp, sb_len_ret)) sb_len_ret = -EFAULT; } else { sb_len_ret = 0; } + mempool_free(sbp, sg_sense_pool); } return sb_len_ret; } @@ -1057,10 +1064,16 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, h2p->host_status = host_byte(rq_result); h2p->driver_status = driver_byte(rq_result); if ((CHECK_CONDITION & status_byte(rq_result)) || - (srp->sense_b[0] & 0x70) == 0x70) { + (srp->sense_bp[0] & 0x70) == 0x70) { h2p->driver_status = DRIVER_SENSE; - memcpy(h2p->sense_buffer, srp->sense_b, - sizeof(h2p->sense_buffer)); + if (srp->sense_bp) { + u8 *sbp = srp->sense_bp; + + srp->sense_bp = NULL; + memcpy(h2p->sense_buffer, sbp, + sizeof(h2p->sense_buffer)); + mempool_free(sbp, sg_sense_pool); + } } switch (host_byte(rq_result)) { /* @@ -1095,18 +1108,22 @@ sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, /* Now copy the result back to the user buffer. */ if (count >= SZ_SG_HEADER) { - if (copy_to_user(buf, h2p, SZ_SG_HEADER)) - return -EFAULT; + if (copy_to_user(buf, h2p, SZ_SG_HEADER)) { + res = -EFAULT; + goto fini; + } buf += SZ_SG_HEADER; if (count > h2p->reply_len) count = h2p->reply_len; if (count > SZ_SG_HEADER) { - if (sg_read_append(srp, buf, count - SZ_SG_HEADER)) - return -EFAULT; + res = sg_read_append(srp, buf, count - SZ_SG_HEADER); + if (res) + goto fini; } } else { res = (h2p->result == 0) ? 0 : -EIO; } +fini: sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); return res; @@ -2006,8 +2023,24 @@ sg_rq_end_io(struct request *rq, blk_status_t status) srp->duration = sg_calc_rq_dur(srp); if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0)) sg_check_sense(sdp, srp, slen); - if (slen > 0) - memcpy(srp->sense_b, scmd->sense_buffer, slen); + if (slen > 0) { + if (scmd->sense_buffer) { + srp->sense_bp = mempool_alloc(sg_sense_pool, GFP_ATOMIC); + if (srp->sense_bp) { + memcpy(srp->sense_bp, scmd->sense_buffer, slen); + if (slen < SCSI_SENSE_BUFFERSIZE) + memset(srp->sense_bp + slen, 0, + SCSI_SENSE_BUFFERSIZE - slen); + } else { + slen = 0; + pr_warn("%s: sense but can't alloc buffer\n", + __func__); + } + } else { + slen = 0; + pr_warn("%s: sense_len>0 but sense==NULL\n", __func__); + } + } srp->sense_len = slen; if (unlikely(test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm))) { if (test_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm)) { @@ -2311,13 +2344,30 @@ init_sg(void) if (rc) return rc; + sg_sense_cache = kmem_cache_create_usercopy + ("sg_sense", SCSI_SENSE_BUFFERSIZE, 0, + SLAB_HWCACHE_ALIGN, 0, + SCSI_SENSE_BUFFERSIZE, NULL); + if (!sg_sense_cache) { + pr_err("sg: can't init sense cache\n"); + rc = -ENOMEM; + goto err_out_unreg; + } + sg_sense_pool = mempool_create_slab_pool(SG_MEMPOOL_MIN_NR, + sg_sense_cache); + if (!sg_sense_pool) { + pr_err("sg: can't init sense pool\n"); + rc = -ENOMEM; + goto err_out_cache; + } + pr_info("Registered %s[char major=0x%x], version: %s, date: %s\n", "sg device ", SCSI_GENERIC_MAJOR, SG_VERSION_STR, sg_version_date); sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); if (IS_ERR(sg_sysfs_class)) { rc = PTR_ERR(sg_sysfs_class); - goto err_out_unreg; + goto err_out_pool; } sg_sysfs_valid = true; rc = scsi_register_interface(&sg_interface); @@ -2328,6 +2378,10 @@ init_sg(void) class_destroy(sg_sysfs_class); register_sg_sysctls(); +err_out_pool: + mempool_destroy(sg_sense_pool); +err_out_cache: + kmem_cache_destroy(sg_sense_cache); err_out_unreg: unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); return rc; @@ -2340,6 +2394,8 @@ exit_sg(void) if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) remove_proc_subtree("scsi/sg", NULL); scsi_unregister_interface(&sg_interface); + mempool_destroy(sg_sense_pool); + kmem_cache_destroy(sg_sense_cache); class_destroy(sg_sysfs_class); sg_sysfs_valid = false; unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), @@ -2831,6 +2887,7 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) int num_inactive = 0; unsigned long idx, last_idx, iflags; struct sg_request *r_srp = NULL; /* request to return */ + struct sg_request *last_srp = NULL; struct xarray *xafp = &sfp->srp_arr; __maybe_unused const char *cp; @@ -2847,19 +2904,17 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) ++num_inactive; if (dxfr_len < SG_DEF_SECTOR_SZ) { last_idx = idx; + last_srp = r_srp; continue; } } /* If dxfr_len is small, use last inactive request */ - if (last_idx != ~0UL) { - idx = last_idx; - r_srp = xa_load(xafp, idx); - if (!r_srp) - goto start_again; + if (last_idx != ~0UL && last_srp) { + r_srp = last_srp; if (sg_rq_state_chg(r_srp, SG_RS_INACTIVE, SG_RS_BUSY, false, __func__)) goto start_again; /* gone to another thread */ - cp = "toward back of srp_arr"; + cp = "toward end of srp_arr"; found = true; } } else { @@ -2944,15 +2999,16 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) { - unsigned long iflags; + u8 *sbp; if (WARN_ON(!sfp || !srp)) return; - atomic_set(&srp->rq_st, SG_RS_INACTIVE); - xa_lock_irqsave(&sfp->srp_arr, iflags); - __xa_set_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_INACTIVE); - __xa_clear_mark(&sfp->srp_arr, srp->rq_idx, SG_XA_RQ_AWAIT); - xa_unlock_irqrestore(&sfp->srp_arr, iflags); + sbp = srp->sense_bp; + srp->sense_bp = NULL; + sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true /* force */, __func__); + /* maybe orphaned req, thus never read */ + if (sbp) + mempool_free(sbp, sg_sense_pool); } /* Returns pointer to sg_fd object or negated errno twisted by ERR_PTR */ @@ -3101,7 +3157,12 @@ sg_remove_sfp_usercontext(struct work_struct *work) continue; if (!xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE)) sg_finish_scsi_blk_rq(srp); - sg_remove_sgat(srp); + if (srp->sgat_h.buflen > 0) + sg_remove_sgat(srp); + if (srp->sense_bp) { + mempool_free(srp->sense_bp, sg_sense_pool); + srp->sense_bp = NULL; + } xa_lock_irqsave(xafp, iflags); e_srp = __xa_erase(xafp, srp->rq_idx); xa_unlock_irqrestore(xafp, iflags); From patchwork Mon Oct 24 03:20:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016569 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C2A5CC433FE for ; Mon, 24 Oct 2022 03:26:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230424AbiJXD0G (ORCPT ); Sun, 23 Oct 2022 23:26:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34710 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230316AbiJXDXA (ORCPT ); Sun, 23 Oct 2022 23:23:00 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id CB15C5DF2C for ; Sun, 23 Oct 2022 20:21:41 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 34CFF2041D7; Mon, 24 Oct 2022 05:21:40 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id wab5CksLaX1B; Mon, 24 Oct 2022 05:21:40 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 0729C2041AF; Mon, 24 Oct 2022 05:21:38 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 26/44] sg: add sg v4 interface support Date: Sun, 23 Oct 2022 23:20:40 -0400 Message-Id: <20221024032058.14077-27-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Add support for the sg v4 interface based on struct sg_io_v4 found in include/uapi/linux/bsg.h and only previously supported by the bsg driver. Add ioctl(SG_IOSUBMIT) and ioctl(SG_IORECEIVE) for async (non-blocking) usage of the sg v4 interface. Do not accept the v3 interface with these ioctls. Do not accept the v4 interface with this driver's existing write() and read() system calls. For sync (blocking) usage expand the existing ioctl(SG_IO) to additionally accept the sg v4 interface object. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 436 +++++++++++++++++++++++++++++++++-------- include/uapi/scsi/sg.h | 37 +++- 2 files changed, 394 insertions(+), 79 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index f46b2e4a2e0f..6cd1a8b77d8f 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -41,7 +41,7 @@ static char *sg_version_date = "20190606"; #include #include #include -#include /* for sg_check_file_access() */ +#include /* for sg_check_file_access() */ #include #include @@ -81,6 +81,9 @@ static struct kmem_cache *sg_sense_cache; #define SG_MEMPOOL_MIN_NR 4 static mempool_t *sg_sense_pool; +#define uptr64(usp_val) ((void __user *)(uintptr_t)(usp_val)) +#define cuptr64(usp_val) ((const void __user *)(uintptr_t)(usp_val)) + /* Following enum contains the states of sg_request::rq_st */ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ SG_RS_INACTIVE = 0, /* request not in use (e.g. on fl) */ @@ -104,6 +107,7 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_ADD_RQ_MAX_RETRIES 40 /* to stop infinite _trylock(s) */ /* Bit positions (flags) for sg_request::frq_bm bitmask follow */ +#define SG_FRQ_IS_V4I 0 /* true (set) when is v4 interface */ #define SG_FRQ_IS_ORPHAN 1 /* owner of request gone */ #define SG_FRQ_SYNC_INVOC 2 /* synchronous (blocking) invocation */ #define SG_FRQ_NO_US_XFER 3 /* no user space transfer of data */ @@ -167,6 +171,15 @@ struct sg_slice_hdr3 { void __user *usr_ptr; }; +struct sg_slice_hdr4 { /* parts of sg_io_v4 object needed in async usage */ + void __user *sbp; /* derived from sg_io_v4::response */ + u64 usr_ptr; /* hold sg_io_v4::usr_ptr as given (u64) */ + int out_resid; + s16 dir; /* data xfer direction; SG_DXFER_* */ + u16 cmd_len; /* truncated of sg_io_v4::request_len */ + u16 max_sb_len; /* truncated of sg_io_v4::max_response_len */ +}; + struct sg_scatter_hold { /* holding area for scsi scatter gather info */ struct page **pages; /* num_sgat element array of struct page* */ int buflen; /* capacity in bytes (dlen<=buflen) */ @@ -180,7 +193,10 @@ struct sg_fd; struct sg_request { /* active SCSI command or inactive request */ struct sg_scatter_hold sgat_h; /* hold buffer, perhaps scatter list */ - struct sg_slice_hdr3 s_hdr3; /* subset of sg_io_hdr */ + union { + struct sg_slice_hdr3 s_hdr3; /* subset of sg_io_hdr */ + struct sg_slice_hdr4 s_hdr4; /* reduced size struct sg_io_v4 */ + }; u32 duration; /* cmd duration in milliseconds */ u32 rq_flags; /* hold user supplied flags */ u32 rq_idx; /* my index within parent's srp_arr */ @@ -240,7 +256,10 @@ struct sg_device { /* holds the state of each scsi generic device */ struct sg_comm_wr_t { /* arguments to sg_common_write() */ int timeout; unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ - struct sg_io_hdr *h3p; + union { /* selector is frq_bm.SG_FRQ_IS_V4I */ + struct sg_io_hdr *h3p; + struct sg_io_v4 *h4p; + }; u8 *cmnd; }; @@ -249,13 +268,13 @@ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); static int sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, - int dxfer_dir); + struct sg_io_v4 *h4p, int dxfer_dir); static void sg_finish_scsi_blk_rq(struct sg_request *srp); static int sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen); static void sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp); -static int sg_submit(struct file *filp, struct sg_fd *sfp, - struct sg_io_hdr *hp, bool sync, - struct sg_request **o_srp); +static int sg_v3_submit(struct file *filp, struct sg_fd *sfp, + struct sg_io_hdr *hp, bool sync, + struct sg_request **o_srp); static struct sg_request *sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp); static int sg_read_append(struct sg_request *srp, void __user *outp, @@ -263,12 +282,12 @@ static int sg_read_append(struct sg_request *srp, void __user *outp, static void sg_remove_sgat(struct sg_request *srp); static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); -static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, +static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int id, bool *busy); static struct sg_request *sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); -static struct sg_device *sg_get_dev(int dev); +static struct sg_device *sg_get_dev(int min_dev); static void sg_device_destroy(struct kref *kref); static struct sg_request *sg_mk_srp_sgat(struct sg_fd *sfp, bool first, int db_len); @@ -278,8 +297,11 @@ static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #define SZ_SG_HEADER ((int)sizeof(struct sg_header)) /* v1 and v2 header */ #define SZ_SG_IO_HDR ((int)sizeof(struct sg_io_hdr)) /* v3 header */ +#define SZ_SG_IO_V4 ((int)sizeof(struct sg_io_v4)) /* v4 header (in bsg.h) */ #define SZ_SG_REQ_INFO ((int)sizeof(struct sg_req_info)) +/* There is a assert that SZ_SG_IO_V4 >= SZ_SG_IO_HDR in first function */ + #define SG_IS_DETACHING(sdp) test_bit(SG_FDEV_DETACHING, (sdp)->fdev_bm) #define SG_HAVE_EXCLUDE(sdp) test_bit(SG_FDEV_EXCLUDE, (sdp)->fdev_bm) #define SG_RS_ACTIVE(srp) (atomic_read(&(srp)->rq_st) != SG_RS_INACTIVE) @@ -336,6 +358,10 @@ static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); static int sg_check_file_access(struct file *filp, const char *caller) { + /* can't put following in declarations where it belongs */ + compiletime_assert(SZ_SG_IO_V4 >= SZ_SG_IO_HDR, + "struct sg_io_v4 should be larger than sg_io_hdr"); + if (filp->f_cred != current_real_cred()) { pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n", caller, task_tgid_vnr(current), current->comm); @@ -425,21 +451,18 @@ sg_open(struct inode *inode, struct file *filp) o_excl = !!(op_flags & O_EXCL); non_block = !!(op_flags & O_NONBLOCK); if (o_excl && ((op_flags & O_ACCMODE) == O_RDONLY)) - return -EPERM; /* Can't lock it with read only access */ + return -EPERM;/* not permitted, need write access for O_EXCL */ sdp = sg_get_dev(min_dev); /* increments sdp->d_ref */ if (IS_ERR(sdp)) return PTR_ERR(sdp); - /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ res = scsi_device_get(sdp->device); if (res) goto sg_put; - res = scsi_autopm_get_device(sdp->device); if (res) goto sdp_put; - res = sg_allow_if_err_recovery(sdp, non_block); if (res) goto error_out; @@ -476,9 +499,10 @@ sg_open(struct inode *inode, struct file *filp) } filp->private_data = sfp; + sfp->tid = (current ? current->pid : -1); mutex_unlock(&sdp->open_rel_lock); - SG_LOG(3, sfp, "%s: minor=%d, op_flags=0x%x; %s count after=%d%s\n", - __func__, min_dev, op_flags, "device open", o_count, + SG_LOG(3, sfp, "%s: o_count after=%d on minor=%d, op_flags=0x%x%s\n", + __func__, o_count, min_dev, op_flags, ((op_flags & O_NONBLOCK) ? " O_NONBLOCK" : "")); res = 0; @@ -501,8 +525,13 @@ sg_open(struct inode *inode, struct file *filp) goto sg_put; } -/* Release resources associated with a successful sg_open() - * Returns 0 on success, else a negated errno value */ +/* + * Release resources associated with a prior, successful sg_open(). It can be + * seen as the (final) close() call on a sg device file descriptor in the user + * space. The real work releasing all resources associated with this file + * descriptor is done by sg_remove_sfp_usercontext() which is scheduled by + * sg_remove_sfp(). + */ static int sg_release(struct inode *inode, struct file *filp) { @@ -604,7 +633,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) __func__); return -EPERM; } - res = sg_submit(filp, sfp, h3p, false, NULL); + res = sg_v3_submit(filp, sfp, h3p, false, NULL); return res < 0 ? res : (int)count; } to_v2: @@ -681,7 +710,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) static inline int sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) { - if (!xa_empty(&sfp->srp_arr)) + if (atomic_read(&sfp->submitted) > 0) return -EBUSY; /* already active requests on fd */ if (len > sfp->rsv_srp->sgat_h.buflen) return -ENOMEM; /* MMAP_IO size must fit in reserve */ @@ -713,8 +742,8 @@ sg_fetch_cmnd(struct file *filp, struct sg_fd *sfp, const u8 __user *u_cdbp, } static int -sg_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, - bool sync, struct sg_request **o_srp) +sg_v3_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, + bool sync, struct sg_request **o_srp) { int res, timeout; unsigned long ul_timeout; @@ -748,6 +777,67 @@ sg_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, return 0; } +static int +sg_submit_v4(struct file *filp, struct sg_fd *sfp, void __user *p, + struct sg_io_v4 *h4p, bool sync, struct sg_request **o_srp) +{ + int timeout, res; + unsigned long ul_timeout; + struct sg_request *srp; + struct sg_comm_wr_t cwr; + u8 cmnd[SG_MAX_CDB_SIZE]; + + if (h4p->flags & SG_FLAG_MMAP_IO) { + int len = 0; + + if (h4p->din_xferp) + len = h4p->din_xfer_len; + else if (h4p->dout_xferp) + len = h4p->dout_xfer_len; + res = sg_chk_mmap(sfp, h4p->flags, len); + if (res) + return res; + } + /* once v4 (or v3) seen, allow cmd_q on this fd (def: no cmd_q) */ + set_bit(SG_FFD_CMD_Q, sfp->ffd_bm); + ul_timeout = msecs_to_jiffies(h4p->timeout); + timeout = min_t(unsigned long, ul_timeout, INT_MAX); + res = sg_fetch_cmnd(filp, sfp, cuptr64(h4p->request), h4p->request_len, + cmnd); + if (res) + return res; + cwr.frq_bm[0] = 0; + assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); + set_bit(SG_FRQ_IS_V4I, cwr.frq_bm); + cwr.h4p = h4p; + cwr.timeout = timeout; + cwr.cmnd = cmnd; + srp = sg_common_write(sfp, &cwr); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (o_srp) + *o_srp = srp; + return res; +} + +static int +sg_ctl_iosubmit(struct file *filp, struct sg_fd *sfp, void __user *p) +{ + int res; + u8 hdr_store[SZ_SG_IO_V4]; + struct sg_io_v4 *h4p = (struct sg_io_v4 *)hdr_store; + struct sg_device *sdp = sfp->parentdp; + + res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK)); + if (res) + return res; + if (copy_from_user(hdr_store, p, SZ_SG_IO_V4)) + return -EFAULT; + if (h4p->guard == 'Q') + return sg_submit_v4(filp, sfp, p, h4p, false, NULL); + return -EPERM; +} + #if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) static void sg_rq_state_fail_msg(struct sg_fd *sfp, enum sg_rq_state exp_old_st, @@ -857,16 +947,46 @@ sg_rq_state_chg(struct sg_request *srp, enum sg_rq_state old_st, return 0; } +static void +sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) +{ + bool at_head, is_v4h, sync; + + is_v4h = test_bit(SG_FRQ_IS_V4I, srp->frq_bm); + sync = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); + SG_LOG(3, sfp, "%s: is_v4h=%d\n", __func__, (int)is_v4h); + srp->start_ns = ktime_get_boottime_ns(); + srp->duration = 0; + + if (!is_v4h && srp->s_hdr3.interface_id == '\0') + at_head = true; /* backward compatibility: v1+v2 interfaces */ + else if (test_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm)) + /* cmd flags can override sfd setting */ + at_head = !!(srp->rq_flags & SG_FLAG_Q_AT_HEAD); + else /* this sfd is defaulting to head */ + at_head = !(srp->rq_flags & SG_FLAG_Q_AT_TAIL); + + kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ + sg_rq_state_chg(srp, SG_RS_BUSY /* ignored */, SG_RS_INFLIGHT, + true, __func__); + + /* >>>>>>> send cmd/req off to other levels <<<<<<<< */ + if (!sync) + atomic_inc(&sfp->submitted); + srp->rq->end_io = sg_rq_end_io; + blk_execute_rq_nowait(srp->rq, at_head); +} + /* * All writes and submits converge on this function to launch the SCSI * command/request (via blk_execute_rq_nowait). Returns a pointer to a * sg_request object holding the request just issued or a negated errno * value twisted by ERR_PTR. + * N.B. pack_id placed in sg_io_v4::request_extra field. */ static struct sg_request * sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) { - bool at_head; int res = 0; int dxfr_len, dir, cmd_len; int pack_id = SG_PACK_ID_WILDCARD; @@ -874,12 +994,32 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) struct sg_device *sdp = sfp->parentdp; struct sg_request *srp; struct sg_io_hdr *hi_p; - - hi_p = cwrp->h3p; - dir = hi_p->dxfer_direction; - dxfr_len = hi_p->dxfer_len; - rq_flags = hi_p->flags; - pack_id = hi_p->pack_id; + struct sg_io_v4 *h4p; + + if (test_bit(SG_FRQ_IS_V4I, cwrp->frq_bm)) { + h4p = cwrp->h4p; + hi_p = NULL; + dxfr_len = 0; + dir = SG_DXFER_NONE; + rq_flags = h4p->flags; + pack_id = h4p->request_extra; + if (h4p->din_xfer_len && h4p->dout_xfer_len) { + return ERR_PTR(-EOPNOTSUPP); + } else if (h4p->din_xfer_len) { + dxfr_len = h4p->din_xfer_len; + dir = SG_DXFER_FROM_DEV; + } else if (h4p->dout_xfer_len) { + dxfr_len = h4p->dout_xfer_len; + dir = SG_DXFER_TO_DEV; + } + } else { /* sg v3 interface so hi_p valid */ + h4p = NULL; + hi_p = cwrp->h3p; + dir = hi_p->dxfer_direction; + dxfr_len = hi_p->dxfer_len; + rq_flags = hi_p->flags; + pack_id = hi_p->pack_id; + } if (dxfr_len >= SZ_256M) return ERR_PTR(-EINVAL); @@ -889,13 +1029,23 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) srp->rq_flags = rq_flags; srp->pack_id = pack_id; - cmd_len = hi_p->cmd_len; - memcpy(&srp->s_hdr3, hi_p, sizeof(srp->s_hdr3)); + if (h4p) { + memset(&srp->s_hdr4, 0, sizeof(srp->s_hdr4)); + srp->s_hdr4.usr_ptr = h4p->usr_ptr; + srp->s_hdr4.sbp = uptr64(h4p->response); + srp->s_hdr4.max_sb_len = h4p->max_response_len; + srp->s_hdr4.cmd_len = h4p->request_len; + srp->s_hdr4.dir = dir; + cmd_len = h4p->request_len; + } else { /* v3 interface active */ + cmd_len = hi_p->cmd_len; + memcpy(&srp->s_hdr3, hi_p, sizeof(srp->s_hdr3)); + } srp->cmd_opcode = cwrp->cmnd[0];/* hold opcode of command for debug */ SG_LOG(4, sfp, "%s: opcode=0x%02x, cdb_sz=%d, pack_id=%d\n", __func__, (int)cwrp->cmnd[0], cmd_len, pack_id); - res = sg_start_req(srp, cwrp->cmnd, cmd_len, dir); + res = sg_start_req(srp, cwrp->cmnd, cmd_len, h4p, dir); if (res < 0) /* probably out of space --> -ENOMEM */ goto err_out; if (unlikely(SG_IS_DETACHING(sdp))) { @@ -903,25 +1053,7 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) goto err_out; } srp->rq->timeout = cwrp->timeout; - kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ - res = sg_rq_state_chg(srp, SG_RS_BUSY, SG_RS_INFLIGHT, false, - __func__); - if (res) - goto err_out; - srp->start_ns = ktime_get_boottime_ns(); - srp->duration = 0; - - if (srp->s_hdr3.interface_id == '\0') - at_head = true; /* backward compatibility: v1+v2 interfaces */ - else if (test_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm)) - /* cmd flags can override sfd setting */ - at_head = !!(srp->rq_flags & SG_FLAG_Q_AT_HEAD); - else /* this sfd is defaulting to head */ - at_head = !(srp->rq_flags & SG_FLAG_Q_AT_TAIL); - if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) - atomic_inc(&sfp->submitted); - srp->rq->end_io = sg_rq_end_io; - blk_execute_rq_nowait(srp->rq, at_head); + sg_execute_cmd(sfp, srp); return srp; err_out: sg_deact_request(sfp, srp); @@ -933,7 +1065,6 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) * sg_ctl_ioreceive(). wait_event_interruptible will return if this one * returns true (or an event like a signal (e.g. control-C) occurs). */ - static inline bool sg_get_ready_srp(struct sg_fd *sfp, struct sg_request **srpp, int pack_id, bool *busy) @@ -949,7 +1080,7 @@ sg_get_ready_srp(struct sg_fd *sfp, struct sg_request **srpp, int pack_id, * negated errno value. */ static int -sg_copy_sense(struct sg_request *srp) +sg_copy_sense(struct sg_request *srp, bool v4_active) { int sb_len_ret = 0; int scsi_stat; @@ -959,11 +1090,18 @@ sg_copy_sense(struct sg_request *srp) if ((scsi_stat & SAM_STAT_CHECK_CONDITION) || (srp->sense_bp[0] & 0x70) == 0x70) { int sb_len = min_t(int, SCSI_SENSE_BUFFERSIZE, srp->sense_len); - int mx_sb_len = srp->s_hdr3.mx_sb_len; + int mx_sb_len; u8 *sbp = srp->sense_bp; - void __user *up = srp->s_hdr3.sbp; + void __user *up; srp->sense_bp = NULL; + if (v4_active) { + up = uptr64(srp->s_hdr4.sbp); + mx_sb_len = srp->s_hdr4.max_sb_len; + } else { + up = (void __user *)srp->s_hdr3.sbp; + mx_sb_len = srp->s_hdr3.mx_sb_len; + } if (up && mx_sb_len > 0 && sbp) { sb_len = min_t(int, mx_sb_len, sb_len); /* Additional sense length field */ @@ -980,14 +1118,16 @@ sg_copy_sense(struct sg_request *srp) } static int -sg_rec_state_v3(struct sg_fd *sfp, struct sg_request *srp) +sg_rec_state_v3v4(struct sg_fd *sfp, struct sg_request *srp, bool v4_active) { - int sb_len_wr; u32 rq_res = srp->rq_result; - sb_len_wr = sg_copy_sense(srp); - if (sb_len_wr < 0) - return sb_len_wr; + if (unlikely(srp->rq_result & 0xff)) { + int sb_len_wr = sg_copy_sense(srp, v4_active); + + if (sb_len_wr < 0) + return sb_len_wr; + } if (rq_res & SG_ML_RESULT_MSK) srp->rq_info |= SG_INFO_CHECK; if (unlikely(SG_IS_DETACHING(sfp->parentdp))) @@ -1014,7 +1154,7 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, goto err_out; } SG_LOG(3, sfp, "%s: srp=0x%pK\n", __func__, srp); - err = sg_rec_state_v3(sfp, srp); + err = sg_rec_state_v3v4(sfp, srp, false); memset(hp, 0, sizeof(*hp)); memcpy(hp, &srp->s_hdr3, sizeof(srp->s_hdr3)); hp->sb_len_wr = srp->sense_len; @@ -1038,11 +1178,104 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, return err; } +static int +sg_receive_v4(struct sg_fd *sfp, struct sg_request *srp, void __user *p, + struct sg_io_v4 *h4p) +{ + int err, err2; + u32 rq_result = srp->rq_result; + + SG_LOG(3, sfp, "%s: p=%s, h4p=%s\n", __func__, + (p ? "given" : "NULL"), (h4p ? "given" : "NULL")); + err = sg_rec_state_v3v4(sfp, srp, true); + h4p->guard = 'Q'; + h4p->protocol = 0; + h4p->subprotocol = 0; + h4p->device_status = rq_result & 0xff; + h4p->driver_status = driver_byte(rq_result); + h4p->transport_status = host_byte(rq_result); + h4p->response_len = srp->sense_len; + h4p->info = srp->rq_info; + h4p->flags = srp->rq_flags; + h4p->duration = srp->duration; + switch (srp->s_hdr4.dir) { + case SG_DXFER_FROM_DEV: + h4p->din_xfer_len = srp->sgat_h.dlen; + break; + case SG_DXFER_TO_DEV: + h4p->dout_xfer_len = srp->sgat_h.dlen; + break; + default: + break; + } + h4p->din_resid = srp->in_resid; + h4p->dout_resid = srp->s_hdr4.out_resid; + h4p->usr_ptr = srp->s_hdr4.usr_ptr; + h4p->response = (u64)srp->s_hdr4.sbp; + h4p->request_extra = srp->pack_id; + if (p) { + if (copy_to_user(p, h4p, SZ_SG_IO_V4)) + err = err ? err : -EFAULT; + } + err2 = sg_rq_state_chg(srp, atomic_read(&srp->rq_st), SG_RS_INACTIVE, + false, __func__); + if (err2) + err = err ? err : err2; + sg_finish_scsi_blk_rq(srp); + sg_deact_request(sfp, srp); + return err < 0 ? err : 0; +} + /* - * Completes a v3 request/command. Called from sg_read {v2 or v3}, - * ioctl(SG_IO) {for v3}, or from ioctl(SG_IORECEIVE) when its - * completing a v3 request/command. + * Called when ioctl(SG_IORECEIVE) received. Expects a v4 interface object. + * Checks if O_NONBLOCK file flag given, if not checks given 'flags' field + * to see if SGV4_FLAG_IMMED is set. Either of these implies non blocking. + * When non-blocking and there is no request waiting, yields EAGAIN; + * otherwise it waits (i.e. it "blocks"). */ +static int +sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) +{ + bool non_block = !!(filp->f_flags & O_NONBLOCK); + bool busy; + int res, id; + int pack_id = SG_PACK_ID_WILDCARD; + u8 v4_holder[SZ_SG_IO_V4]; + struct sg_io_v4 *h4p = (struct sg_io_v4 *)v4_holder; + struct sg_device *sdp = sfp->parentdp; + struct sg_request *srp; + + res = sg_allow_if_err_recovery(sdp, non_block); + if (res) + return res; + /* Get first three 32 bit integers: guard, proto+subproto */ + if (copy_from_user(h4p, p, SZ_SG_IO_V4)) + return -EFAULT; + /* for v4: protocol=0 --> SCSI; subprotocol=0 --> SPC++ */ + if (h4p->guard != 'Q' || h4p->protocol != 0 || h4p->subprotocol != 0) + return -EPERM; + if (h4p->flags & SGV4_FLAG_IMMED) + non_block = true; /* set by either this or O_NONBLOCK */ + SG_LOG(3, sfp, "%s: non_block(+IMMED)=%d\n", __func__, non_block); + /* read in part of v3 or v4 header for pack_id or tag based find */ + id = pack_id; + srp = sg_find_srp_by_id(sfp, id, &busy); + while (!srp) { /* nothing available so wait on packet or */ + if (unlikely(!busy && SG_IS_DETACHING(sdp))) + return -ENODEV; + if (non_block) + return -EAGAIN; + res = wait_event_interruptible(sfp->read_wait, + sg_get_ready_srp(sfp, &srp, + id, &busy)); + if (unlikely(!busy && SG_IS_DETACHING(sdp))) + return -ENODEV; + if (res) /* -ERESTARTSYS as signal hit process */ + return res; + } + return sg_receive_v4(sfp, srp, p, h4p); +} + static int sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, struct sg_request *srp) @@ -1316,6 +1549,8 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, rip->sg_io_owned = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); rip->problem = !!(srp->rq_result & SG_ML_RESULT_MSK); rip->pack_id = srp->pack_id; + rip->usr_ptr = test_bit(SG_FRQ_IS_V4I, srp->frq_bm) ? + uptr64(srp->s_hdr4.usr_ptr) : srp->s_hdr3.usr_ptr; rip->usr_ptr = srp->s_hdr3.usr_ptr; xa_unlock_irqrestore(&sfp->srp_arr, iflags); } @@ -1333,7 +1568,7 @@ sg_rq_landed(struct sg_device *sdp, struct sg_request *srp) */ static int sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, - struct sg_request *srp) + struct sg_io_v4 *h4p, struct sg_request *srp) { int res; enum sg_rq_state sr_st; @@ -1361,7 +1596,10 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, res = sg_rq_state_chg(srp, sr_st, SG_RS_BUSY, false, __func__); if (unlikely(res)) return res; - res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, p); + if (test_bit(SG_FRQ_IS_V4I, srp->frq_bm)) + res = sg_receive_v4(sfp, srp, p, h4p); + else + res = sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, p); return (res < 0) ? res : 0; } @@ -1375,8 +1613,9 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, { int res; struct sg_request *srp = NULL; - u8 hu8arr[SZ_SG_IO_HDR]; + u8 hu8arr[SZ_SG_IO_V4]; struct sg_io_hdr *h3p = (struct sg_io_hdr *)hu8arr; + struct sg_io_v4 *h4p = (struct sg_io_v4 *)hu8arr; SG_LOG(3, sfp, "%s: SG_IO%s\n", __func__, ((filp->f_flags & O_NONBLOCK) ? " O_NONBLOCK ignored" : "")); @@ -1385,15 +1624,25 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return res; if (get_sg_io_hdr(h3p, buf)) return -EFAULT; - if (h3p->interface_id == 'S') - res = sg_submit(filp, sfp, h3p, true, &srp); - else + if (h3p->interface_id == 'Q') { + /* copy in rest of sg_io_v4 object */ + if (copy_from_user(hu8arr + SZ_SG_IO_HDR, + ((u8 __user *)buf) + SZ_SG_IO_HDR, + SZ_SG_IO_V4 - SZ_SG_IO_HDR)) + return -EFAULT; + res = sg_submit_v4(filp, sfp, buf, h4p, true, &srp); + } else if (h3p->interface_id == 'S') { + res = sg_v3_submit(filp, sfp, h3p, true, &srp); + } else { + pr_info_once("sg: %s: v3 or v4 interface only here\n", + __func__); return -EPERM; + } if (unlikely(res < 0)) return res; if (!srp) /* mrq case: already processed all responses */ return res; - res = sg_wait_event_srp(filp, sfp, buf, srp); + res = sg_wait_event_srp(filp, sfp, buf, h4p, srp); if (res) SG_LOG(1, sfp, "%s: %s=0x%pK state: %s\n", __func__, "unexpected srp", srp, @@ -1562,6 +1811,12 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, switch (cmd_in) { case SG_IO: return sg_ctl_sg_io(filp, sdp, sfp, p); + case SG_IOSUBMIT: + SG_LOG(3, sfp, "%s: SG_IOSUBMIT\n", __func__); + return sg_ctl_iosubmit(filp, sfp, p); + case SG_IORECEIVE: + SG_LOG(3, sfp, "%s: SG_IORECEIVE\n", __func__); + return sg_ctl_ioreceive(filp, sfp, p); case SG_GET_SCSI_ID: return sg_ctl_scsi_id(sdev, sfp, p); case SG_SET_FORCE_PACK_ID: @@ -2015,8 +2270,16 @@ sg_rq_end_io(struct request *rq, blk_status_t status) slen = min_t(int, scmd->sense_len, SCSI_SENSE_BUFFERSIZE); a_resid = scmd->resid_len; - if (a_resid) - srp->in_resid = a_resid; + if (a_resid) { + if (test_bit(SG_FRQ_IS_V4I, srp->frq_bm)) { + if (rq_data_dir(rq) == READ) + srp->in_resid = a_resid; + else + srp->s_hdr4.out_resid = a_resid; + } else { + srp->in_resid = a_resid; + } + } SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->pack_id, srp->rq_result); @@ -2427,7 +2690,8 @@ sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, } static int -sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, int dxfer_dir) +sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, + struct sg_io_v4 *h4p, int dxfer_dir) { bool reserved, us_xfer; int res = 0; @@ -2443,14 +2707,30 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, int dxfer_dir) struct request_queue *q; struct rq_map_data *md = (void *)srp; /* want any non-NULL value */ __maybe_unused const char *cp = ""; - struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; struct rq_map_data map_data; sdp = sfp->parentdp; - up = sh3p->dxferp; - dxfer_len = (int)sh3p->dxfer_len; - iov_count = sh3p->iovec_count; - r0w = dxfer_dir == SG_DXFER_TO_DEV ? WRITE : READ; + if (h4p) { + if (dxfer_dir == SG_DXFER_TO_DEV) { + r0w = WRITE; + up = uptr64(h4p->dout_xferp); + dxfer_len = (int)h4p->dout_xfer_len; + iov_count = h4p->dout_iovec_count; + } else if (dxfer_dir == SG_DXFER_FROM_DEV) { + up = uptr64(h4p->din_xferp); + dxfer_len = (int)h4p->din_xfer_len; + iov_count = h4p->din_iovec_count; + } else { + up = NULL; + } + } else { + struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; + + up = sh3p->dxferp; + dxfer_len = (int)sh3p->dxfer_len; + iov_count = sh3p->iovec_count; + r0w = dxfer_dir == SG_DXFER_TO_DEV ? WRITE : READ; + } SG_LOG(4, sfp, "%s: dxfer_len=%d, data-%s\n", __func__, dxfer_len, (r0w ? "OUT" : "IN")); q = sdp->device->request_queue; diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 3c507fbef30f..217b574c27ab 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -98,6 +98,18 @@ typedef struct sg_io_hdr { #define SG_FLAG_Q_AT_TAIL 0x10 #define SG_FLAG_Q_AT_HEAD 0x20 +/* + * Flags used by ioctl(SG_IOSUBMIT) [abbrev: SG_IOS] and ioctl(SG_IORECEIVE) + * [abbrev: SG_IOR] OR-ed into sg_io_v4::flags. The sync v4 interface uses + * ioctl(SG_IO) and can take these new flags, as can the v3 interface. + * These flags apply for SG_IOS unless otherwise noted. May be OR-ed together. + */ +#define SGV4_FLAG_DIRECT_IO SG_FLAG_DIRECT_IO +#define SGV4_FLAG_MMAP_IO SG_FLAG_MMAP_IO +#define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL +#define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD +#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR, ignored in SG_IOS */ + /* Output (potentially OR-ed together) in v3::info or v4::info field */ #define SG_INFO_OK_MASK 0x1 #define SG_INFO_OK 0x0 /* no sense, host nor driver "noise" */ @@ -167,7 +179,6 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ /* sg_io_owned set imples synchronous, clear implies asynchronous */ char sg_io_owned;/* 0 -> complete with read(), 1 -> owned by SG_IO */ char problem; /* 0 -> no problem detected, 1 -> error to report */ - /* If SG_CTL_FLAGM_TAG_FOR_PACK_ID set on fd then next field is tag */ int pack_id; /* pack_id, in v4 driver may be tag instead */ void __user *usr_ptr; /* user provided pointer in v3+v4 interface */ unsigned int duration; @@ -196,6 +207,13 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ #define SG_SET_RESERVED_SIZE 0x2275 /* request new reserved buffer size */ #define SG_GET_RESERVED_SIZE 0x2272 /* actual size of reserved buffer */ +/* + * Historically the scsi/sg driver has used 0x22 as it ioctl base number. + * Add a define for that value and use it for several new ioctls added in + * version 4.0.01 sg driver and later. + */ +#define SG_IOCTL_MAGIC_NUM 0x22 + /* The following ioctl has a 'sg_scsi_id_t *' object as its 3rd argument. */ #define SG_GET_SCSI_ID 0x2276 /* Yields fd's bus, chan, dev, lun + type */ /* SCSI id information can also be obtained from SCSI_IOCTL_GET_IDLUN */ @@ -352,6 +370,23 @@ struct sg_header { */ #define SG_NEXT_CMD_LEN 0x2283 +/* + * New ioctls to replace async (non-blocking) write()/read() interface. + * Present in version 4 and later of the sg driver [>20190427]. The + * SG_IOSUBMIT and SG_IORECEIVE ioctls accept the sg_v4 interface based on + * struct sg_io_v4 found in . These objects are + * passed by a pointer in the third argument of the ioctl. + * + * Data may be transferred both from the user space to the driver by these + * ioctls. Hence the _IOWR macro is used here to generate the ioctl number + * rather than _IOW or _IOR. + */ +/* Submits a v4 interface object to driver, optionally receive tag back */ +#define SG_IOSUBMIT _IOWR(SG_IOCTL_MAGIC_NUM, 0x41, struct sg_io_v4) + +/* Gives some v4 identifying info to driver, receives associated response */ +#define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4) + /* command queuing is always on when the v3 or v4 interface is used */ #define SG_DEF_COMMAND_Q 0 From patchwork Mon Oct 24 03:20:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016566 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0DAF3C3A59D for ; Mon, 24 Oct 2022 03:26:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230465AbiJXDZ7 (ORCPT ); Sun, 23 Oct 2022 23:25:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230093AbiJXDXB (ORCPT ); Sun, 23 Oct 2022 23:23:01 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 0AB1D10FCD for ; Sun, 23 Oct 2022 20:21:43 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 8102B20423B; Mon, 24 Oct 2022 05:21:41 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 0m9D-ppuT5hR; Mon, 24 Oct 2022 05:21:41 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 5AD952041AF; Mon, 24 Oct 2022 05:21:40 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 27/44] sg: rework debug info Date: Sun, 23 Oct 2022 23:20:41 -0400 Message-Id: <20221024032058.14077-28-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Since the version 2 driver, the state of the driver can be found with 'cat /proc/scsi/sg/debug'. As the driver becomes more threaded and IO faster (e.g. scsi_debug with a command timer of 5 microseconds), the existing state dump can become misleading as the state can change during the "snapshot". The new approach in this patch is to allocate a buffer of SG_PROC_DEBUG_SZ bytes and use scnprintf() to populate it. Only when the whole state is captured (or the buffer fills) is the output to the caller's terminal performed. The previous approach was line based: assemble a line of information and then output it. Locks are taken as required for short periods and should not interfere with a disk IO intensive program. Operations such as closing a sg file descriptor or removing a sg device may be held up for a short while (microseconds). Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 255 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 177 insertions(+), 78 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6cd1a8b77d8f..264955e660f3 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2287,7 +2287,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0)) sg_check_sense(sdp, srp, slen); if (slen > 0) { - if (scmd->sense_buffer) { + if (scmd->sense_buffer && !srp->sense_bp) { srp->sense_bp = mempool_alloc(sg_sense_pool, GFP_ATOMIC); if (srp->sense_bp) { memcpy(srp->sense_bp, scmd->sense_buffer, slen); @@ -2299,6 +2299,9 @@ sg_rq_end_io(struct request *rq, blk_status_t status) pr_warn("%s: sense but can't alloc buffer\n", __func__); } + } else if (srp->sense_bp) { + slen = 0; + pr_warn("%s: non-NULL srp->sense_bp ? ?\n", __func__); } else { slen = 0; pr_warn("%s: sense_len>0 but sense==NULL\n", __func__); @@ -3785,115 +3788,211 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) return 0; } -/* must be called while holding sg_index_lock */ -static void -sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) +/* Writes debug info for one sg_request in obp buffer */ +static int +sg_proc_debug_sreq(struct sg_request *srp, int to, char *obp, int len) { - int k; - unsigned long idx, idx2; + bool is_v3v4, v4, is_dur; + int n = 0; + u32 dur; + enum sg_rq_state rq_st; + const char *cp; + + if (len < 1) + return 0; + v4 = test_bit(SG_FRQ_IS_V4I, srp->frq_bm); + is_v3v4 = v4 ? true : (srp->s_hdr3.interface_id != '\0'); + if (srp->parentfp->rsv_srp == srp) + cp = (is_v3v4 && (srp->rq_flags & SG_FLAG_MMAP_IO)) ? + " mmap>> " : " rsv>> "; + else + cp = (srp->rq_info & SG_INFO_DIRECT_IO_MASK) ? + " dio>> " : " "; + rq_st = atomic_read(&srp->rq_st); + dur = sg_get_dur(srp, &rq_st, &is_dur); + n += scnprintf(obp + n, len - n, "%s%s: dlen=%d/%d id=%d", cp, + sg_rq_st_str(rq_st, false), srp->sgat_h.dlen, + srp->sgat_h.buflen, (int)srp->pack_id); + if (is_dur) /* cmd/req has completed, waiting for ... */ + n += scnprintf(obp + n, len - n, " dur=%ums", dur); + else if (dur < U32_MAX) /* in-flight or busy (so ongoing) */ + n += scnprintf(obp + n, len - n, " t_o/elap=%us/%ums", + to / 1000, dur); + n += scnprintf(obp + n, len - n, " sgat=%d op=0x%02x\n", + srp->sgat_h.num_sgat, srp->cmd_opcode); + return n; +} + +/* Writes debug info for one sg fd (including its sg requests) in obp buffer */ +static int +sg_proc_debug_fd(struct sg_fd *fp, char *obp, int len, unsigned long idx) +{ + int n = 0; + int to, k; + unsigned long iflags; struct sg_request *srp; - struct sg_fd *fp; - const char * cp; - unsigned int ms; + /* sgat=-1 means unavailable */ + to = (fp->timeout >= 0) ? jiffies_to_msecs(fp->timeout) : -999; + if (to < 0) + n += scnprintf(obp + n, len - n, "BAD timeout=%d", + fp->timeout); + else if (to % 1000) + n += scnprintf(obp + n, len - n, "timeout=%dms rs", to); + else + n += scnprintf(obp + n, len - n, "timeout=%ds rs", to / 1000); + n += scnprintf(obp + n, len - n, "v_buflen=%d idx=%lu\n cmd_q=%d ", + fp->rsv_srp->sgat_h.buflen, idx, + (int)test_bit(SG_FFD_CMD_Q, fp->ffd_bm)); + n += scnprintf(obp + n, len - n, + "f_packid=%d k_orphan=%d ffd_bm=0x%lx\n", + (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), + (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm), + fp->ffd_bm[0]); + n += scnprintf(obp + n, len - n, " mmap_called=%d\n", + test_bit(SG_FFD_MMAP_CALLED, fp->ffd_bm)); + n += scnprintf(obp + n, len - n, + " submitted=%d waiting=%d open thr_id=%d\n", + atomic_read(&fp->submitted), + atomic_read(&fp->waiting), fp->tid); k = 0; + xa_lock_irqsave(&fp->srp_arr, iflags); + xa_for_each(&fp->srp_arr, idx, srp) { + if (!srp) + continue; + if (xa_get_mark(&fp->srp_arr, idx, SG_XA_RQ_INACTIVE)) + continue; + n += sg_proc_debug_sreq(srp, fp->timeout, obp + n, len - n); + ++k; + if ((k % 8) == 0) { /* don't hold up isr_s too long */ + xa_unlock_irqrestore(&fp->srp_arr, iflags); + cpu_relax(); + xa_lock_irqsave(&fp->srp_arr, iflags); + } + } + if (k == 0) + n += scnprintf(obp + n, len - n, " No requests active\n"); + k = 0; + xa_for_each_marked(&fp->srp_arr, idx, srp, SG_XA_RQ_INACTIVE) { + if (!srp) + continue; + if (k == 0) + n += scnprintf(obp + n, len - n, " Inactives:\n"); + n += sg_proc_debug_sreq(srp, fp->timeout, obp + n, len - n); + ++k; + if ((k % 8) == 0) { /* don't hold up isr_s too long */ + xa_unlock_irqrestore(&fp->srp_arr, iflags); + cpu_relax(); + xa_lock_irqsave(&fp->srp_arr, iflags); + } + } + xa_unlock_irqrestore(&fp->srp_arr, iflags); + return n; +} + +/* Writes debug info for one sg device (including its sg fds) in obp buffer */ +static int +sg_proc_debug_sdev(struct sg_device *sdp, char *obp, int len, int *fd_counterp) +{ + int n = 0; + int my_count = 0; + unsigned long idx; + struct scsi_device *ssdp = sdp->device; + struct sg_fd *fp; + char *disk_name; + int *countp; + + countp = fd_counterp ? fd_counterp : &my_count; + disk_name = (sdp->name[0] ? sdp->name : "?_?"); + n += scnprintf(obp + n, len - n, " >>> device=%s ", disk_name); + n += scnprintf(obp + n, len - n, "%d:%d:%d:%llu ", ssdp->host->host_no, + ssdp->channel, ssdp->id, ssdp->lun); + n += scnprintf(obp + n, len - n, + " max_sgat_sz,elems=2^%d,%d excl=%d open_cnt=%d\n", + ilog2(sdp->max_sgat_sz), sdp->max_sgat_elems, + SG_HAVE_EXCLUDE(sdp), atomic_read(&sdp->open_cnt)); xa_for_each(&sdp->sfp_arr, idx, fp) { if (!fp) continue; - k++; - seq_printf(s, " FD(%d): timeout=%dms buflen=%d (res)sgat=%d idx=%lu\n", - k, jiffies_to_msecs(fp->timeout), - fp->rsv_srp->sgat_h.buflen, - (int)fp->rsv_srp->sgat_h.num_sgat, idx); - seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", - (int)test_bit(SG_FFD_CMD_Q, fp->ffd_bm), - (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), - (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm)); - seq_printf(s, " submitted=%d waiting=%d\n", - atomic_read(&fp->submitted), - atomic_read(&fp->waiting)); - xa_for_each(&fp->srp_arr, idx2, srp) { - const struct sg_slice_hdr3 *sh3p = &srp->s_hdr3; - bool is_v3 = (sh3p->interface_id != '\0'); - enum sg_rq_state rq_st = atomic_read(&srp->rq_st); - - if (!srp) - continue; - if (srp->parentfp->rsv_srp == srp) { - if (is_v3 && (SG_FLAG_MMAP_IO & sh3p->flags)) - cp = " mmap>> "; - else - cp = " rb>> "; - } else { - if (SG_INFO_DIRECT_IO_MASK & srp->rq_info) - cp = " dio>> "; - else - cp = " "; - } - seq_puts(s, cp); - seq_puts(s, sg_rq_st_str(rq_st, false)); - seq_printf(s, ": id=%d len/blen=%d/%d", - srp->pack_id, srp->sgat_h.dlen, - srp->sgat_h.buflen); - if (rq_st == SG_RS_AWAIT_RCV || - rq_st == SG_RS_INACTIVE) { - seq_printf(s, " dur=%d", srp->duration); - goto fin_line; - } - ms = jiffies_to_msecs(jiffies); - seq_printf(s, " t_o/elap=%d/%d", - (is_v3 ? sh3p->timeout : - jiffies_to_msecs(fp->timeout)), - (ms > srp->duration ? ms - srp->duration : - 0)); -fin_line: - seq_printf(s, "ms sgat=%d op=0x%02x\n", - srp->sgat_h.num_sgat, (int)srp->cmd_opcode); - } - if (xa_empty(&fp->srp_arr)) - seq_puts(s, " No requests active\n"); + ++*countp; + n += scnprintf(obp + n, len - n, " FD(%d): ", *countp); + n += sg_proc_debug_fd(fp, obp + n, len - n, idx); } + return n; } +/* Called via dbg_seq_ops once for each sg device */ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) { + bool found = false; + bool trunc = false; + const int bp_len = SG_PROC_DEBUG_SZ; + int n = 0; + int k = 0; + unsigned long iflags; struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; - unsigned long iflags; + int *fdi_p; + char *bp; + char *disk_name; + char b1[128]; + b1[0] = '\0'; if (it && (0 == it->index)) seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", - (int)it->max, sg_big_buff); - + (int)it->max, def_reserved_size); + fdi_p = it ? &it->fd_index : &k; + bp = kzalloc(bp_len, __GFP_NOWARN | GFP_KERNEL); + if (!bp) { + seq_printf(s, "%s: Unable to allocate %d on heap, finish\n", + __func__, bp_len); + return -ENOMEM; + } read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; if (NULL == sdp) goto skip; if (!xa_empty(&sdp->sfp_arr)) { - seq_printf(s, " >>> device=%s ", sdp->name); + found = true; + disk_name = (sdp->name[0] ? sdp->name : "?_?"); if (SG_IS_DETACHING(sdp)) - seq_puts(s, "detaching pending close "); + snprintf(b1, sizeof(b1), " >>> device=%s %s\n", + disk_name, "detaching pending close\n"); else if (sdp->device) { - struct scsi_device *scsidp = sdp->device; - - seq_printf(s, "%d:%d:%d:%llu em=%d", - scsidp->host->host_no, - scsidp->channel, scsidp->id, - scsidp->lun, - scsidp->host->hostt->emulated); + n = sg_proc_debug_sdev(sdp, bp, bp_len, fdi_p); + if (n >= bp_len - 1) { + trunc = true; + if (bp[bp_len - 2] != '\n') + bp[bp_len - 2] = '\n'; + } + } else { + snprintf(b1, sizeof(b1), " >>> device=%s %s\n", + disk_name, "sdp->device==NULL, skip"); } - seq_printf(s, " max_sgat_sz=%d excl=%d open_cnt=%d\n", - sdp->max_sgat_sz, SG_HAVE_EXCLUDE(sdp), - atomic_read(&sdp->open_cnt)); - sg_proc_debug_helper(s, sdp); } skip: read_unlock_irqrestore(&sg_index_lock, iflags); + if (found) { + if (n > 0) { + seq_puts(s, bp); + if (seq_has_overflowed(s)) + goto s_ovfl; + if (trunc) + seq_printf(s, " >> Output truncated %s\n", + "due to buffer size"); + } else if (b1[0]) { + seq_puts(s, b1); + if (seq_has_overflowed(s)) + goto s_ovfl; + } + } +s_ovfl: + kfree(bp); return 0; } -#endif /* CONFIG_SCSI_PROC_FS (~300 lines back) */ +#endif /* CONFIG_SCSI_PROC_FS (~400 lines back) */ module_init(init_sg); module_exit(exit_sg); From patchwork Mon Oct 24 03:20:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016554 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9E7B5C3A59D for ; Mon, 24 Oct 2022 03:25:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229717AbiJXDZ2 (ORCPT ); Sun, 23 Oct 2022 23:25:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38344 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230265AbiJXDXB (ORCPT ); Sun, 23 Oct 2022 23:23:01 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 492AF2A73B for ; Sun, 23 Oct 2022 20:21:45 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id F371920423F; Mon, 24 Oct 2022 05:21:42 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 8y3ClyGzQl8c; Mon, 24 Oct 2022 05:21:42 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id AC40A2041AF; Mon, 24 Oct 2022 05:21:41 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 28/44] sg: add 8 byte SCSI LUN to sg_scsi_id Date: Sun, 23 Oct 2022 23:20:42 -0400 Message-Id: <20221024032058.14077-29-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org The existing ioctl(SG_GET_SCSI_ID) fills a object of type struct sg_scsi_id whose last field is int unused[2]. Add an anonymous union with u8 scsi_lun[8] sharing those last 8 bytes. This patch will place the current device's full LUN in the scsi_lun array using T10's preferred LUN format (i.e. an array of 8 bytes) when ioctl(SG_GET_SCSI_ID) is called. Note that structure already contains a 'lun' field but that is a 32 bit integer. Users of this upgrade should choose the scsi_lun array field henceforth but existing code can remain as it is and will get the same 'lun' value with the version 3 or version 4 driver. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 8 +++++--- include/uapi/scsi/sg.h | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 264955e660f3..4c08e62f959a 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1211,7 +1211,7 @@ sg_receive_v4(struct sg_fd *sfp, struct sg_request *srp, void __user *p, h4p->din_resid = srp->in_resid; h4p->dout_resid = srp->s_hdr4.out_resid; h4p->usr_ptr = srp->s_hdr4.usr_ptr; - h4p->response = (u64)srp->s_hdr4.sbp; + h4p->response = (uintptr_t)srp->s_hdr4.sbp; h4p->request_extra = srp->pack_id; if (p) { if (copy_to_user(p, h4p, SZ_SG_IO_V4)) @@ -1773,6 +1773,7 @@ static int sg_ctl_scsi_id(struct scsi_device *sdev, struct sg_fd *sfp, void __user *p) { struct sg_scsi_id ss_id; + struct scsi_lun lun8b; SG_LOG(3, sfp, "%s: SG_GET_SCSI_ID\n", __func__); ss_id.host_no = sdev->host->host_no; @@ -1782,8 +1783,9 @@ sg_ctl_scsi_id(struct scsi_device *sdev, struct sg_fd *sfp, void __user *p) ss_id.scsi_type = sdev->type; ss_id.h_cmd_per_lun = sdev->host->cmd_per_lun; ss_id.d_queue_depth = sdev->queue_depth; - ss_id.unused[0] = 0; - ss_id.unused[1] = 0; + int_to_scsilun(sdev->lun, &lun8b); + /* ss_id.scsi_lun is in an anonymous union with 'int unused[2]' */ + memcpy(ss_id.scsi_lun, lun8b.scsi_lun, 8); if (copy_to_user(p, &ss_id, sizeof(struct sg_scsi_id))) return -EFAULT; return 0; diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 217b574c27ab..1425b41b2d83 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -169,7 +169,10 @@ typedef struct sg_scsi_id { int scsi_type; /* TYPE_... defined in scsi/scsi.h */ short h_cmd_per_lun;/* host (adapter) maximum commands per lun */ short d_queue_depth;/* device (or adapter) maximum queue length */ - int unused[2]; + union { + int unused[2]; /* as per version 3 driver */ + __u8 scsi_lun[8]; /* full 8 byte SCSI LUN [in v4 driver] */ + }; } sg_scsi_id_t; /* For backward compatibility v4 driver yields at most SG_MAX_QUEUE of these */ From patchwork Mon Oct 24 03:20:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016555 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3245AC433FE for ; Mon, 24 Oct 2022 03:25:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229967AbiJXDZb (ORCPT ); Sun, 23 Oct 2022 23:25:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230121AbiJXDXC (ORCPT ); Sun, 23 Oct 2022 23:23:02 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A72AE5E677 for ; Sun, 23 Oct 2022 20:21:46 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 916F2204245; Mon, 24 Oct 2022 05:21:44 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id RKc8cXTItNlN; Mon, 24 Oct 2022 05:21:44 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 2F6BB2041AF; Mon, 24 Oct 2022 05:21:43 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Hannes Reinecke Subject: [PATCH v25 29/44] sg: expand sg_comm_wr_t Date: Sun, 23 Oct 2022 23:20:43 -0400 Message-Id: <20221024032058.14077-30-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org The internal struct sg_comm_wr_t was added when the number of arguments to sg_common_write() became excessive. Expand this idea so multiple calls to sg_fetch_cmnd() can be deferred until a scsi_request object is ready to receive the command. This saves a 252 byte stack allocation on every submit path. Prior to this and a few other changes, the kernel infrastructure was warning about excessive stack usage. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 162 ++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 79 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 4c08e62f959a..f680ccb9f9df 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -255,28 +255,30 @@ struct sg_device { /* holds the state of each scsi generic device */ struct sg_comm_wr_t { /* arguments to sg_common_write() */ int timeout; + int cmd_len; unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ union { /* selector is frq_bm.SG_FRQ_IS_V4I */ struct sg_io_hdr *h3p; struct sg_io_v4 *h4p; }; - u8 *cmnd; + struct sg_fd *sfp; + struct file *filp; + const u8 __user *u_cmdp; }; /* tasklet or soft irq callback */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); -static int sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, - struct sg_io_v4 *h4p, int dxfer_dir); +static int sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, + int dxfer_dir); static void sg_finish_scsi_blk_rq(struct sg_request *srp); static int sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen); static void sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp); static int sg_v3_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, bool sync, struct sg_request **o_srp); -static struct sg_request *sg_common_write(struct sg_fd *sfp, - struct sg_comm_wr_t *cwrp); +static struct sg_request *sg_common_write(struct sg_comm_wr_t *cwrp); static int sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer); static void sg_remove_sgat(struct sg_request *srp); @@ -284,8 +286,8 @@ static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int id, bool *busy); -static struct sg_request *sg_setup_req(struct sg_fd *sfp, int dxfr_len, - struct sg_comm_wr_t *cwrp); +static struct sg_request *sg_setup_req(struct sg_comm_wr_t *cwrp, + int dxfr_len); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); static struct sg_device *sg_get_dev(int min_dev); static void sg_device_destroy(struct kref *kref); @@ -571,7 +573,6 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - u8 cmnd[SG_MAX_CDB_SIZE]; struct sg_header ov2hdr; struct sg_io_hdr v3hdr; struct sg_header *ohp = &ov2hdr; @@ -683,9 +684,6 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) h3p->flags = input_size; /* structure abuse ... */ h3p->pack_id = ohp->pack_id; h3p->usr_ptr = NULL; - cmnd[0] = opcode; - if (copy_from_user(cmnd + 1, p + 1, cmd_size - 1)) - return -EFAULT; /* * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV, * but it is possible that the app intended SG_DXFER_TO_DEV, because @@ -697,13 +695,16 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) "%s: data in/out %d/%d bytes for SCSI command 0x%x-- guessing data in;\n" " program %s not setting count and/or reply_len properly\n", __func__, ohp->reply_len - (int)SZ_SG_HEADER, - input_size, (unsigned int)cmnd[0], current->comm); + input_size, (unsigned int)opcode, current->comm); } - cwr.frq_bm[0] = 0; /* initial state clear for all req flags */ cwr.h3p = h3p; + cwr.frq_bm[0] = 0; cwr.timeout = sfp->timeout; - cwr.cmnd = cmnd; - srp = sg_common_write(sfp, &cwr); + cwr.cmd_len = cmd_size; + cwr.filp = filp; + cwr.sfp = sfp; + cwr.u_cmdp = p; + srp = sg_common_write(&cwr); return (IS_ERR(srp)) ? PTR_ERR(srp) : (int)count; } @@ -745,31 +746,29 @@ static int sg_v3_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, bool sync, struct sg_request **o_srp) { - int res, timeout; unsigned long ul_timeout; struct sg_request *srp; struct sg_comm_wr_t cwr; - u8 cmnd[SG_MAX_CDB_SIZE]; /* now doing v3 blocking (sync) or non-blocking submission */ if (hp->flags & SG_FLAG_MMAP_IO) { - res = sg_chk_mmap(sfp, hp->flags, hp->dxfer_len); + int res = sg_chk_mmap(sfp, hp->flags, hp->dxfer_len); + if (res) return res; } /* when v3 seen, allow cmd_q on this fd (def: no cmd_q) */ set_bit(SG_FFD_CMD_Q, sfp->ffd_bm); ul_timeout = msecs_to_jiffies(hp->timeout); - timeout = min_t(unsigned long, ul_timeout, INT_MAX); - res = sg_fetch_cmnd(filp, sfp, hp->cmdp, hp->cmd_len, cmnd); - if (res) - return res; cwr.frq_bm[0] = 0; __assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); cwr.h3p = hp; - cwr.timeout = timeout; - cwr.cmnd = cmnd; - srp = sg_common_write(sfp, &cwr); + cwr.timeout = min_t(unsigned long, ul_timeout, INT_MAX); + cwr.cmd_len = hp->cmd_len; + cwr.filp = filp; + cwr.sfp = sfp; + cwr.u_cmdp = hp->cmdp; + srp = sg_common_write(&cwr); if (IS_ERR(srp)) return PTR_ERR(srp); if (o_srp) @@ -781,11 +780,10 @@ static int sg_submit_v4(struct file *filp, struct sg_fd *sfp, void __user *p, struct sg_io_v4 *h4p, bool sync, struct sg_request **o_srp) { - int timeout, res; + int res = 0; unsigned long ul_timeout; struct sg_request *srp; struct sg_comm_wr_t cwr; - u8 cmnd[SG_MAX_CDB_SIZE]; if (h4p->flags & SG_FLAG_MMAP_IO) { int len = 0; @@ -801,18 +799,16 @@ sg_submit_v4(struct file *filp, struct sg_fd *sfp, void __user *p, /* once v4 (or v3) seen, allow cmd_q on this fd (def: no cmd_q) */ set_bit(SG_FFD_CMD_Q, sfp->ffd_bm); ul_timeout = msecs_to_jiffies(h4p->timeout); - timeout = min_t(unsigned long, ul_timeout, INT_MAX); - res = sg_fetch_cmnd(filp, sfp, cuptr64(h4p->request), h4p->request_len, - cmnd); - if (res) - return res; + cwr.filp = filp; + cwr.sfp = sfp; cwr.frq_bm[0] = 0; - assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); - set_bit(SG_FRQ_IS_V4I, cwr.frq_bm); + __assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); + __set_bit(SG_FRQ_IS_V4I, cwr.frq_bm); cwr.h4p = h4p; - cwr.timeout = timeout; - cwr.cmnd = cmnd; - srp = sg_common_write(sfp, &cwr); + cwr.timeout = min_t(unsigned long, ul_timeout, INT_MAX); + cwr.cmd_len = h4p->request_len; + cwr.u_cmdp = cuptr64(h4p->request); + srp = sg_common_write(&cwr); if (IS_ERR(srp)) return PTR_ERR(srp); if (o_srp) @@ -985,13 +981,14 @@ sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) * N.B. pack_id placed in sg_io_v4::request_extra field. */ static struct sg_request * -sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) +sg_common_write(struct sg_comm_wr_t *cwrp) { int res = 0; - int dxfr_len, dir, cmd_len; + int dxfr_len, dir; int pack_id = SG_PACK_ID_WILDCARD; u32 rq_flags; - struct sg_device *sdp = sfp->parentdp; + struct sg_fd *fp = cwrp->sfp; + struct sg_device *sdp = fp->parentdp; struct sg_request *srp; struct sg_io_hdr *hi_p; struct sg_io_v4 *h4p; @@ -1023,40 +1020,36 @@ sg_common_write(struct sg_fd *sfp, struct sg_comm_wr_t *cwrp) if (dxfr_len >= SZ_256M) return ERR_PTR(-EINVAL); - srp = sg_setup_req(sfp, dxfr_len, cwrp); + srp = sg_setup_req(cwrp, dxfr_len); if (IS_ERR(srp)) return srp; srp->rq_flags = rq_flags; srp->pack_id = pack_id; if (h4p) { - memset(&srp->s_hdr4, 0, sizeof(srp->s_hdr4)); srp->s_hdr4.usr_ptr = h4p->usr_ptr; srp->s_hdr4.sbp = uptr64(h4p->response); srp->s_hdr4.max_sb_len = h4p->max_response_len; srp->s_hdr4.cmd_len = h4p->request_len; srp->s_hdr4.dir = dir; - cmd_len = h4p->request_len; + srp->s_hdr4.out_resid = 0; } else { /* v3 interface active */ - cmd_len = hi_p->cmd_len; memcpy(&srp->s_hdr3, hi_p, sizeof(srp->s_hdr3)); } - srp->cmd_opcode = cwrp->cmnd[0];/* hold opcode of command for debug */ - SG_LOG(4, sfp, "%s: opcode=0x%02x, cdb_sz=%d, pack_id=%d\n", __func__, - (int)cwrp->cmnd[0], cmd_len, pack_id); - - res = sg_start_req(srp, cwrp->cmnd, cmd_len, h4p, dir); + res = sg_start_req(srp, cwrp, dir); if (res < 0) /* probably out of space --> -ENOMEM */ goto err_out; + SG_LOG(4, fp, "%s: opcode=0x%02x, cdb_sz=%d, pack_id=%d\n", __func__, + srp->cmd_opcode, cwrp->cmd_len, pack_id); if (unlikely(SG_IS_DETACHING(sdp))) { res = -ENODEV; goto err_out; } srp->rq->timeout = cwrp->timeout; - sg_execute_cmd(sfp, srp); + sg_execute_cmd(fp, srp); return srp; err_out: - sg_deact_request(sfp, srp); + sg_deact_request(fp, srp); return ERR_PTR(res); } @@ -1270,8 +1263,8 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) id, &busy)); if (unlikely(!busy && SG_IS_DETACHING(sdp))) return -ENODEV; - if (res) /* -ERESTARTSYS as signal hit process */ - return res; + if (res) + return res; /* signal --> -ERESTARTSYS */ } return sg_receive_v4(sfp, srp, p, h4p); } @@ -2639,7 +2632,7 @@ init_sg(void) } sg_sysfs_valid = true; rc = scsi_register_interface(&sg_interface); - if (0 == rc) { + if (rc == 0) { sg_proc_init(); return 0; } @@ -2682,11 +2675,10 @@ sg_chk_dio_allowed(struct sg_device *sdp, struct sg_request *srp, return false; } -static void +static inline void sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, struct rq_map_data *mdp) { - memset(mdp, 0, sizeof(*mdp)); mdp->pages = schp->pages; mdp->page_order = schp->page_order; mdp->nr_entries = schp->num_sgat; @@ -2695,8 +2687,7 @@ sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, } static int -sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, - struct sg_io_v4 *h4p, int dxfer_dir) +sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) { bool reserved, us_xfer; int res = 0; @@ -2706,7 +2697,7 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, void __user *up; struct request *rq; struct scsi_cmnd *scmd; - struct sg_fd *sfp = srp->parentfp; + struct sg_fd *sfp = cwrp->sfp; struct sg_device *sdp; struct sg_scatter_hold *req_schp; struct request_queue *q; @@ -2715,7 +2706,9 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, struct rq_map_data map_data; sdp = sfp->parentdp; - if (h4p) { + if (test_bit(SG_FRQ_IS_V4I, srp->frq_bm)) { + struct sg_io_v4 *h4p = cwrp->h4p; + if (dxfer_dir == SG_DXFER_TO_DEV) { r0w = WRITE; up = uptr64(h4p->dout_xferp); @@ -2758,8 +2751,15 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, scmd = blk_mq_rq_to_pdu(rq); srp->rq = rq; - memcpy(scmd->cmnd, cmd, cmd_len); - scmd->cmd_len = cmd_len; + if (cwrp->u_cmdp) + res = sg_fetch_cmnd(cwrp->filp, sfp, cwrp->u_cmdp, + cwrp->cmd_len, scmd->cmnd); + else + res = -EPROTO; + if (res) + goto fini; + scmd->cmd_len = cwrp->cmd_len; + srp->cmd_opcode = scmd->cmnd[0]; us_xfer = !(srp->rq_flags & SG_FLAG_NO_DXFER); assign_bit(SG_FRQ_NO_US_XFER, srp->frq_bm, !us_xfer); reserved = (sfp->rsv_srp == srp); @@ -2782,7 +2782,7 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, } if (likely(md)) { /* normal, "indirect" IO */ - if (unlikely((srp->rq_flags & SG_FLAG_MMAP_IO))) { + if (unlikely(srp->rq_flags & SG_FLAG_MMAP_IO)) { /* mmap IO must use and fit in reserve request */ if (!reserved || dxfer_len > req_schp->buflen) res = reserved ? -ENOMEM : -EBUSY; @@ -2791,7 +2791,7 @@ sg_start_req(struct sg_request *srp, u8 *cmd, int cmd_len, res = sg_mk_sgat(&srp->sgat_h, sfp, up_sz); } - if (res) + if (unlikely(res)) goto fini; sg_set_map_data(req_schp, !!up, md); @@ -3109,7 +3109,7 @@ sg_mk_srp_sgat(struct sg_fd *sfp, bool first, int db_len) return n_srp; if (db_len > 0) { res = sg_mk_sgat(&n_srp->sgat_h, sfp, db_len); - if (res) { + if (unlikely(res)) { kfree(n_srp); return ERR_PTR(res); } @@ -3162,18 +3162,18 @@ sg_build_reserve(struct sg_fd *sfp, int buflen) * failure returns a negated errno value twisted by ERR_PTR() macro. */ static struct sg_request * -sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) +sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) { bool act_empty = false; bool found = false; - bool mk_new_srp = false; + bool mk_new_srp = true; bool try_harder = false; - int res; int num_inactive = 0; unsigned long idx, last_idx, iflags; + struct sg_fd *fp = cwrp->sfp; struct sg_request *r_srp = NULL; /* request to return */ struct sg_request *last_srp = NULL; - struct xarray *xafp = &sfp->srp_arr; + struct xarray *xafp = &fp->srp_arr; __maybe_unused const char *cp; start_again: @@ -3226,18 +3226,19 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) mk_new_srp = true; } if (mk_new_srp) { - bool allow_cmd_q = test_bit(SG_FFD_CMD_Q, sfp->ffd_bm); + bool allow_cmd_q = test_bit(SG_FFD_CMD_Q, fp->ffd_bm); + int res; u32 n_idx; struct xa_limit xal = { .max = 0, .min = 0 }; cp = "new"; - if (!allow_cmd_q && atomic_read(&sfp->submitted) > 0) { + if (!allow_cmd_q && atomic_read(&fp->submitted) > 0) { r_srp = ERR_PTR(-EDOM); - SG_LOG(6, sfp, "%s: trying 2nd req but cmd_q=false\n", + SG_LOG(6, fp, "%s: trying 2nd req but cmd_q=false\n", __func__); goto fini; } - r_srp = sg_mk_srp_sgat(sfp, act_empty, dxfr_len); + r_srp = sg_mk_srp_sgat(fp, act_empty, dxfr_len); if (IS_ERR(r_srp)) { if (!try_harder && dxfr_len < SG_DEF_SECTOR_SZ && num_inactive > 0) { @@ -3248,11 +3249,11 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) } atomic_set(&r_srp->rq_st, SG_RS_BUSY); xa_lock_irqsave(xafp, iflags); - xal.max = atomic_inc_return(&sfp->req_cnt); + xal.max = atomic_inc_return(&fp->req_cnt); res = __xa_alloc(xafp, &n_idx, r_srp, xal, GFP_KERNEL); xa_unlock_irqrestore(xafp, iflags); if (res < 0) { - SG_LOG(1, sfp, "%s: xa_alloc() failed, errno=%d\n", + SG_LOG(1, fp, "%s: xa_alloc() failed, errno=%d\n", __func__, -res); sg_remove_sgat(r_srp); kfree(r_srp); @@ -3261,17 +3262,18 @@ sg_setup_req(struct sg_fd *sfp, int dxfr_len, struct sg_comm_wr_t *cwrp) } idx = n_idx; r_srp->rq_idx = idx; - r_srp->parentfp = sfp; - SG_LOG(4, sfp, "%s: mk_new_srp=0x%pK ++\n", __func__, r_srp); + r_srp->parentfp = fp; + SG_LOG(4, fp, "%s: mk_new_srp=0x%pK ++\n", __func__, r_srp); } r_srp->frq_bm[0] = cwrp->frq_bm[0]; /* assumes <= 32 req flags */ r_srp->sgat_h.dlen = dxfr_len;/* must be <= r_srp->sgat_h.buflen */ r_srp->cmd_opcode = 0xff; /* set invalid opcode (VS), 0x0 is TUR */ fini: if (IS_ERR(r_srp)) - SG_LOG(1, sfp, "%s: err=%ld\n", __func__, PTR_ERR(r_srp)); + SG_LOG(1, fp, "%s: err=%ld\n", __func__, PTR_ERR(r_srp)); if (!IS_ERR(r_srp)) - SG_LOG(4, sfp, "%s: %s r_srp=0x%pK\n", __func__, cp, r_srp); + SG_LOG(4, fp, "%s: %s %sr_srp=0x%pK\n", __func__, cp, + ((r_srp == fp->rsv_srp) ? "[rsv] " : ""), r_srp); return r_srp; } @@ -3326,6 +3328,8 @@ sg_add_sfp(struct sg_device *sdp) __assign_bit(SG_FFD_CMD_Q, sfp->ffd_bm, SG_DEF_COMMAND_Q); __assign_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm, SG_DEF_KEEP_ORPHAN); __assign_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm, SG_DEFAULT_Q_AT); + atomic_set(&sfp->submitted, 0); + atomic_set(&sfp->waiting, 0); /* * SG_SCATTER_SZ initializes scatter_elem_sz but different value may * be given as driver/module parameter (e.g. 'scatter_elem_sz=8192'). From patchwork Mon Oct 24 03:20:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016564 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6833C433FE for ; Mon, 24 Oct 2022 03:25:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230418AbiJXDZx (ORCPT ); Sun, 23 Oct 2022 23:25:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230096AbiJXDXB (ORCPT ); Sun, 23 Oct 2022 23:23:01 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A6CED5E655 for ; Sun, 23 Oct 2022 20:21:47 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 0535F2041BB; Mon, 24 Oct 2022 05:21:46 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id KOMqKY60JUea; Mon, 24 Oct 2022 05:21:45 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id BE3282041AF; Mon, 24 Oct 2022 05:21:44 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 30/44] sg: add sg_iosubmit_v3 and sg_ioreceive_v3 ioctls Date: Sun, 23 Oct 2022 23:20:44 -0400 Message-Id: <20221024032058.14077-31-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Add ioctl(SG_IOSUBMIT_V3) and ioctl(SG_IORECEIVE_V3). These ioctls are meant to be (almost) drop-in replacements for the write()/read() async version 3 interface. They only accept the version 3 interface. See the webpage at: https://sg.danny.cz/sg/sg_v40.html specifically the table in the section titled: "13 SG interface support changes". If sgv3 is a struct sg_io_hdr object, suitably configured, then res = write(sg_fd, &sgv3, sizeof(sgv3)); and res = ioctl(sg_fd, SG_IOSUBMIT_V3, &sgv3); are equivalent. Dito for read() and ioctl(SG_IORECEIVE_V3). Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 75 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/scsi/sg.h | 6 ++++ 2 files changed, 81 insertions(+) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index f680ccb9f9df..cda03d2bc674 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -834,6 +834,24 @@ sg_ctl_iosubmit(struct file *filp, struct sg_fd *sfp, void __user *p) return -EPERM; } +static int +sg_ctl_iosubmit_v3(struct file *filp, struct sg_fd *sfp, void __user *p) +{ + int res; + u8 hdr_store[SZ_SG_IO_V4]; /* max(v3interface, v4interface) */ + struct sg_io_hdr *h3p = (struct sg_io_hdr *)hdr_store; + struct sg_device *sdp = sfp->parentdp; + + res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK)); + if (unlikely(res)) + return res; + if (copy_from_user(h3p, p, SZ_SG_IO_HDR)) + return -EFAULT; + if (h3p->interface_id == 'S') + return sg_v3_submit(filp, sfp, h3p, false, NULL); + return -EPERM; +} + #if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) static void sg_rq_state_fail_msg(struct sg_fd *sfp, enum sg_rq_state exp_old_st, @@ -1153,6 +1171,7 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, hp->sb_len_wr = srp->sense_len; hp->info = srp->rq_info; hp->resid = srp->in_resid; + hp->pack_id = srp->pack_id; hp->duration = srp->duration; hp->status = rq_result & 0xff; hp->masked_status = status_byte(rq_result); @@ -1269,6 +1288,56 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) return sg_receive_v4(sfp, srp, p, h4p); } +/* + * Called when ioctl(SG_IORECEIVE_V3) received. Expects a v3 interface. + * Checks if O_NONBLOCK file flag given, if not checks given flags field + * to see if SGV4_FLAG_IMMED is set. Either of these implies non blocking. + * When non-blocking and there is no request waiting, yields EAGAIN; + * otherwise it waits. + */ +static int +sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) +{ + bool busy; + bool non_block = !!(filp->f_flags & O_NONBLOCK); + int res; + int pack_id = SG_PACK_ID_WILDCARD; + u8 v3_holder[SZ_SG_IO_HDR]; + struct sg_io_hdr *h3p = (struct sg_io_hdr *)v3_holder; + struct sg_device *sdp = sfp->parentdp; + struct sg_request *srp; + + res = sg_allow_if_err_recovery(sdp, non_block); + if (unlikely(res)) + return res; + /* Get first three 32 bit integers: guard, proto+subproto */ + if (copy_from_user(h3p, p, SZ_SG_IO_HDR)) + return -EFAULT; + /* for v3: interface_id=='S' (in a 32 bit int) */ + if (h3p->interface_id != 'S') + return -EPERM; + if (h3p->flags & SGV4_FLAG_IMMED) + non_block = true; /* set by either this or O_NONBLOCK */ + SG_LOG(3, sfp, "%s: non_block(+IMMED)=%d\n", __func__, non_block); + + if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm)) + pack_id = h3p->pack_id; + + srp = sg_find_srp_by_id(sfp, pack_id, &busy); + while (!srp) { /* nothing available so wait on packet or */ + if (unlikely(!busy && SG_IS_DETACHING(sdp))) + return -ENODEV; + if (non_block) + return -EAGAIN; + res = wait_event_interruptible + (sfp->read_wait, + sg_get_ready_srp(sfp, &srp, pack_id, &busy)); + if (unlikely(res)) + return res; /* signal --> -ERESTARTSYS */ + } + return sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, p); +} + static int sg_read_v1v2(void __user *buf, int count, struct sg_fd *sfp, struct sg_request *srp) @@ -1809,9 +1878,15 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, case SG_IOSUBMIT: SG_LOG(3, sfp, "%s: SG_IOSUBMIT\n", __func__); return sg_ctl_iosubmit(filp, sfp, p); + case SG_IOSUBMIT_V3: + SG_LOG(3, sfp, "%s: SG_IOSUBMIT_V3\n", __func__); + return sg_ctl_iosubmit_v3(filp, sfp, p); case SG_IORECEIVE: SG_LOG(3, sfp, "%s: SG_IORECEIVE\n", __func__); return sg_ctl_ioreceive(filp, sfp, p); + case SG_IORECEIVE_V3: + SG_LOG(3, sfp, "%s: SG_IORECEIVE_V3\n", __func__); + return sg_ctl_ioreceive_v3(filp, sfp, p); case SG_GET_SCSI_ID: return sg_ctl_scsi_id(sdev, sfp, p); case SG_SET_FORCE_PACK_ID: diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 1425b41b2d83..9060c40957f9 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -390,6 +390,12 @@ struct sg_header { /* Gives some v4 identifying info to driver, receives associated response */ #define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4) +/* Submits a v3 interface object to driver */ +#define SG_IOSUBMIT_V3 _IOWR(SG_IOCTL_MAGIC_NUM, 0x45, struct sg_io_hdr) + +/* Gives some v3 identifying info to driver, receives associated response */ +#define SG_IORECEIVE_V3 _IOWR(SG_IOCTL_MAGIC_NUM, 0x46, struct sg_io_hdr) + /* command queuing is always on when the v3 or v4 interface is used */ #define SG_DEF_COMMAND_Q 0 From patchwork Mon Oct 24 03:20:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016565 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 09B63FA373D for ; Mon, 24 Oct 2022 03:25:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230462AbiJXDZ4 (ORCPT ); Sun, 23 Oct 2022 23:25:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58694 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230129AbiJXDXC (ORCPT ); Sun, 23 Oct 2022 23:23:02 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 869186EF22 for ; Sun, 23 Oct 2022 20:21:48 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id C75482041BD; Mon, 24 Oct 2022 05:21:47 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id t-r1z-NZMvEK; Mon, 24 Oct 2022 05:21:47 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 417662041AF; Mon, 24 Oct 2022 05:21:46 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 31/44] sg: move procfs objects to avoid forward decls Date: Sun, 23 Oct 2022 23:20:45 -0400 Message-Id: <20221024032058.14077-32-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Move the procfs related file_operations and seq_operations definitions toward the end of the source file to minimize the need for forward declarations of the functions they name. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 133 +++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 73 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index cda03d2bc674..735ea5d11b33 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -3630,79 +3630,6 @@ sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) #endif #if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* long, almost to end of file */ -static int sg_proc_seq_show_int(struct seq_file *s, void *v); - -static int sg_proc_single_open_adio(struct inode *inode, struct file *filp); -static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, - size_t count, loff_t *off); -static const struct proc_ops adio_proc_ops = { - .proc_open = sg_proc_single_open_adio, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_write = sg_proc_write_adio, - .proc_release = single_release, -}; - -static int sg_proc_single_open_dressz(struct inode *inode, struct file *filp); -static ssize_t sg_proc_write_dressz(struct file *filp, - const char __user *buffer, size_t count, loff_t *off); -static const struct proc_ops dressz_proc_ops = { - .proc_open = sg_proc_single_open_dressz, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_write = sg_proc_write_dressz, - .proc_release = single_release, -}; - -static int sg_proc_seq_show_version(struct seq_file *s, void *v); -static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v); -static int sg_proc_seq_show_dev(struct seq_file *s, void *v); -static void * dev_seq_start(struct seq_file *s, loff_t *pos); -static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos); -static void dev_seq_stop(struct seq_file *s, void *v); -static const struct seq_operations dev_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = sg_proc_seq_show_dev, -}; - -static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v); -static const struct seq_operations devstrs_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = sg_proc_seq_show_devstrs, -}; - -static int sg_proc_seq_show_debug(struct seq_file *s, void *v); -static const struct seq_operations debug_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = sg_proc_seq_show_debug, -}; - -static int -sg_proc_init(void) -{ -#if IS_ENABLED(CONFIG_SCSI_PROC_FS) - struct proc_dir_entry *p; - - p = proc_mkdir("scsi/sg", NULL); - if (!p) - return 1; - - proc_create("allow_dio", 0644, p, &adio_proc_ops); - proc_create_seq("debug", 0444, p, &debug_seq_ops); - proc_create("def_reserved_size", 0644, p, &dressz_proc_ops); - proc_create_single("device_hdr", 0444, p, sg_proc_seq_show_devhdr); - proc_create_seq("devices", 0444, p, &dev_seq_ops); - proc_create_seq("device_strs", 0444, p, &devstrs_seq_ops); - proc_create_single("version", 0444, p, sg_proc_seq_show_version); -#endif - return 0; -} static int sg_last_dev(void) @@ -4073,6 +4000,66 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) return 0; } +static const struct proc_ops adio_proc_ops = { + .proc_open = sg_proc_single_open_adio, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = sg_proc_write_adio, + .proc_release = single_release, +}; + +static const struct proc_ops dressz_proc_ops = { + .proc_open = sg_proc_single_open_dressz, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = sg_proc_write_dressz, + .proc_release = single_release, +}; + +static const struct seq_operations dev_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = sg_proc_seq_show_dev, +}; + +static const struct seq_operations devstrs_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = sg_proc_seq_show_devstrs, +}; + +static const struct seq_operations debug_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = sg_proc_seq_show_debug, +}; + +static int +sg_proc_init(void) +{ +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) + struct proc_dir_entry *p; + + p = proc_mkdir("scsi/sg", NULL); + if (!p) + return 1; + + proc_create("allow_dio", 0644, p, &adio_proc_ops); + proc_create_seq("debug", 0444, p, &debug_seq_ops); + proc_create("def_reserved_size", 0644, p, &dressz_proc_ops); + proc_create_single("device_hdr", 0444, p, sg_proc_seq_show_devhdr); + proc_create_seq("devices", 0444, p, &dev_seq_ops); + proc_create_seq("device_strs", 0444, p, &devstrs_seq_ops); + proc_create_single("version", 0444, p, sg_proc_seq_show_version); +#endif + return 0; +} + +/* remove_proc_subtree("scsi/sg", NULL) in exit_sg() does cleanup */ + #endif /* CONFIG_SCSI_PROC_FS (~400 lines back) */ module_init(init_sg); From patchwork Mon Oct 24 03:20:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016559 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8DF5DC3A59D for ; Mon, 24 Oct 2022 03:25:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230060AbiJXDZk (ORCPT ); Sun, 23 Oct 2022 23:25:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58602 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230270AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 1792A5F227 for ; Sun, 23 Oct 2022 20:21:49 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 704272041C0; Mon, 24 Oct 2022 05:21:49 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id d8hhtBp2FEZ0; Mon, 24 Oct 2022 05:21:49 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 1B5422041AF; Mon, 24 Oct 2022 05:21:47 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 32/44] sg: protect multiple receivers Date: Sun, 23 Oct 2022 23:20:46 -0400 Message-Id: <20221024032058.14077-33-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org If two threads call ioctl(SG_IORECEIVE) [or read()] on the same file descriptor there is a potential race on the same request response. Use atomic bit operations to make sure only one thread gets each request response. [The other thread will either get another request response or nothing.] Also make sfp cleanup a bit more robust and report if the number of submitted requests (which are decremented when completed) is other than the expected value of zero. In the case of sg_wait_open_event() which calls mutex_unlock on sdp->open_rel_lock and later calls mutex_lock on the same lock; this macro is needed to stop sparse complaining. In other cases it is a reminder to the coder (a precondition). Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 53 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 735ea5d11b33..5079a449111e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -112,6 +112,7 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_FRQ_SYNC_INVOC 2 /* synchronous (blocking) invocation */ #define SG_FRQ_NO_US_XFER 3 /* no user space transfer of data */ #define SG_FRQ_DEACT_ORPHAN 6 /* not keeping orphan so de-activate */ +#define SG_FRQ_RECEIVING 7 /* guard against multiple receivers */ /* Bit positions (flags) for sg_fd::ffd_bm bitmask follow */ #define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ @@ -374,6 +375,7 @@ sg_check_file_access(struct file *filp, const char *caller) static int sg_wait_open_event(struct sg_device *sdp, bool o_excl) + __must_hold(sdp->open_rel_lock) { int res = 0; @@ -1271,6 +1273,7 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) SG_LOG(3, sfp, "%s: non_block(+IMMED)=%d\n", __func__, non_block); /* read in part of v3 or v4 header for pack_id or tag based find */ id = pack_id; +try_again: srp = sg_find_srp_by_id(sfp, id, &busy); while (!srp) { /* nothing available so wait on packet or */ if (unlikely(!busy && SG_IS_DETACHING(sdp))) @@ -1285,6 +1288,10 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) if (res) return res; /* signal --> -ERESTARTSYS */ } + if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { + cpu_relax(); + goto try_again; + } return sg_receive_v4(sfp, srp, p, h4p); } @@ -1322,7 +1329,7 @@ sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm)) pack_id = h3p->pack_id; - +try_again: srp = sg_find_srp_by_id(sfp, pack_id, &busy); while (!srp) { /* nothing available so wait on packet or */ if (unlikely(!busy && SG_IS_DETACHING(sdp))) @@ -1335,6 +1342,10 @@ sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) if (unlikely(res)) return res; /* signal --> -ERESTARTSYS */ } + if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { + cpu_relax(); + goto try_again; + } return sg_receive_v3(sfp, srp, SZ_SG_IO_HDR, p); } @@ -1451,7 +1462,6 @@ get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) return get_user(*pack_id, &hp->pack_id); } } - /* no valid header was passed, so ignore the pack_id */ *pack_id = -1; return 0; @@ -1486,8 +1496,9 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) return ret; } +try_again: srp = sg_find_srp_by_id(sfp, req_pack_id, &busy); - if (!srp) { /* now wait on packet to arrive */ + while (!srp) { /* now wait on packet to arrive */ if (unlikely(!busy && SG_IS_DETACHING(sdp))) return -ENODEV; if (non_block) /* O_NONBLOCK or v3::flags & SGV4_FLAG_IMMED */ @@ -1501,6 +1512,10 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) return ret; /* otherwise srp should be valid */ } + if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { + cpu_relax(); + goto try_again; + } if (srp->s_hdr3.interface_id == '\0') ret = sg_read_v1v2(buf, (int)count, sfp, srp); else @@ -1720,6 +1735,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, */ static int sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) + __must_hold(sfp->f_mutex) { int new_sz, blen, res; unsigned long iflags; @@ -2908,26 +2924,27 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) atomic_dec(&sfp->submitted); atomic_dec(&sfp->waiting); } + + /* Expect blk_mq_free_request(rq) already called in sg_rq_end_io() */ + if (rq) { /* blk_get_request() may have failed */ + srp->rq = NULL; + blk_mq_free_request(rq); + } if (srp->bio) { bool us_xfer = !test_bit(SG_FRQ_NO_US_XFER, srp->frq_bm); + struct bio *bio = srp->bio; - if (us_xfer) { - ret = blk_rq_unmap_user(srp->bio); + srp->bio = NULL; + if (us_xfer && bio) { + ret = blk_rq_unmap_user(bio); if (ret) { /* -EINTR (-4) can be ignored */ SG_LOG(6, sfp, "%s: blk_rq_unmap_user() --> %d\n", __func__, ret); } } - srp->bio = NULL; - } - /* In worst case READ data returned to user space by this point */ - - /* Expect blk_mq_free_request(rq) already called in sg_rq_end_io() */ - if (rq) { /* blk_get_request() may have failed */ - srp->rq = NULL; - blk_mq_free_request(rq); } + /* In worst case, READ data returned to user space by this point */ } static int @@ -3367,6 +3384,7 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) return; sbp = srp->sense_bp; srp->sense_bp = NULL; + srp->frq_bm[0] = 0; sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true /* force */, __func__); /* maybe orphaned req, thus never read */ if (sbp) @@ -3499,6 +3517,7 @@ static void sg_remove_sfp_usercontext(struct work_struct *work) { __maybe_unused int o_count; + int subm; unsigned long idx, iflags; struct sg_device *sdp; struct sg_fd *sfp = container_of(work, struct sg_fd, ew_fd.work); @@ -3536,6 +3555,10 @@ sg_remove_sfp_usercontext(struct work_struct *work) SG_LOG(6, sfp, "%s: kfree: srp=%pK --\n", __func__, srp); kfree(srp); } + subm = atomic_read(&sfp->submitted); + if (subm != 0) + SG_LOG(1, sfp, "%s: expected submitted=0 got %d\n", + __func__, subm); xa_destroy(xafp); xa_lock_irqsave(xadp, iflags); e_sfp = __xa_erase(xadp, sfp->idx); @@ -3564,12 +3587,12 @@ sg_remove_sfp(struct kref *kref) static int sg_idr_max_id(int id, void *p, void *data) + __must_hold(sg_index_lock) { int *k = data; if (*k < id) *k = id; - return 0; } @@ -3799,6 +3822,7 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) /* Writes debug info for one sg_request in obp buffer */ static int sg_proc_debug_sreq(struct sg_request *srp, int to, char *obp, int len) + __must_hold(sfp->srp_arr.xa_lock) { bool is_v3v4, v4, is_dur; int n = 0; @@ -3901,6 +3925,7 @@ sg_proc_debug_fd(struct sg_fd *fp, char *obp, int len, unsigned long idx) /* Writes debug info for one sg device (including its sg fds) in obp buffer */ static int sg_proc_debug_sdev(struct sg_device *sdp, char *obp, int len, int *fd_counterp) + __must_hold(sg_index_lock) { int n = 0; int my_count = 0; From patchwork Mon Oct 24 03:20:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016556 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93ACDC3A59D for ; Mon, 24 Oct 2022 03:25:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230327AbiJXDZd (ORCPT ); Sun, 23 Oct 2022 23:25:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55336 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230324AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 810F374B82 for ; Sun, 23 Oct 2022 20:21:51 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 167C42041CB; Mon, 24 Oct 2022 05:21:51 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zn5SEYlK2NOZ; Mon, 24 Oct 2022 05:21:50 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id B74782041AF; Mon, 24 Oct 2022 05:21:49 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 33/44] sg: first debugfs support Date: Sun, 23 Oct 2022 23:20:47 -0400 Message-Id: <20221024032058.14077-34-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Duplicate the semantics of 'cat /proc/scsi/sg/debug' on 'cat /sys/kernel/debug/scsi_generic/snapshot'. Make code that generates the snapshot conditional on either CONFIG_SCSI_PROC_FS or CONFIG_DEBUG_FS being defined. Also add snapshot_devs which can be written with a list of comma separated integers corresponding to sg (minor) device numbers. That file can also be read showing that list. Minus one (or any negative number) means accept all when in the first position (the default) or means the end of the list in a later position. When a subsequent cat /sys/kernel/debug/scsi_generic/snapshot is performed, only sg device numbers matching an element in that list are output. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 404 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 332 insertions(+), 72 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 5079a449111e..5ccd410e4a53 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -44,6 +44,7 @@ static char *sg_version_date = "20190606"; #include /* for sg_check_file_access() */ #include #include +#include #include #include @@ -71,6 +72,10 @@ static char *sg_version_date = "20190606"; #endif #endif +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) || IS_ENABLED(CONFIG_DEBUG_FS) +#define CONFIG_SG_PROC_OR_DEBUG_FS 1 +#endif + /* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater * than 16 bytes are "variable length" whose length is a multiple of 4, so: @@ -271,6 +276,8 @@ struct sg_comm_wr_t { /* arguments to sg_common_write() */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ static int sg_proc_init(void); +static void sg_dfs_init(void); +static void sg_dfs_exit(void); static int sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir); static void sg_finish_scsi_blk_rq(struct sg_request *srp); @@ -2629,22 +2636,6 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) kref_put(&sdp->d_ref, sg_device_destroy); } -module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); -module_param_named(def_reserved_size, def_reserved_size, int, - S_IRUGO | S_IWUSR); -module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR); - -MODULE_AUTHOR("Douglas Gilbert"); -MODULE_DESCRIPTION("SCSI generic (sg) driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(SG_VERSION_STR); -MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR); - -MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " - "size (default: max(SG_SCATTER_SZ, PAGE_SIZE))"); -MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); -MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); - #ifdef CONFIG_SYSCTL #include @@ -2725,6 +2716,7 @@ init_sg(void) rc = scsi_register_interface(&sg_interface); if (rc == 0) { sg_proc_init(); + sg_dfs_init(); return 0; } class_destroy(sg_sysfs_class); @@ -2742,6 +2734,7 @@ init_sg(void) static void __exit exit_sg(void) { + sg_dfs_exit(); unregister_sg_sysctls(); if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) remove_proc_subtree("scsi/sg", NULL); @@ -3148,7 +3141,7 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) } if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) { if (search_for_1) { - const char *cptp = "pack_id="; + __maybe_unused const char *cptp = "pack_id="; if (is_bad_st) SG_LOG(1, sfp, "%s: %s%d wrong state: %s\n", @@ -3585,17 +3578,6 @@ sg_remove_sfp(struct kref *kref) schedule_work(&sfp->ew_fd.work); } -static int -sg_idr_max_id(int id, void *p, void *data) - __must_hold(sg_index_lock) -{ - int *k = data; - - if (*k < id) - *k = id; - return 0; -} - /* must be called with sg_index_lock held */ static struct sg_device * sg_lookup_dev(int dev) @@ -3625,7 +3607,7 @@ sg_get_dev(int dev) return sdp; } -#if IS_ENABLED(CONFIG_SCSI_PROC_FS) +#if IS_ENABLED(CONFIG_SG_PROC_OR_DEBUG_FS) static const char * sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) { @@ -3652,7 +3634,35 @@ sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) } #endif -#if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* long, almost to end of file */ +#if IS_ENABLED(CONFIG_SG_PROC_OR_DEBUG_FS) + +#define SG_SNAPSHOT_DEV_MAX 4 + +/* + * For snapshot_devs array, -1 or two adjacent the same is terminator. + * -1 in first element of first two elements the same implies all. + */ +static struct sg_dfs_context_t { + struct dentry *dfs_rootdir; + int snapshot_devs[SG_SNAPSHOT_DEV_MAX]; +} sg_dfs_cxt; + +struct sg_proc_deviter { + loff_t index; + size_t max; + int fd_index; +}; + +static int +sg_idr_max_id(int id, void *p, void *data) + __must_hold(sg_index_lock) +{ + int *k = data; + + if (*k < id) + *k = id; + return 0; +} static int sg_last_dev(void) @@ -3666,6 +3676,41 @@ sg_last_dev(void) return k + 1; /* origin 1 */ } +static void * +dev_seq_start(struct seq_file *s, loff_t *pos) +{ + struct sg_proc_deviter *it = kzalloc(sizeof(*it), GFP_KERNEL); + + s->private = it; + if (!it) + return NULL; + + it->index = *pos; + it->max = sg_last_dev(); + if (it->index >= it->max) + return NULL; + return it; +} + +static void * +dev_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct sg_proc_deviter *it = s->private; + + *pos = ++it->index; + return (it->index < it->max) ? it : NULL; +} + +static void +dev_seq_stop(struct seq_file *s, void *v) +{ + kfree(s->private); +} + +#endif /* SG_PROC_OR_DEBUG_FS */ + +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) /* around 100 lines */ + static int sg_proc_seq_show_int(struct seq_file *s, void *v) { @@ -3679,7 +3724,7 @@ sg_proc_single_open_adio(struct inode *inode, struct file *filp) return single_open(filp, sg_proc_seq_show_int, &sg_allow_dio); } -static ssize_t +static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -3701,7 +3746,7 @@ sg_proc_single_open_dressz(struct inode *inode, struct file *filp) return single_open(filp, sg_proc_seq_show_int, &sg_big_buff); } -static ssize_t +static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -3736,43 +3781,6 @@ sg_proc_seq_show_devhdr(struct seq_file *s, void *v) return 0; } -struct sg_proc_deviter { - loff_t index; - size_t max; - int fd_index; -}; - -static void * -dev_seq_start(struct seq_file *s, loff_t *pos) -{ - struct sg_proc_deviter *it = kzalloc(sizeof(*it), GFP_KERNEL); - - s->private = it; - if (! it) - return NULL; - - it->index = *pos; - it->max = sg_last_dev(); - if (it->index >= it->max) - return NULL; - return it; -} - -static void * -dev_seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - struct sg_proc_deviter *it = s->private; - - *pos = ++it->index; - return (it->index < it->max) ? it : NULL; -} - -static void -dev_seq_stop(struct seq_file *s, void *v) -{ - kfree(s->private); -} - static int sg_proc_seq_show_dev(struct seq_file *s, void *v) { @@ -3819,6 +3827,10 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) return 0; } +#endif /* CONFIG_SCSI_PROC_FS (~100 lines back) */ + +#if IS_ENABLED(CONFIG_SG_PROC_OR_DEBUG_FS) + /* Writes debug info for one sg_request in obp buffer */ static int sg_proc_debug_sreq(struct sg_request *srp, int to, char *obp, int len) @@ -3961,18 +3973,20 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) bool found = false; bool trunc = false; const int bp_len = SG_PROC_DEBUG_SZ; + int j, sd_n; int n = 0; int k = 0; unsigned long iflags; struct sg_proc_deviter *it = (struct sg_proc_deviter *)v; struct sg_device *sdp; int *fdi_p; + const int *dev_arr = sg_dfs_cxt.snapshot_devs; char *bp; char *disk_name; char b1[128]; b1[0] = '\0'; - if (it && (0 == it->index)) + if (it && it->index == 0) seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", (int)it->max, def_reserved_size); fdi_p = it ? &it->fd_index : &k; @@ -3984,8 +3998,31 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) } read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - if (NULL == sdp) + if (!sdp) goto skip; + sd_n = dev_arr[0]; + if (sd_n != -1 && sd_n != sdp->index && sd_n != dev_arr[1]) { + for (j = 1; j < SG_SNAPSHOT_DEV_MAX; ) { + sd_n = dev_arr[j]; + if (sd_n < 0) + goto skip; + ++j; + if (j >= SG_SNAPSHOT_DEV_MAX) { + if (sd_n == sdp->index) { + found = true; + break; + } + } else if (sd_n == dev_arr[j]) { + goto skip; + } else if (sd_n == sdp->index) { + found = true; + break; + } + } + if (!found) + goto skip; + found = false; + } if (!xa_empty(&sdp->sfp_arr)) { found = true; disk_name = (sdp->name[0] ? sdp->name : "?_?"); @@ -4025,6 +4062,10 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) return 0; } +#endif /* SG_PROC_OR_DEBUG_FS */ + +#if IS_ENABLED(CONFIG_SCSI_PROC_FS) + static const struct proc_ops adio_proc_ops = { .proc_open = sg_proc_single_open_adio, .proc_read = seq_read, @@ -4085,7 +4126,226 @@ sg_proc_init(void) /* remove_proc_subtree("scsi/sg", NULL) in exit_sg() does cleanup */ -#endif /* CONFIG_SCSI_PROC_FS (~400 lines back) */ +#else + +static int +sg_proc_init(void) +{ + return 0; +} + +#endif /* CONFIG_SCSI_PROC_FS */ + +#if IS_ENABLED(CONFIG_DEBUG_FS) + +struct sg_dfs_attr { + const char *name; + umode_t mode; + int (*show)(void *d, struct seq_file *m); + ssize_t (*write)(void *d, const char __user *b, size_t s, loff_t *o); + /* Set either .show or .seq_ops. */ + const struct seq_operations *seq_ops; +}; + +static int +sg_dfs_snapshot_devs_show(void *data, struct seq_file *m) +{ + bool last; + int k, d; + struct sg_dfs_context_t *ctxp = data; + + for (k = 0; k < SG_SNAPSHOT_DEV_MAX; ++k) { + d = ctxp->snapshot_devs[k]; + last = (k + 1 == SG_SNAPSHOT_DEV_MAX); + if (d < 0) { + if (k == 0) + seq_puts(m, "-1"); + break; + } + if (!last && d == ctxp->snapshot_devs[k + 1]) { + if (k == 0) + seq_puts(m, "-1"); + break; + } + if (k != 0) + seq_puts(m, ","); + seq_printf(m, "%d", d); + } + seq_puts(m, "\n"); + return 0; +} + +static ssize_t +sg_dfs_snapshot_devs_write(void *data, const char __user *buf, size_t count, + loff_t *ppos) +{ + bool trailing_comma; + int k, n; + struct sg_dfs_context_t *cxtp = data; + char lbuf[64] = { }, *cp, *c2p; + + if (count >= sizeof(lbuf)) { + pr_err("%s: operation too long\n", __func__); + return -EINVAL; + } + if (copy_from_user(lbuf, buf, count)) + return -EFAULT; + for (k = 0, n = 0, cp = lbuf; k < SG_SNAPSHOT_DEV_MAX; + ++k, cp = c2p + 1) { + c2p = strchr(cp, ','); + if (c2p) + *c2p = '\0'; + trailing_comma = !!c2p; + /* sscanf is easier to use that this ... */ + if (kstrtoint(cp, 10, cxtp->snapshot_devs + k)) + break; + ++n; + if (!trailing_comma) + break; + } + if (n == 0) { + return -EINVAL; + } else if (k >= SG_SNAPSHOT_DEV_MAX && trailing_comma) { + pr_err("%s: only %d elements in snapshot array\n", __func__, + SG_SNAPSHOT_DEV_MAX); + return -EINVAL; + } + if (n < SG_SNAPSHOT_DEV_MAX) + cxtp->snapshot_devs[n] = -1; + return count; +} + +static int +sg_dfs_show(struct seq_file *m, void *v) +{ + const struct sg_dfs_attr *attr = m->private; + void *data = d_inode(m->file->f_path.dentry->d_parent)->i_private; + + return attr->show(data, m); +} +static ssize_t +sg_dfs_write(struct file *file, const char __user *buf, size_t count, + loff_t *ppos) +{ + struct seq_file *m = file->private_data; + const struct sg_dfs_attr *attr = m->private; + void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + + /* + * Attributes that only implement .seq_ops are read-only and 'attr' is + * the same with 'data' in this case. + */ + if (attr == data || !attr->write) + return -EPERM; + return attr->write(data, buf, count, ppos); +} + +static int +sg_dfs_open(struct inode *inode, struct file *file) +{ + const struct sg_dfs_attr *attr = inode->i_private; + void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + struct seq_file *m; + int ret; + + if (attr->seq_ops) { + ret = seq_open(file, attr->seq_ops); + if (!ret) { + m = file->private_data; + m->private = data; + } + return ret; + } + if (WARN_ON_ONCE(!attr->show)) + return -EPERM; + return single_open(file, sg_dfs_show, inode->i_private); +} + +static int +sg_dfs_release(struct inode *inode, struct file *file) +{ + const struct sg_dfs_attr *attr = inode->i_private; + + if (attr->show) + return single_release(inode, file); + return seq_release(inode, file); +} + +static const struct file_operations sg_dfs_fops = { + .owner = THIS_MODULE, + .open = sg_dfs_open, + .read = seq_read, + .write = sg_dfs_write, + .llseek = seq_lseek, + .release = sg_dfs_release, +}; + +static void sg_dfs_mk_files(struct dentry *parent, void *data, + const struct sg_dfs_attr *attr) +{ + if (IS_ERR_OR_NULL(parent)) + return; + + d_inode(parent)->i_private = data; + for (; attr->name; ++attr) + debugfs_create_file(attr->name, attr->mode, parent, + (void *)attr, &sg_dfs_fops); +} + +static const struct seq_operations sg_snapshot_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = sg_proc_seq_show_debug, +}; + +static const struct sg_dfs_attr sg_dfs_attrs[] = { + {"snapshot", 0400, .seq_ops = &sg_snapshot_seq_ops}, + {"snapshot_devs", 0600, sg_dfs_snapshot_devs_show, + sg_dfs_snapshot_devs_write}, + { }, +}; + +static void +sg_dfs_init(void) +{ + /* create and populate /sys/kernel/debug/scsi_generic directory */ + if (!sg_dfs_cxt.dfs_rootdir) { + sg_dfs_cxt.dfs_rootdir = debugfs_create_dir("scsi_generic", + NULL); + sg_dfs_mk_files(sg_dfs_cxt.dfs_rootdir, &sg_dfs_cxt, + sg_dfs_attrs); + } + sg_dfs_cxt.snapshot_devs[0] = -1; /* show all sg devices */ +} + +static void +sg_dfs_exit(void) +{ + debugfs_remove_recursive(sg_dfs_cxt.dfs_rootdir); + sg_dfs_cxt.dfs_rootdir = NULL; +} + +#else /* not defined: CONFIG_DEBUG_FS */ + +static void sg_dfs_init(void) {} +static void sg_dfs_exit(void) {} + +#endif /* CONFIG_DEBUG_FS */ + +module_param_named(scatter_elem_sz, scatter_elem_sz, int, 0644); +module_param_named(def_reserved_size, def_reserved_size, int, 0644); +module_param_named(allow_dio, sg_allow_dio, int, 0644); + +MODULE_AUTHOR("Douglas Gilbert"); +MODULE_DESCRIPTION("SCSI generic (sg) driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SG_VERSION_STR); +MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR); + +MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element size (default: max(SG_SCATTER_SZ, PAGE_SIZE))"); +MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); +MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); module_init(init_sg); module_exit(exit_sg); From patchwork Mon Oct 24 03:20:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016562 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55FF9FA373D for ; Mon, 24 Oct 2022 03:25:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229904AbiJXDZw (ORCPT ); Sun, 23 Oct 2022 23:25:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60544 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230331AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 48DD332EC4 for ; Sun, 23 Oct 2022 20:21:52 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 758F72041CE; Mon, 24 Oct 2022 05:21:52 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id J49BR7svqaGD; Mon, 24 Oct 2022 05:21:52 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 4062C2041AF; Mon, 24 Oct 2022 05:21:51 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 34/44] sg: rework mmap support Date: Sun, 23 Oct 2022 23:20:48 -0400 Message-Id: <20221024032058.14077-35-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Linux has an issue with mmap-ed multiple pages issued by alloc_pages() [with order > 0]. So when mmap(2) is called if the reserve request's scatter gather (sgat) list is either: - not big enough, or - made up of elements of order > 0 (i.e. size > PAGE_SIZE) then throw away reserve requests sgat list and rebuild it meeting those requirements. Clean up related code and stop doing mmap+indirect_io. This new mmap implementation is marginally more flexible (but still compatible with) the production driver. Previously if the user wanted a larger mmap(2) 'length' than the reserve request's size, then they needed to use ioctl(SG_SET_RESERVED_SIZE) to set the new size first. Now mmap(2) called on a sg device node will adjust to the size given by 'length' [mmap's second parameter]. Tweak some SG_LOG() levels to control the amount of debug output. Add some WRITE_ONCE() macros when bitop integers are being initialised. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 181 ++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 72 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 5ccd410e4a53..cb110ff18047 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -118,6 +118,7 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_FRQ_NO_US_XFER 3 /* no user space transfer of data */ #define SG_FRQ_DEACT_ORPHAN 6 /* not keeping orphan so de-activate */ #define SG_FRQ_RECEIVING 7 /* guard against multiple receivers */ +#define SG_FRQ_FOR_MMAP 8 /* request needs PAGE_SIZE elements */ /* Bit positions (flags) for sg_fd::ffd_bm bitmask follow */ #define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ @@ -707,7 +708,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) input_size, (unsigned int)opcode, current->comm); } cwr.h3p = h3p; - cwr.frq_bm[0] = 0; + WRITE_ONCE(cwr.frq_bm[0], 0); cwr.timeout = sfp->timeout; cwr.cmd_len = cmd_size; cwr.filp = filp; @@ -769,7 +770,7 @@ sg_v3_submit(struct file *filp, struct sg_fd *sfp, struct sg_io_hdr *hp, /* when v3 seen, allow cmd_q on this fd (def: no cmd_q) */ set_bit(SG_FFD_CMD_Q, sfp->ffd_bm); ul_timeout = msecs_to_jiffies(hp->timeout); - cwr.frq_bm[0] = 0; + WRITE_ONCE(cwr.frq_bm[0], 0); __assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); cwr.h3p = hp; cwr.timeout = min_t(unsigned long, ul_timeout, INT_MAX); @@ -810,7 +811,7 @@ sg_submit_v4(struct file *filp, struct sg_fd *sfp, void __user *p, ul_timeout = msecs_to_jiffies(h4p->timeout); cwr.filp = filp; cwr.sfp = sfp; - cwr.frq_bm[0] = 0; + WRITE_ONCE(cwr.frq_bm[0], 0); __assign_bit(SG_FRQ_SYNC_INVOC, cwr.frq_bm, (int)sync); __set_bit(SG_FRQ_IS_V4I, cwr.frq_bm); cwr.h4p = h4p; @@ -1753,6 +1754,8 @@ sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) srp = sfp->rsv_srp; if (!srp) return -EPROTO; + if (test_bit(SG_FRQ_FOR_MMAP, srp->frq_bm)) + return -EBUSY; new_sz = min_t(int, want_rsv_sz, sfp->parentdp->max_sgat_sz); new_sz = max_t(int, new_sz, sfp->sgat_elem_sz); blen = srp->sgat_h.buflen; @@ -2144,13 +2147,39 @@ sg_fasync(int fd, struct file *filp, int mode) return fasync_helper(fd, filp, mode, &sfp->async_qp); } +static void +sg_vma_open(struct vm_area_struct *vma) +{ + struct sg_fd *sfp = vma->vm_private_data; + + if (unlikely(!sfp)) { + pr_warn("%s: sfp null\n", __func__); + return; + } + kref_get(&sfp->f_ref); +} + +static void +sg_vma_close(struct vm_area_struct *vma) +{ + struct sg_fd *sfp = vma->vm_private_data; + + if (unlikely(!sfp)) { + pr_warn("%s: sfp null\n", __func__); + return; + } + kref_put(&sfp->f_ref, sg_remove_sfp); /* get in: sg_vma_open() */ +} + /* Note: the error return: VM_FAULT_SIGBUS causes a "bus error" */ static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { - int k, length; - unsigned long offset, len, sa, iflags; + int k, n, length; + int res = VM_FAULT_SIGBUS; + unsigned long offset; struct vm_area_struct *vma = vmf->vma; + struct page *page; struct sg_scatter_hold *rsv_schp; struct sg_request *srp; struct sg_device *sdp; @@ -2176,7 +2205,7 @@ sg_vma_fault(struct vm_fault *vmf) SG_LOG(1, sfp, "%s: srp%s\n", __func__, nbp); goto out_err; } - xa_lock_irqsave(&sfp->srp_arr, iflags); + mutex_lock(&sfp->f_mutex); rsv_schp = &srp->sgat_h; offset = vmf->pgoff << PAGE_SHIFT; if (offset >= (unsigned int)rsv_schp->buflen) { @@ -2184,44 +2213,37 @@ sg_vma_fault(struct vm_fault *vmf) offset); goto out_err_unlock; } - sa = vma->vm_start; - SG_LOG(3, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, sa, offset); + SG_LOG(5, sfp, "%s: vm_start=0x%lx, off=%lu\n", __func__, + vma->vm_start, offset); length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) { - len = vma->vm_end - sa; - len = min_t(int, len, (int)length); - if (offset < len) { - struct page *page; - struct page *pp; - - pp = rsv_schp->pages[k]; - xa_unlock_irqrestore(&sfp->srp_arr, iflags); - page = nth_page(pp, offset >> PAGE_SHIFT); - get_page(page); /* increment page count */ - vmf->page = page; - return 0; /* success */ - } - sa += len; - offset -= len; - } + k = (int)offset / length; + n = ((int)offset % length) >> PAGE_SHIFT; + page = nth_page(rsv_schp->pages[k], n); + get_page(page); + vmf->page = page; + res = 0; out_err_unlock: - xa_unlock_irqrestore(&sfp->srp_arr, iflags); + mutex_unlock(&sfp->f_mutex); out_err: - return VM_FAULT_SIGBUS; + return res; } static const struct vm_operations_struct sg_mmap_vm_ops = { .fault = sg_vma_fault, + .open = sg_vma_open, + .close = sg_vma_close, }; -/* Entry point for mmap(2) system call */ +/* + * Entry point for mmap(2) system call. For mmap(2) to work, request's + * scatter gather list needs to be order 0 which it is unlikely to be + * by default. mmap(2) cannot be called more than once per fd. + */ static int sg_mmap(struct file *filp, struct vm_area_struct *vma) { - int k, length; - int ret = 0; - unsigned long req_sz, len, sa, iflags; - struct sg_scatter_hold *rsv_schp; + int res = 0; + unsigned long req_sz; struct sg_fd *sfp; struct sg_request *srp; @@ -2232,40 +2254,48 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) pr_warn("sg: %s: sfp is NULL\n", __func__); return -ENXIO; } + mutex_lock(&sfp->f_mutex); req_sz = vma->vm_end - vma->vm_start; SG_LOG(3, sfp, "%s: vm_start=%pK, len=%d\n", __func__, (void *)vma->vm_start, (int)req_sz); - if (vma->vm_pgoff) - return -EINVAL; /* only an offset of 0 accepted */ + if (vma->vm_pgoff) { + res = -EINVAL; /* only an offset of 0 accepted */ + goto fini; + } /* Check reserve request is inactive and has large enough buffer */ - mutex_lock(&sfp->f_mutex); srp = sfp->rsv_srp; - xa_lock_irqsave(&sfp->srp_arr, iflags); if (SG_RS_ACTIVE(srp)) { - ret = -EBUSY; - goto out; + res = -EBUSY; + goto fini; } - rsv_schp = &srp->sgat_h; - if (req_sz > (unsigned long)rsv_schp->buflen) { - ret = -ENOMEM; - goto out; + if (req_sz > SG_WRITE_COUNT_LIMIT) { /* sanity check */ + res = -ENOMEM; + goto fini; } - sa = vma->vm_start; - length = 1 << (PAGE_SHIFT + rsv_schp->page_order); - for (k = 0; k < rsv_schp->num_sgat && sa < vma->vm_end; ++k) { - len = vma->vm_end - sa; - len = min_t(unsigned long, len, (unsigned long)length); - sa += len; + if (test_and_set_bit(SG_FFD_MMAP_CALLED, sfp->ffd_bm)) { + SG_LOG(1, sfp, "%s: multiple invocations on this fd\n", + __func__); + res = -EADDRINUSE; + goto fini; + } + if (srp->sgat_h.page_order > 0 || + req_sz > (unsigned long)srp->sgat_h.buflen) { + sg_remove_sgat(srp); + set_bit(SG_FRQ_FOR_MMAP, srp->frq_bm); + res = sg_mk_sgat(&srp->sgat_h, sfp, req_sz); + if (res) { + SG_LOG(1, sfp, "%s: sg_mk_sgat failed, wanted=%lu\n", + __func__, req_sz); + goto fini; + } } - - set_bit(SG_FFD_MMAP_CALLED, sfp->ffd_bm); vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; -out: - xa_unlock_irqrestore(&sfp->srp_arr, iflags); + sg_vma_open(vma); +fini: mutex_unlock(&sfp->f_mutex); - return ret; + return res; } static void @@ -2844,7 +2874,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) goto fini; scmd->cmd_len = cwrp->cmd_len; srp->cmd_opcode = scmd->cmnd[0]; - us_xfer = !(srp->rq_flags & SG_FLAG_NO_DXFER); + us_xfer = !(srp->rq_flags & (SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO)); assign_bit(SG_FRQ_NO_US_XFER, srp->frq_bm, !us_xfer); reserved = (sfp->rsv_srp == srp); rq->end_io_data = srp; @@ -2945,10 +2975,11 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) { int j, k, rem_sz, align_sz, order, o_order; int mx_sgat_elems = sfp->parentdp->max_sgat_elems; - unsigned int elem_sz; + unsigned int elem_sz = sfp->sgat_elem_sz; const size_t ptr_sz = sizeof(struct page *); gfp_t mask_ap = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; gfp_t mask_kz = GFP_ATOMIC | __GFP_NOWARN; + struct page **pgp; if (unlikely(minlen <= 0)) { if (minlen < 0) @@ -2963,30 +2994,35 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) mx_sgat_elems * ptr_sz, schp->pages); if (unlikely(!schp->pages)) return -ENOMEM; - elem_sz = sfp->sgat_elem_sz; /* power of 2 and >= PAGE_SIZE */ + + /* elem_sz must be power of 2 and >= PAGE_SIZE */ o_order = get_order(elem_sz); order = o_order; again: - for (k = 0, rem_sz = align_sz; rem_sz > 0 && k < mx_sgat_elems; - ++k, rem_sz -= elem_sz) { - schp->pages[k] = alloc_pages(mask_ap, order); - if (!schp->pages[k]) + if (elem_sz * mx_sgat_elems < align_sz) { /* misfit ? */ + SG_LOG(1, sfp, "%s: align_sz=%d too big\n", __func__, + align_sz); + goto b4_alloc_pages; + } + rem_sz = align_sz; + for (pgp = schp->pages; rem_sz > 0; ++pgp, rem_sz -= elem_sz) { + *pgp = alloc_pages(mask_ap, order); + if (unlikely(!*pgp)) goto err_out; - SG_LOG(5, sfp, "%s: k=%d, order=%d [0x%pK ++]\n", __func__, k, - order, schp->pages[k]); + SG_LOG(6, sfp, "%s: elem_sz=%d [0x%pK ++]\n", __func__, + elem_sz, *pgp); } + k = pgp - schp->pages; + SG_LOG(((order != o_order || rem_sz > 0) ? 2 : 5), sfp, + "%s: num_sgat=%d, order=%d,%d rem_sz=%d\n", __func__, k, + o_order, order, rem_sz); schp->page_order = order; schp->num_sgat = k; - SG_LOG(((order != o_order || rem_sz > 0) ? 2 : 5), sfp, - "%s: num_sgat=%d, order=%d,%d\n", __func__, k, o_order, order); - if (unlikely(rem_sz > 0)) { /* hit mx_sgat_elems */ - order = 0; /* force exit */ - goto err_out; - } schp->buflen = align_sz; return 0; err_out: + k = pgp - schp->pages; for (j = 0; j < k; ++j) __free_pages(schp->pages[j], order); @@ -2994,6 +3030,7 @@ sg_mk_sgat(struct sg_scatter_hold *schp, struct sg_fd *sfp, int minlen) elem_sz >>= 1; goto again; } +b4_alloc_pages: kfree(schp->pages); schp->pages = NULL; return -ENOMEM; @@ -3009,7 +3046,7 @@ sg_remove_sgat_helper(struct sg_fd *sfp, struct sg_scatter_hold *schp) return; for (k = 0; k < schp->num_sgat; ++k) { p = schp->pages[k]; - SG_LOG(5, sfp, "%s: pg[%d]=0x%pK --\n", __func__, k, p); + SG_LOG(6, sfp, "%s: pg[%d]=0x%pK --\n", __func__, k, p); if (unlikely(!p)) continue; __free_pages(p, schp->page_order); @@ -3154,7 +3191,7 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) } return NULL; good: - SG_LOG(6, sfp, "%s: %s%d found [srp=0x%pK]\n", __func__, "pack_id=", + SG_LOG(5, sfp, "%s: %s%d found [srp=0x%pK]\n", __func__, "pack_id=", pack_id, srp); return srp; } @@ -3350,7 +3387,7 @@ sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) r_srp->parentfp = fp; SG_LOG(4, fp, "%s: mk_new_srp=0x%pK ++\n", __func__, r_srp); } - r_srp->frq_bm[0] = cwrp->frq_bm[0]; /* assumes <= 32 req flags */ + WRITE_ONCE(r_srp->frq_bm[0], cwrp->frq_bm[0]); /* assumes <= 32 req flags */ r_srp->sgat_h.dlen = dxfr_len;/* must be <= r_srp->sgat_h.buflen */ r_srp->cmd_opcode = 0xff; /* set invalid opcode (VS), 0x0 is TUR */ fini: @@ -3377,7 +3414,7 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) return; sbp = srp->sense_bp; srp->sense_bp = NULL; - srp->frq_bm[0] = 0; + WRITE_ONCE(srp->frq_bm[0], 0); sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true /* force */, __func__); /* maybe orphaned req, thus never read */ if (sbp) From patchwork Mon Oct 24 03:20:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016557 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 24C79C433FE for ; Mon, 24 Oct 2022 03:25:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230365AbiJXDZf (ORCPT ); Sun, 23 Oct 2022 23:25:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58696 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230349AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id EDE052EF54 for ; Sun, 23 Oct 2022 20:21:54 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id D91B22041BB; Mon, 24 Oct 2022 05:21:53 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hInKlDPGwKC1; Mon, 24 Oct 2022 05:21:53 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id A18B22041AF; Mon, 24 Oct 2022 05:21:52 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 35/44] sg: defang allow_dio Date: Sun, 23 Oct 2022 23:20:49 -0400 Message-Id: <20221024032058.14077-36-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Before direct IO was permitted by this driver, the user either had to give 'allow_dio=1' as a module parameter or write to procfs with 'echo 1 > /proc/scsi/sg/allow_dio'. The user also needs to set the SG_FLAG_DIRECT_IO flag in the flags field of either the sg v3 or v3 interface. The reason this "belts and braces" approach was taken is lost in the mists of time (done over 20 years ago). So this patch keeps the allow_dio attribute for backward compatibility but ignores it, relying on the SG_FLAG_DIRECT_IO flag alone. This brings the use of the SG_FLAG_DIRECT_IO flag into line with the SG_FLAG_MMAP_IO flag; the two mechanisms are no more, or less, safe than one another in recent Linux kernels. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index cb110ff18047..ef83f2097c73 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -144,7 +144,7 @@ static int sg_big_buff = SG_DEF_RESERVED_SIZE; * not enough memory) will be reserved for use by this file descriptor. */ static int def_reserved_size = -1; /* picks up init parameter */ -static int sg_allow_dio = SG_ALLOW_DIO_DEF; +static int sg_allow_dio = SG_ALLOW_DIO_DEF; /* ignored by code */ static int scatter_elem_sz = SG_SCATTER_SZ; @@ -2778,17 +2778,6 @@ exit_sg(void) idr_destroy(&sg_index_idr); } -static inline bool -sg_chk_dio_allowed(struct sg_device *sdp, struct sg_request *srp, - int iov_count, int dir) -{ - if (sg_allow_dio && (srp->rq_flags & SG_FLAG_DIRECT_IO)) { - if (dir != SG_DXFER_UNKNOWN && !iov_count) - return true; - } - return false; -} - static inline void sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, struct rq_map_data *mdp) @@ -2807,6 +2796,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) int res = 0; int dxfer_len = 0; int r0w = READ; + u32 rq_flags = srp->rq_flags; unsigned int iov_count = 0; void __user *up; struct request *rq; @@ -2874,7 +2864,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) goto fini; scmd->cmd_len = cwrp->cmd_len; srp->cmd_opcode = scmd->cmnd[0]; - us_xfer = !(srp->rq_flags & (SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO)); + us_xfer = !(rq_flags & (SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO)); assign_bit(SG_FRQ_NO_US_XFER, srp->frq_bm, !us_xfer); reserved = (sfp->rsv_srp == srp); rq->end_io_data = srp; @@ -2885,7 +2875,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) SG_LOG(4, sfp, "%s: no data xfer [0x%pK]\n", __func__, srp); set_bit(SG_FRQ_NO_US_XFER, srp->frq_bm); goto fini; /* path of reqs with no din nor dout */ - } else if (sg_chk_dio_allowed(sdp, srp, iov_count, dxfer_dir) && + } else if ((rq_flags & SG_FLAG_DIRECT_IO) && iov_count == 0 && blk_rq_aligned(q, (unsigned long)up, dxfer_len)) { srp->rq_info |= SG_INFO_DIRECT_IO; md = NULL; @@ -2896,7 +2886,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) } if (likely(md)) { /* normal, "indirect" IO */ - if (unlikely(srp->rq_flags & SG_FLAG_MMAP_IO)) { + if (unlikely(rq_flags & SG_FLAG_MMAP_IO)) { /* mmap IO must use and fit in reserve request */ if (!reserved || dxfer_len > req_schp->buflen) res = reserved ? -ENOMEM : -EBUSY; @@ -3761,6 +3751,7 @@ sg_proc_single_open_adio(struct inode *inode, struct file *filp) return single_open(filp, sg_proc_seq_show_int, &sg_allow_dio); } +/* Kept for backward compatibility. sg_allow_dio is now ignored. */ static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off) @@ -4383,6 +4374,6 @@ MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR); MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element size (default: max(SG_SCATTER_SZ, PAGE_SIZE))"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); -MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); +MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow)); now ignored"); module_init(init_sg); module_exit(exit_sg); From patchwork Mon Oct 24 03:20:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016558 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82D12C433FE for ; Mon, 24 Oct 2022 03:25:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230329AbiJXDZi (ORCPT ); Sun, 23 Oct 2022 23:25:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55622 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230351AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C848032EF9 for ; Sun, 23 Oct 2022 20:21:55 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 3EE262041BD; Mon, 24 Oct 2022 05:21:55 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NWGSnYuvtYNt; Mon, 24 Oct 2022 05:21:55 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 13EBC2041AF; Mon, 24 Oct 2022 05:21:53 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 36/44] sg: warn v3 write system call users Date: Sun, 23 Oct 2022 23:20:50 -0400 Message-Id: <20221024032058.14077-37-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Should generate one log message per kernel run when the write() system call is used with the sg interface version 3. Due to security concerns suggest that they use ioctl(SG_SUBMIT_v3) instead. Sg interface version 1 or 2 based code may also be calling write() in this context. There is no easy solution for them (short of upgrading their interface to version 3 or 4), so don't produce a warning suggesting the conversion will be simple. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ef83f2097c73..c9eb9e842f9e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -644,6 +644,9 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) __func__); return -EPERM; } + pr_warn_once("Please use %s instead of write(),\n%s\n", + "ioctl(SG_SUBMIT_V3)", + " See: https://sg.danny.cz/sg/sg_v40.html"); res = sg_v3_submit(filp, sfp, h3p, false, NULL); return res < 0 ? res : (int)count; } From patchwork Mon Oct 24 03:20:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016560 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E72FBC433FE for ; Mon, 24 Oct 2022 03:25:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230270AbiJXDZp (ORCPT ); Sun, 23 Oct 2022 23:25:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230118AbiJXDXD (ORCPT ); Sun, 23 Oct 2022 23:23:03 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C87BB6E2EC for ; Sun, 23 Oct 2022 20:21:56 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 9A7D42041C0; Mon, 24 Oct 2022 05:21:56 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id p9Zv5hqK1Q9n; Mon, 24 Oct 2022 05:21:56 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 70BF42041AF; Mon, 24 Oct 2022 05:21:55 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 37/44] sg: add mmap_sz tracking Date: Sun, 23 Oct 2022 23:20:51 -0400 Message-Id: <20221024032058.14077-38-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Track mmap_sz from prior mmap(2) call, per sg file descriptor. Also reset this value whenever munmap(2) is called. Fail SG_FLAG_MMAP_IO uses if mmap(2) hasn't been called or the memory associated with it is not large enough for the current request. Remove SG_FFD_MMAP_CALLED bit as it can be deduced from sfp->mmap_sz where a value of 0 implies no mmap() call active. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c9eb9e842f9e..a37e1156e4c2 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -124,8 +124,7 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ #define SG_FFD_CMD_Q 1 /* clear: only 1 active req per fd */ #define SG_FFD_KEEP_ORPHAN 2 /* policy for this fd */ -#define SG_FFD_MMAP_CALLED 3 /* mmap(2) system call made on fd */ -#define SG_FFD_Q_AT_TAIL 5 /* set: queue reqs at tail of blk q */ +#define SG_FFD_Q_AT_TAIL 3 /* set: queue reqs at tail of blk q */ /* Bit positions (flags) for sg_device::fdev_bm bitmask follow */ #define SG_FDEV_EXCLUDE 0 /* have fd open with O_EXCL */ @@ -234,6 +233,7 @@ struct sg_fd { /* holds the state of a file descriptor */ atomic_t waiting; /* number of requests awaiting receive */ atomic_t req_cnt; /* number of requests */ int sgat_elem_sz; /* initialized to scatter_elem_sz */ + int mmap_sz; /* byte size of previous mmap() call */ unsigned long ffd_bm[1]; /* see SG_FFD_* defines above */ pid_t tid; /* thread id when opened */ u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ @@ -724,10 +724,14 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) static inline int sg_chk_mmap(struct sg_fd *sfp, int rq_flags, int len) { + if (unlikely(sfp->mmap_sz == 0)) + return -EBADFD; if (atomic_read(&sfp->submitted) > 0) return -EBUSY; /* already active requests on fd */ if (len > sfp->rsv_srp->sgat_h.buflen) return -ENOMEM; /* MMAP_IO size must fit in reserve */ + if (unlikely(len > sfp->mmap_sz)) + return -ENOMEM; /* MMAP_IO size can't exceed mmap() size */ if (rq_flags & SG_FLAG_DIRECT_IO) return -EINVAL; /* not both MMAP_IO and DIRECT_IO */ return 0; @@ -1751,13 +1755,12 @@ sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz) int new_sz, blen, res; unsigned long iflags; struct sg_scatter_hold n_schp, o_schp; - struct sg_request *srp; + struct sg_request *srp = sfp->rsv_srp; struct xarray *xafp = &sfp->srp_arr; - srp = sfp->rsv_srp; if (!srp) return -EPROTO; - if (test_bit(SG_FRQ_FOR_MMAP, srp->frq_bm)) + if (SG_RS_ACTIVE(srp) || sfp->mmap_sz > 0) return -EBUSY; new_sz = min_t(int, want_rsv_sz, sfp->parentdp->max_sgat_sz); new_sz = max_t(int, new_sz, sfp->sgat_elem_sz); @@ -2171,6 +2174,7 @@ sg_vma_close(struct vm_area_struct *vma) pr_warn("%s: sfp null\n", __func__); return; } + sfp->mmap_sz = 0; kref_put(&sfp->f_ref, sg_remove_sfp); /* get in: sg_vma_open() */ } @@ -2261,7 +2265,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) req_sz = vma->vm_end - vma->vm_start; SG_LOG(3, sfp, "%s: vm_start=%pK, len=%d\n", __func__, (void *)vma->vm_start, (int)req_sz); - if (vma->vm_pgoff) { + if (unlikely(vma->vm_pgoff || req_sz < SG_DEF_SECTOR_SZ)) { res = -EINVAL; /* only an offset of 0 accepted */ goto fini; } @@ -2275,7 +2279,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) res = -ENOMEM; goto fini; } - if (test_and_set_bit(SG_FFD_MMAP_CALLED, sfp->ffd_bm)) { + if (sfp->mmap_sz > 0) { SG_LOG(1, sfp, "%s: multiple invocations on this fd\n", __func__); res = -EADDRINUSE; @@ -2292,6 +2296,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) goto fini; } } + sfp->mmap_sz = req_sz; vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; @@ -3924,8 +3929,7 @@ sg_proc_debug_fd(struct sg_fd *fp, char *obp, int len, unsigned long idx) (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm), fp->ffd_bm[0]); - n += scnprintf(obp + n, len - n, " mmap_called=%d\n", - test_bit(SG_FFD_MMAP_CALLED, fp->ffd_bm)); + n += scnprintf(obp + n, len - n, " mmap_sz=%d\n", fp->mmap_sz); n += scnprintf(obp + n, len - n, " submitted=%d waiting=%d open thr_id=%d\n", atomic_read(&fp->submitted), From patchwork Mon Oct 24 03:20:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016563 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A49CFC3A59D for ; Mon, 24 Oct 2022 03:25:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230378AbiJXDZt (ORCPT ); Sun, 23 Oct 2022 23:25:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38420 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229501AbiJXDXJ (ORCPT ); Sun, 23 Oct 2022 23:23:09 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id C486C76578 for ; Sun, 23 Oct 2022 20:21:58 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 20B122041CB; Mon, 24 Oct 2022 05:21:58 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id TtXLUh1RxX87; Mon, 24 Oct 2022 05:21:57 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id E02BD2041AF; Mon, 24 Oct 2022 05:21:56 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 38/44] sg: track lowest inactive and await indexes Date: Sun, 23 Oct 2022 23:20:52 -0400 Message-Id: <20221024032058.14077-39-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Use two integers in the sg_fd structure to track recent and lowest xarray indexes that have become inactive or await a foreground receive. This is used to shorten the number of xarray iterations required prior to a match when queue (IO) depths are large, say 128. Replace the req_cnt atomic in struct sg_fd with the inactives atomic. With large queues, cycles were wasted checking the request xarray for any inactives when there were none to be found. Rename the sg_rq_state_chg_*() functions to sg_rq_chg_state_*() since too many things start with "sg_rq_state". Also the new function names emphasize the change part a little more. Rename the struct request pointer from rq to rqq and when it read and written to sg_request::rqq use READ_ONCE() and WRITE_ONCE() macros. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 421 ++++++++++++++++++++++++++++------------------ 1 file changed, 256 insertions(+), 165 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index a37e1156e4c2..e5e1daab990e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -119,6 +119,7 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_FRQ_DEACT_ORPHAN 6 /* not keeping orphan so de-activate */ #define SG_FRQ_RECEIVING 7 /* guard against multiple receivers */ #define SG_FRQ_FOR_MMAP 8 /* request needs PAGE_SIZE elements */ +#define SG_FRQ_COUNT_ACTIVE 9 /* sfp->submitted + waiting active */ /* Bit positions (flags) for sg_fd::ffd_bm bitmask follow */ #define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ @@ -217,7 +218,7 @@ struct sg_request { /* active SCSI command or inactive request */ unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ u8 *sense_bp; /* mempool alloc-ed sense buffer, as needed */ struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ - struct request *rq; /* released in sg_rq_end_io(), bio kept */ + struct request *rqq; /* released in sg_rq_end_io(), bio kept */ struct bio *bio; /* kept until this req -->SG_RS_INACTIVE */ struct execute_work ew_orph; /* harvest orphan request */ }; @@ -228,10 +229,12 @@ struct sg_fd { /* holds the state of a file descriptor */ struct mutex f_mutex; /* serialize ioctls on this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ + int low_used_idx; /* previous or lower used index */ + int low_await_idx; /* previous or lower await index */ u32 idx; /* my index within parent's sfp_arr */ atomic_t submitted; /* number inflight or awaiting receive */ atomic_t waiting; /* number of requests awaiting receive */ - atomic_t req_cnt; /* number of requests */ + atomic_t inactives; /* number of inactive requests */ int sgat_elem_sz; /* initialized to scatter_elem_sz */ int mmap_sz; /* byte size of previous mmap() call */ unsigned long ffd_bm[1]; /* see SG_FFD_* defines above */ @@ -302,7 +305,9 @@ static struct sg_device *sg_get_dev(int min_dev); static void sg_device_destroy(struct kref *kref); static struct sg_request *sg_mk_srp_sgat(struct sg_fd *sfp, bool first, int db_len); +#if IS_ENABLED(CONFIG_SCSI_LOGGING) && IS_ENABLED(CONFIG_SG_DEBUG) static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); +#endif #define SG_WRITE_COUNT_LIMIT (32 * 1024 * 1024) @@ -872,28 +877,28 @@ sg_ctl_iosubmit_v3(struct file *filp, struct sg_fd *sfp, void __user *p) #if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) static void sg_rq_state_fail_msg(struct sg_fd *sfp, enum sg_rq_state exp_old_st, - enum sg_rq_state want_st, enum sg_rq_state act_old_st, - const char *fromp) + enum sg_rq_state want_st, const char *fromp) { - const char *eaw_rs = "expected_old,actual_old,wanted rq_st"; + const char *eaw_rs = "expected_old,wanted rq_st"; if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) - SG_LOG(1, sfp, "%s: %s: %s: %s,%s,%s\n", + SG_LOG(1, sfp, "%s: %s: %s,%s,%s\n", __func__, fromp, eaw_rs, sg_rq_st_str(exp_old_st, false), - sg_rq_st_str(act_old_st, false), sg_rq_st_str(want_st, false)); else - pr_info("sg: %s: %s: %s: %d,%d,%d\n", __func__, fromp, eaw_rs, - (int)exp_old_st, (int)act_old_st, (int)want_st); + pr_info("sg: %s: %s: %s: %d,%d\n", __func__, fromp, eaw_rs, + (int)exp_old_st, (int)want_st); } #endif +/* Functions ending in '_ulck' assume sfp->xa_lock held by caller. */ static void -sg_rq_state_force(struct sg_request *srp, enum sg_rq_state new_st) +sg_rq_chg_state_force_ulck(struct sg_request *srp, enum sg_rq_state new_st) { bool prev, want; - struct xarray *xafp = &srp->parentfp->srp_arr; + struct sg_fd *sfp = srp->parentfp; + struct xarray *xafp = &sfp->srp_arr; atomic_set(&srp->rq_st, new_st); want = (new_st == SG_RS_AWAIT_RCV); @@ -907,15 +912,21 @@ sg_rq_state_force(struct sg_request *srp, enum sg_rq_state new_st) want = (new_st == SG_RS_INACTIVE); prev = xa_get_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); if (prev != want) { - if (want) + if (want) { + int prev_idx = READ_ONCE(sfp->low_used_idx); + + if (prev_idx < 0 || srp->rq_idx < prev_idx || + !xa_get_mark(xafp, prev_idx, SG_XA_RQ_INACTIVE)) + WRITE_ONCE(sfp->low_used_idx, srp->rq_idx); __xa_set_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); - else + } else { __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); + } } } static void -sg_rq_state_helper(struct xarray *xafp, struct sg_request *srp, int indic) +sg_rq_chg_state_help(struct xarray *xafp, struct sg_request *srp, int indic) { if (indic & 1) /* from inactive state */ __xa_clear_mark(xafp, srp->rq_idx, SG_XA_RQ_INACTIVE); @@ -929,8 +940,8 @@ sg_rq_state_helper(struct xarray *xafp, struct sg_request *srp, int indic) } /* Following array indexed by enum sg_rq_state, 0 means no xa mark change */ -static const int sg_rq_state_arr[] = {1, 0, 4, 0, 0}; -static const int sg_rq_state_mul2arr[] = {2, 0, 8, 0, 0}; +static const int sg_rq_state_arr[] = {1, 0, 4, 0}; +static const int sg_rq_state_mul2arr[] = {2, 0, 8, 0}; /* * This function keeps the srp->rq_st state and associated marks on the @@ -944,40 +955,56 @@ static const int sg_rq_state_mul2arr[] = {2, 0, 8, 0, 0}; * spinlock is held. */ static int -sg_rq_state_chg(struct sg_request *srp, enum sg_rq_state old_st, - enum sg_rq_state new_st, bool force, const char *fromp) +sg_rq_chg_state(struct sg_request *srp, enum sg_rq_state old_st, + enum sg_rq_state new_st) { enum sg_rq_state act_old_st; - int indic; - unsigned long iflags; - struct xarray *xafp = &srp->parentfp->srp_arr; + int indic = sg_rq_state_arr[(int)old_st] + sg_rq_state_mul2arr[(int)new_st]; + struct sg_fd *sfp = srp->parentfp; - if (force) { - xa_lock_irqsave(xafp, iflags); - sg_rq_state_force(srp, new_st); - xa_unlock_irqrestore(xafp, iflags); - return 0; - } - indic = sg_rq_state_arr[(int)old_st] + - sg_rq_state_mul2arr[(int)new_st]; - act_old_st = (enum sg_rq_state)atomic_cmpxchg(&srp->rq_st, old_st, - new_st); - if (act_old_st != old_st) { -#if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) - if (fromp) - sg_rq_state_fail_msg(srp->parentfp, old_st, new_st, - act_old_st, fromp); -#endif - return -EPROTOTYPE; /* only used for this error type */ - } if (indic) { + unsigned long iflags; + struct xarray *xafp = &sfp->srp_arr; + xa_lock_irqsave(xafp, iflags); - sg_rq_state_helper(xafp, srp, indic); + act_old_st = (enum sg_rq_state)atomic_cmpxchg_relaxed(&srp->rq_st, old_st, new_st); + if (unlikely(act_old_st != old_st)) { + xa_unlock_irqrestore(xafp, iflags); + SG_LOG(1, sfp, "%s: unexpected old state: %s\n", __func__, + sg_rq_st_str(act_old_st, false)); + return -EPROTOTYPE; /* only used for this error type */ + } + if (new_st == SG_RS_INACTIVE) { + int prev_idx = READ_ONCE(sfp->low_used_idx); + + if (prev_idx < 0 || srp->rq_idx < prev_idx || + !xa_get_mark(xafp, prev_idx, SG_XA_RQ_INACTIVE)) + WRITE_ONCE(sfp->low_used_idx, srp->rq_idx); + } + sg_rq_chg_state_help(xafp, srp, indic); xa_unlock_irqrestore(xafp, iflags); + } else { + act_old_st = (enum sg_rq_state)atomic_cmpxchg(&srp->rq_st, old_st, new_st); + if (unlikely(act_old_st != old_st)) { + SG_LOG(1, sfp, "%s: unexpected old state: %s\n", __func__, + sg_rq_st_str(act_old_st, false)); + return -EPROTOTYPE; /* only used for this error type */ + } } return 0; } +static void +sg_rq_chg_state_force(struct sg_request *srp, enum sg_rq_state new_st) +{ + unsigned long iflags; + struct xarray *xafp = &srp->parentfp->srp_arr; + + xa_lock_irqsave(xafp, iflags); + sg_rq_chg_state_force_ulck(srp, new_st); + xa_unlock_irqrestore(xafp, iflags); +} + static void sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) { @@ -998,14 +1025,15 @@ sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) at_head = !(srp->rq_flags & SG_FLAG_Q_AT_TAIL); kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ - sg_rq_state_chg(srp, SG_RS_BUSY /* ignored */, SG_RS_INFLIGHT, - true, __func__); + sg_rq_chg_state_force(srp, SG_RS_INFLIGHT); /* >>>>>>> send cmd/req off to other levels <<<<<<<< */ - if (!sync) + if (!sync) { atomic_inc(&sfp->submitted); - srp->rq->end_io = sg_rq_end_io; - blk_execute_rq_nowait(srp->rq, at_head); + set_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm); + } + srp->rqq->end_io = sg_rq_end_io; + blk_execute_rq_nowait(READ_ONCE(srp->rqq), (int)at_head); } /* @@ -1080,7 +1108,7 @@ sg_common_write(struct sg_comm_wr_t *cwrp) res = -ENODEV; goto err_out; } - srp->rq->timeout = cwrp->timeout; + READ_ONCE(srp->rqq)->timeout = cwrp->timeout; sg_execute_cmd(fp, srp); return srp; err_out: @@ -1197,10 +1225,6 @@ sg_receive_v3(struct sg_fd *sfp, struct sg_request *srp, size_t count, hp->driver_status = driver_byte(rq_result); err2 = put_sg_io_hdr(hp, buf); err = err ? err : err2; - err2 = sg_rq_state_chg(srp, atomic_read(&srp->rq_st), SG_RS_INACTIVE, - false, __func__); - if (err2) - err = err ? err : err2; err_out: sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); @@ -1211,7 +1235,7 @@ static int sg_receive_v4(struct sg_fd *sfp, struct sg_request *srp, void __user *p, struct sg_io_v4 *h4p) { - int err, err2; + int err; u32 rq_result = srp->rq_result; SG_LOG(3, sfp, "%s: p=%s, h4p=%s\n", __func__, @@ -1246,10 +1270,6 @@ sg_receive_v4(struct sg_fd *sfp, struct sg_request *srp, void __user *p, if (copy_to_user(p, h4p, SZ_SG_IO_V4)) err = err ? err : -EFAULT; } - err2 = sg_rq_state_chg(srp, atomic_read(&srp->rq_st), SG_RS_INACTIVE, - false, __func__); - if (err2) - err = err ? err : err2; sg_finish_scsi_blk_rq(srp); sg_deact_request(sfp, srp); return err < 0 ? err : 0; @@ -1615,6 +1635,7 @@ sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, case SG_RS_BUSY: res = sg_calc_rq_dur(srp); break; + case SG_RS_AWAIT_RCV: case SG_RS_INACTIVE: res = srp->duration; is_dur = true; /* completion has occurred, timing finished */ @@ -1650,7 +1671,7 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, static inline bool sg_rq_landed(struct sg_device *sdp, struct sg_request *srp) { - return atomic_read(&srp->rq_st) != SG_RS_INFLIGHT || + return atomic_read_acquire(&srp->rq_st) != SG_RS_INFLIGHT || unlikely(SG_IS_DETACHING(sdp)); } @@ -1666,6 +1687,8 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, enum sg_rq_state sr_st; struct sg_device *sdp = sfp->parentdp; + if (atomic_read(&srp->rq_st) != SG_RS_INFLIGHT) + goto skip_wait; /* and skip _acquire() */ SG_LOG(3, sfp, "%s: about to wait_event...()\n", __func__); /* usually will be woken up by sg_rq_end_io() callback */ res = wait_event_interruptible(sfp->read_wait, @@ -1678,16 +1701,22 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, __func__, res); return res; } +skip_wait: if (unlikely(SG_IS_DETACHING(sdp))) { - atomic_set(&srp->rq_st, SG_RS_INACTIVE); + sg_rq_chg_state_force(srp, SG_RS_INACTIVE); + atomic_inc(&sfp->inactives); return -ENODEV; } sr_st = atomic_read(&srp->rq_st); if (unlikely(sr_st != SG_RS_AWAIT_RCV)) return -EPROTO; /* Logic error */ - res = sg_rq_state_chg(srp, sr_st, SG_RS_BUSY, false, __func__); - if (unlikely(res)) + res = sg_rq_chg_state(srp, sr_st, SG_RS_BUSY); + if (unlikely(res)) { +#if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) + sg_rq_state_fail_msg(sfp, sr_st, SG_RS_BUSY, __func__); +#endif return res; + } if (test_bit(SG_FRQ_IS_V4I, srp->frq_bm)) res = sg_receive_v4(sfp, srp, p, h4p); else @@ -1939,7 +1968,10 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, SG_LOG(3, sfp, "%s: SG_GET_PACK_ID=%d\n", __func__, val); return put_user(val, ip); case SG_GET_NUM_WAITING: - return put_user(atomic_read(&sfp->waiting), ip); + val = atomic_read(&sfp->waiting); + if (val) + return put_user(val, ip); + return put_user(atomic_read_acquire(&sfp->waiting), ip); case SG_GET_SG_TABLESIZE: SG_LOG(3, sfp, "%s: SG_GET_SG_TABLESIZE=%d\n", __func__, sdp->max_sgat_sz); @@ -2127,11 +2159,16 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) static __poll_t sg_poll(struct file *filp, poll_table * wait) { + int num; __poll_t p_res = 0; struct sg_fd *sfp = filp->private_data; - poll_wait(filp, &sfp->read_wait, wait); - if (atomic_read(&sfp->waiting) > 0) + num = atomic_read(&sfp->waiting); + if (num < 1) { + poll_wait(filp, &sfp->read_wait, wait); + num = atomic_read(&sfp->waiting); + } + if (num > 0) p_res = EPOLLIN | EPOLLRDNORM; if (unlikely(SG_IS_DETACHING(sfp->parentdp))) @@ -2335,7 +2372,7 @@ sg_check_sense(struct sg_device *sdp, struct sg_request *srp, int sense_len) { int driver_stat; u32 rq_res = srp->rq_result; - struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(srp->rq); + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(READ_ONCE(srp->rqq)); u8 *sbp = scmd ? scmd->sense_buffer : NULL; if (!sbp) @@ -2370,32 +2407,18 @@ sg_check_sense(struct sg_device *sdp, struct sg_request *srp, int sense_len) * (sync) usage, sg_ctl_sg_io() waits to be woken up by this callback. */ static enum rq_end_io_ret -sg_rq_end_io(struct request *rq, blk_status_t status) +sg_rq_end_io(struct request *rqq, blk_status_t status) { enum sg_rq_state rqq_state = SG_RS_AWAIT_RCV; int a_resid, slen; - struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); - struct sg_request *srp = rq->end_io_data; + unsigned long iflags; + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rqq); + struct sg_request *srp = rqq->end_io_data; struct sg_device *sdp; struct sg_fd *sfp; - if (!srp) { - WARN_ONCE("%s: srp unexpectedly NULL\n", __func__); - return RQ_END_IO_NONE; - } - if (WARN_ON(atomic_read(&srp->rq_st) != SG_RS_INFLIGHT)) { - pr_warn("%s: bad rq_st=%d\n", __func__, - atomic_read(&srp->rq_st)); - goto early_err; - } sfp = srp->parentfp; - if (unlikely(!sfp)) { - WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__); - goto early_err; - } sdp = sfp->parentdp; - if (unlikely(SG_IS_DETACHING(sdp))) - pr_info("%s: device detaching\n", __func__); srp->rq_result = scmd->result; slen = min_t(int, scmd->sense_len, SCSI_SENSE_BUFFERSIZE); @@ -2403,7 +2426,7 @@ sg_rq_end_io(struct request *rq, blk_status_t status) if (a_resid) { if (test_bit(SG_FRQ_IS_V4I, srp->frq_bm)) { - if (rq_data_dir(rq) == READ) + if (rq_data_dir(rqq) == READ) srp->in_resid = a_resid; else srp->s_hdr4.out_resid = a_resid; @@ -2447,17 +2470,29 @@ sg_rq_end_io(struct request *rq, blk_status_t status) set_bit(SG_FRQ_DEACT_ORPHAN, srp->frq_bm); } } - if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) - atomic_inc(&sfp->waiting); - if (unlikely(sg_rq_state_chg(srp, SG_RS_INFLIGHT, rqq_state, - false, __func__))) - pr_warn("%s: can't set rq_st\n", __func__); + xa_lock_irqsave(&sfp->srp_arr, iflags); + sg_rq_chg_state_force_ulck(srp, rqq_state); + WRITE_ONCE(srp->rqq, NULL); + if (test_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm)) { + int num = atomic_inc_return(&sfp->waiting); + + if (num < 2) { + WRITE_ONCE(sfp->low_await_idx, srp->rq_idx); + } else { + int l_await_idx = READ_ONCE(sfp->low_await_idx); + + if (l_await_idx < 0 || srp->rq_idx < l_await_idx || + !xa_get_mark(&sfp->srp_arr, l_await_idx, SG_XA_RQ_AWAIT)) + WRITE_ONCE(sfp->low_await_idx, srp->rq_idx); + } + } + xa_unlock_irqrestore(&sfp->srp_arr, iflags); + /* * Free the mid-level resources apart from the bio (if any). The bio's * blk_rq_unmap_user() can be called later from user context. */ - srp->rq = NULL; - blk_mq_free_request(rq); + blk_mq_free_request(rqq); if (likely(rqq_state == SG_RS_AWAIT_RCV)) { /* Wake any sg_read()/ioctl(SG_IORECEIVE) awaiting this req */ @@ -2469,10 +2504,6 @@ sg_rq_end_io(struct request *rq, blk_status_t status) schedule_work(&srp->ew_orph.work); } return RQ_END_IO_NONE; - -early_err: - srp->rq = NULL; - return RQ_END_IO_FREE; } static const struct file_operations sg_fops = { @@ -2807,7 +2838,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) u32 rq_flags = srp->rq_flags; unsigned int iov_count = 0; void __user *up; - struct request *rq; + struct request *rqq; struct scsi_cmnd *scmd; struct sg_fd *sfp = cwrp->sfp; struct sg_device *sdp; @@ -2856,12 +2887,12 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) * do not want to use BLK_MQ_REQ_NOWAIT here because userspace might * not expect an EWOULDBLOCK from this condition. */ - rq = scsi_alloc_request(q, (r0w ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN), 0); - if (IS_ERR(rq)) - return PTR_ERR(rq); + rqq = scsi_alloc_request(q, (r0w ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN), 0); + if (IS_ERR(rqq)) + return PTR_ERR(rqq); /* current sg_request protected by SG_RS_BUSY state */ - scmd = blk_mq_rq_to_pdu(rq); - srp->rq = rq; + scmd = blk_mq_rq_to_pdu(rqq); + WRITE_ONCE(srp->rqq, rqq); if (cwrp->u_cmdp) res = sg_fetch_cmnd(cwrp->filp, sfp, cwrp->u_cmdp, @@ -2875,7 +2906,7 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) us_xfer = !(rq_flags & (SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO)); assign_bit(SG_FRQ_NO_US_XFER, srp->frq_bm, !us_xfer); reserved = (sfp->rsv_srp == srp); - rq->end_io_data = srp; + rqq->end_io_data = srp; scmd->retries = SG_DEFAULT_RETRIES; req_schp = &srp->sgat_h; @@ -2910,15 +2941,16 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) md->from_user = (dxfer_dir == SG_DXFER_TO_FROM_DEV); } - res = blk_rq_map_user_io(rq, md, up, dxfer_len, GFP_ATOMIC, + res = blk_rq_map_user_io(rqq, md, up, dxfer_len, GFP_ATOMIC, iov_count, iov_count, 1, r0w); fini: if (unlikely(res)) { /* failure, free up resources */ - srp->rq = NULL; + WRITE_ONCE(srp->rqq, NULL); SG_LOG(1, sfp, "%s: blk_rq_map_user_io() failed, iov_count=%d\n", __func__, iov_count); + blk_mq_free_request(rqq); } else { - srp->bio = rq->bio; + srp->bio = rqq->bio; } SG_LOG((res ? 1 : 4), sfp, "%s: %s res=%d [0x%pK]\n", __func__, cp, res, srp); @@ -2937,19 +2969,19 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) { int ret; struct sg_fd *sfp = srp->parentfp; - struct request *rq = srp->rq; + struct request *rqq = READ_ONCE(srp->rqq); SG_LOG(4, sfp, "%s: srp=0x%pK%s\n", __func__, srp, (srp->parentfp->rsv_srp == srp) ? " rsv" : ""); - if (!test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) { + if (test_and_clear_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm)) { atomic_dec(&sfp->submitted); atomic_dec(&sfp->waiting); } - /* Expect blk_mq_free_request(rq) already called in sg_rq_end_io() */ - if (rq) { /* blk_get_request() may have failed */ - srp->rq = NULL; - blk_mq_free_request(rq); + /* Expect blk_mq_free_request(rqq) already called in sg_rq_end_io() */ + if (rqq) { /* scsi_alloc_request() may have failed */ + WRITE_ONCE(srp->rqq, NULL); + blk_mq_free_request(rqq); } if (srp->bio) { bool us_xfer = !test_bit(SG_FRQ_NO_US_XFER, srp->frq_bm); @@ -3127,23 +3159,34 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) __maybe_unused bool is_bad_st = false; __maybe_unused enum sg_rq_state bad_sr_st = SG_RS_INACTIVE; bool search_for_1 = (pack_id != SG_PACK_ID_WILDCARD); + bool second = false; int res; int num_waiting = atomic_read(&sfp->waiting); - unsigned long idx; + int l_await_idx = READ_ONCE(sfp->low_await_idx); + unsigned long idx, s_idx; + unsigned long end_idx = ULONG_MAX; struct sg_request *srp = NULL; struct xarray *xafp = &sfp->srp_arr; *busy = false; - if (num_waiting < 1) - return NULL; + if (num_waiting < 1) { + num_waiting = atomic_read_acquire(&sfp->waiting); + if (num_waiting < 1) + return NULL; + } + + s_idx = (l_await_idx < 0) ? 0 : l_await_idx; + idx = s_idx; if (unlikely(search_for_1)) { - xa_for_each_marked(xafp, idx, srp, SG_XA_RQ_AWAIT) { +second_time: + for (srp = xa_find(xafp, &idx, end_idx, SG_XA_RQ_AWAIT); + srp; + srp = xa_find_after(xafp, &idx, end_idx, SG_XA_RQ_AWAIT)) { if (test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) continue; if (srp->pack_id != pack_id) continue; - res = sg_rq_state_chg(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY, - false, __func__); + res = sg_rq_chg_state(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY); if (likely(res == 0)) goto good; /* else another caller got it, move on */ @@ -3153,14 +3196,43 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) } break; } - } else { /* search for any request is more likely */ - xa_for_each_marked(xafp, idx, srp, SG_XA_RQ_AWAIT) { + /* If not found so far, need to wrap around and search [0 ... s_idx) */ + if (!srp && !second && s_idx > 0) { + end_idx = s_idx - 1; + s_idx = 0; + idx = s_idx; + second = true; + goto second_time; + } + } else { + /* + * Searching for _any_ request is the more likely usage. Start searching with the + * last xarray index that was used. In the case of a large-ish IO depth, it is + * likely that the second (relative) position will be the request we want, if it + * is ready. If there is no queuing and the "last used" has been re-used then the + * first (relative) position will be the request we want. + */ +second_time2: + for (srp = xa_find(xafp, &idx, end_idx, SG_XA_RQ_AWAIT); + srp; + srp = xa_find_after(xafp, &idx, end_idx, SG_XA_RQ_AWAIT)) { if (test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm)) continue; - res = sg_rq_state_chg(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY, - false, __func__); - if (likely(res == 0)) + res = sg_rq_chg_state(srp, SG_RS_AWAIT_RCV, SG_RS_BUSY); + if (likely(res == 0)) { + WRITE_ONCE(sfp->low_await_idx, idx + 1); goto good; + } +#if IS_ENABLED(CONFIG_SG_LOG_ACTIVE) + sg_rq_state_fail_msg(sfp, SG_RS_AWAIT_RCV, SG_RS_BUSY, __func__); +#endif + } + if (!srp && !second && s_idx > 0) { + end_idx = s_idx - 1; + s_idx = 0; + idx = s_idx; + second = true; + goto second_time2; } } /* here if one of above loops does _not_ find a match */ @@ -3288,11 +3360,13 @@ sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) bool found = false; bool mk_new_srp = true; bool try_harder = false; - int num_inactive = 0; - unsigned long idx, last_idx, iflags; + bool second = false; + bool has_inactive = false; + int l_used_idx; + unsigned long idx, s_idx, end_idx, iflags; struct sg_fd *fp = cwrp->sfp; struct sg_request *r_srp = NULL; /* request to return */ - struct sg_request *last_srp = NULL; + struct sg_request *low_srp = NULL; struct xarray *xafp = &fp->srp_arr; __maybe_unused const char *cp; @@ -3301,42 +3375,66 @@ sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) if (xa_empty(xafp)) { act_empty = true; mk_new_srp = true; + } else if (atomic_read(&fp->inactives) <= 0) { + mk_new_srp = true; } else if (!try_harder && dxfr_len < SG_DEF_SECTOR_SZ) { - last_idx = ~0UL; + l_used_idx = READ_ONCE(fp->low_used_idx); + s_idx = (l_used_idx < 0) ? 0 : l_used_idx; + if (l_used_idx >= 0 && xa_get_mark(xafp, s_idx, SG_XA_RQ_INACTIVE)) { + r_srp = xa_load(xafp, s_idx); + if (r_srp && r_srp->sgat_h.buflen <= SG_DEF_SECTOR_SZ) { + if (sg_rq_chg_state(r_srp, SG_RS_INACTIVE, SG_RS_BUSY) == 0) { + found = true; + atomic_dec(&fp->inactives); + goto have_existing; + } + } + } xa_for_each_marked(xafp, idx, r_srp, SG_XA_RQ_INACTIVE) { - if (!r_srp) - continue; - ++num_inactive; - if (dxfr_len < SG_DEF_SECTOR_SZ) { - last_idx = idx; - last_srp = r_srp; - continue; + has_inactive = true; + if (!low_srp && dxfr_len < SG_DEF_SECTOR_SZ) { + low_srp = r_srp; + break; } } - /* If dxfr_len is small, use last inactive request */ - if (last_idx != ~0UL && last_srp) { - r_srp = last_srp; - if (sg_rq_state_chg(r_srp, SG_RS_INACTIVE, SG_RS_BUSY, - false, __func__)) + /* If dxfr_len is small, use lowest inactive request */ + if (low_srp) { + r_srp = low_srp; + if (sg_rq_chg_state(r_srp, SG_RS_INACTIVE, SG_RS_BUSY)) goto start_again; /* gone to another thread */ + atomic_dec(&fp->inactives); cp = "toward end of srp_arr"; found = true; } } else { - xa_for_each_marked(xafp, idx, r_srp, SG_XA_RQ_INACTIVE) { - if (!r_srp) - continue; + l_used_idx = READ_ONCE(fp->low_used_idx); + s_idx = (l_used_idx < 0) ? 0 : l_used_idx; + idx = s_idx; + end_idx = ULONG_MAX; +second_time: + for (r_srp = xa_find(xafp, &idx, end_idx, SG_XA_RQ_INACTIVE); + r_srp; + r_srp = xa_find_after(xafp, &idx, end_idx, SG_XA_RQ_INACTIVE)) { if (r_srp->sgat_h.buflen >= dxfr_len) { - if (sg_rq_state_chg - (r_srp, SG_RS_INACTIVE, SG_RS_BUSY, - false, __func__)) + if (sg_rq_chg_state(r_srp, SG_RS_INACTIVE, SG_RS_BUSY)) continue; - cp = "from front of srp_arr"; + atomic_dec(&fp->inactives); + WRITE_ONCE(fp->low_used_idx, idx + 1); + cp = "near front of srp_arr"; found = true; break; } } + /* If not found so far, need to wrap around and search [0 ... start_idx) */ + if (!r_srp && !second && s_idx > 0) { + end_idx = s_idx - 1; + s_idx = 0; + idx = s_idx; + second = true; + goto second_time; + } } +have_existing: if (found) { r_srp->in_resid = 0; r_srp->rq_info = 0; @@ -3349,7 +3447,6 @@ sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) bool allow_cmd_q = test_bit(SG_FFD_CMD_Q, fp->ffd_bm); int res; u32 n_idx; - struct xa_limit xal = { .max = 0, .min = 0 }; cp = "new"; if (!allow_cmd_q && atomic_read(&fp->submitted) > 0) { @@ -3361,16 +3458,14 @@ sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len) r_srp = sg_mk_srp_sgat(fp, act_empty, dxfr_len); if (IS_ERR(r_srp)) { if (!try_harder && dxfr_len < SG_DEF_SECTOR_SZ && - num_inactive > 0) { + has_inactive) { try_harder = true; goto start_again; } goto fini; } - atomic_set(&r_srp->rq_st, SG_RS_BUSY); xa_lock_irqsave(xafp, iflags); - xal.max = atomic_inc_return(&fp->req_cnt); - res = __xa_alloc(xafp, &n_idx, r_srp, xal, GFP_KERNEL); + res = __xa_alloc(xafp, &n_idx, r_srp, xa_limit_32b, GFP_KERNEL); xa_unlock_irqrestore(xafp, iflags); if (res < 0) { SG_LOG(1, fp, "%s: xa_alloc() failed, errno=%d\n", @@ -3413,7 +3508,8 @@ sg_deact_request(struct sg_fd *sfp, struct sg_request *srp) sbp = srp->sense_bp; srp->sense_bp = NULL; WRITE_ONCE(srp->frq_bm[0], 0); - sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true /* force */, __func__); + sg_rq_chg_state_force(srp, SG_RS_INACTIVE); + atomic_inc(&sfp->inactives); /* maybe orphaned req, thus never read */ if (sbp) mempool_free(sbp, sg_sense_pool); @@ -3432,7 +3528,6 @@ sg_add_sfp(struct sg_device *sdp) struct sg_request *srp = NULL; struct xarray *xadp = &sdp->sfp_arr; struct xarray *xafp; - struct xa_limit xal; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); if (!sfp) @@ -3449,8 +3544,6 @@ sg_add_sfp(struct sg_device *sdp) __assign_bit(SG_FFD_CMD_Q, sfp->ffd_bm, SG_DEF_COMMAND_Q); __assign_bit(SG_FFD_KEEP_ORPHAN, sfp->ffd_bm, SG_DEF_KEEP_ORPHAN); __assign_bit(SG_FFD_Q_AT_TAIL, sfp->ffd_bm, SG_DEFAULT_Q_AT); - atomic_set(&sfp->submitted, 0); - atomic_set(&sfp->waiting, 0); /* * SG_SCATTER_SZ initializes scatter_elem_sz but different value may * be given as driver/module parameter (e.g. 'scatter_elem_sz=8192'). @@ -3462,7 +3555,7 @@ sg_add_sfp(struct sg_device *sdp) sfp->parentdp = sdp; atomic_set(&sfp->submitted, 0); atomic_set(&sfp->waiting, 0); - atomic_set(&sfp->req_cnt, 0); + atomic_set(&sfp->inactives, 0); if (unlikely(SG_IS_DETACHING(sdp))) { SG_LOG(1, sfp, "%s: detaching\n", __func__); @@ -3474,8 +3567,6 @@ sg_add_sfp(struct sg_device *sdp) rbuf_len = min_t(int, sg_big_buff, sdp->max_sgat_sz); if (rbuf_len > 0) { - struct xa_limit xalrq = { .max = 0, .min = 0 }; - srp = sg_build_reserve(sfp, rbuf_len); if (IS_ERR(srp)) { err = PTR_ERR(srp); @@ -3491,8 +3582,7 @@ sg_add_sfp(struct sg_device *sdp) __func__, rbuf_len, srp->sgat_h.buflen); } xa_lock_irqsave(xafp, iflags); - xalrq.max = atomic_inc_return(&sfp->req_cnt); - res = __xa_alloc(xafp, &idx, srp, xalrq, GFP_ATOMIC); + res = __xa_alloc(xafp, &idx, srp, xa_limit_32b, GFP_ATOMIC); xa_unlock_irqrestore(xafp, iflags); if (res < 0) { SG_LOG(1, sfp, "%s: xa_alloc(srp) bad, errno=%d\n", @@ -3504,20 +3594,19 @@ sg_add_sfp(struct sg_device *sdp) } srp->rq_idx = idx; srp->parentfp = sfp; - sg_rq_state_chg(srp, 0, SG_RS_INACTIVE, true, __func__); + sg_rq_chg_state_force(srp, SG_RS_INACTIVE); + atomic_inc(&sfp->inactives); } if (!reduced) { SG_LOG(4, sfp, "%s: built reserve buflen=%d\n", __func__, rbuf_len); } xa_lock_irqsave(xadp, iflags); - xal.min = 0; - xal.max = atomic_read(&sdp->open_cnt); - res = __xa_alloc(xadp, &idx, sfp, xal, GFP_KERNEL); + res = __xa_alloc(xadp, &idx, sfp, xa_limit_32b, GFP_KERNEL); xa_unlock_irqrestore(xadp, iflags); if (res < 0) { pr_warn("%s: xa_alloc(sdp) bad, o_count=%d, errno=%d\n", - __func__, xal.max, -res); + __func__, atomic_read(&sdp->open_cnt), -res); if (srp) { sg_remove_sgat(srp); kfree(srp); @@ -3929,11 +4018,13 @@ sg_proc_debug_fd(struct sg_fd *fp, char *obp, int len, unsigned long idx) (int)test_bit(SG_FFD_FORCE_PACKID, fp->ffd_bm), (int)test_bit(SG_FFD_KEEP_ORPHAN, fp->ffd_bm), fp->ffd_bm[0]); - n += scnprintf(obp + n, len - n, " mmap_sz=%d\n", fp->mmap_sz); n += scnprintf(obp + n, len - n, - " submitted=%d waiting=%d open thr_id=%d\n", + " mmap_sz=%d low_used_idx=%d low_await_idx=%d\n", + fp->mmap_sz, READ_ONCE(fp->low_used_idx), READ_ONCE(fp->low_await_idx)); + n += scnprintf(obp + n, len - n, + " submitted=%d waiting=%d inactives=%d open thr_id=%d\n", atomic_read(&fp->submitted), - atomic_read(&fp->waiting), fp->tid); + atomic_read(&fp->waiting), atomic_read(&fp->inactives), fp->tid); k = 0; xa_lock_irqsave(&fp->srp_arr, iflags); xa_for_each(&fp->srp_arr, idx, srp) { From patchwork Mon Oct 24 03:20:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016561 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E172C433FE for ; Mon, 24 Oct 2022 03:25:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230351AbiJXDZq (ORCPT ); Sun, 23 Oct 2022 23:25:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38410 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230354AbiJXDXI (ORCPT ); Sun, 23 Oct 2022 23:23:08 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E4A457695E for ; Sun, 23 Oct 2022 20:21:59 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 774312041BB; Mon, 24 Oct 2022 05:21:59 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bW-z0aRjBFMN; Mon, 24 Oct 2022 05:21:59 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 469042041AF; Mon, 24 Oct 2022 05:21:58 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 39/44] sg: remove unit attention check for device changed Date: Sun, 23 Oct 2022 23:20:53 -0400 Message-Id: <20221024032058.14077-40-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org The SCSI mid-layer now checks for SCSI UNIT ATTENTIONs and takes the appropriate actions. This means that the sg driver no longer needs to do this check. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 49 ++++++++++++----------------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index e5e1daab990e..f6acb1cffc57 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2367,39 +2367,6 @@ sg_rq_end_io_usercontext(struct work_struct *work) kref_put(&sfp->f_ref, sg_remove_sfp); } -static void -sg_check_sense(struct sg_device *sdp, struct sg_request *srp, int sense_len) -{ - int driver_stat; - u32 rq_res = srp->rq_result; - struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(READ_ONCE(srp->rqq)); - u8 *sbp = scmd ? scmd->sense_buffer : NULL; - - if (!sbp) - return; - driver_stat = driver_byte(rq_res); - if (driver_stat & DRIVER_SENSE) { - struct scsi_sense_hdr ssh; - - if (scsi_normalize_sense(sbp, sense_len, &ssh)) { - if (!scsi_sense_is_deferred(&ssh)) { - if (ssh.sense_key == UNIT_ATTENTION) { - if (sdp->device->removable) - sdp->device->changed = 1; - } - } - } - } - if (test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm) > 0) { - int scsi_stat = rq_res & 0xff; - - if (scsi_stat == SAM_STAT_CHECK_CONDITION || - scsi_stat == SAM_STAT_COMMAND_TERMINATED) - __scsi_print_sense(sdp->device, __func__, sbp, - sense_len); - } -} - /* * This "bottom half" (soft interrupt) handler is called by the mid-level * when a request has completed or failed. This callback is registered in a @@ -2411,6 +2378,7 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) { enum sg_rq_state rqq_state = SG_RS_AWAIT_RCV; int a_resid, slen; + u32 rq_result; unsigned long iflags; struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rqq); struct sg_request *srp = rqq->end_io_data; @@ -2420,7 +2388,8 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) sfp = srp->parentfp; sdp = sfp->parentdp; - srp->rq_result = scmd->result; + rq_result = scmd->result; + srp->rq_result = rq_result; slen = min_t(int, scmd->sense_len, SCSI_SENSE_BUFFERSIZE); a_resid = scmd->resid_len; @@ -2436,10 +2405,16 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) } SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->pack_id, - srp->rq_result); + rq_result); srp->duration = sg_calc_rq_dur(srp); - if (unlikely((srp->rq_result & SG_ML_RESULT_MSK) && slen > 0)) - sg_check_sense(sdp, srp, slen); + if (unlikely((rq_result & SG_ML_RESULT_MSK) && slen > 0 && + test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm))) { + u32 scsi_stat = rq_result & 0xff; + + if (scsi_stat == SAM_STAT_CHECK_CONDITION || + scsi_stat == SAM_STAT_COMMAND_TERMINATED) + __scsi_print_sense(sdp->device, __func__, scmd->sense_buffer, slen); + } if (slen > 0) { if (scmd->sense_buffer && !srp->sense_bp) { srp->sense_bp = mempool_alloc(sg_sense_pool, GFP_ATOMIC); From patchwork Mon Oct 24 03:20:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016570 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9A8BAFA373D for ; Mon, 24 Oct 2022 03:26:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230334AbiJXD0H (ORCPT ); Sun, 23 Oct 2022 23:26:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55338 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230358AbiJXDXJ (ORCPT ); Sun, 23 Oct 2022 23:23:09 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 49CA075486 for ; Sun, 23 Oct 2022 20:22:01 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id DF2C12041BD; Mon, 24 Oct 2022 05:22:00 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dggw5tCBkvan; Mon, 24 Oct 2022 05:22:00 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id A6FB72041AF; Mon, 24 Oct 2022 05:21:59 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 40/44] sg: no_dxfer: move to/from kernel buffers Date: Sun, 23 Oct 2022 23:20:54 -0400 Message-Id: <20221024032058.14077-41-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org When the NO_DXFER flag is use on a command/request, the data-in and data-out buffers (if present) should not be ignored. Add sg_rq_map_kern() function to handle this. Uses a single bio with multiple bvec_s usually each holding multiple pages, if necessary. The driver default element size is 32 KiB so if PAGE_SIZE is 4096 then get_order()==3 . Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index f6acb1cffc57..94f55ce27c64 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2278,6 +2278,24 @@ static const struct vm_operations_struct sg_mmap_vm_ops = { .close = sg_vma_close, }; +static inline void +sg_bio_get(struct bio *bio) +{ + bio_get(bio); +} + +static void +sg_bio_put(struct bio *bio) +{ + if (unlikely(bio_flagged(bio, BIO_REFFED))) { + WARN_ON(!atomic_read(&bio->__bi_cnt)); + if (!atomic_dec_and_test(&bio->__bi_cnt)) + return; + } + bio_uninit(bio); + kfree(bio); +} + /* * Entry point for mmap(2) system call. For mmap(2) to work, request's * scatter gather list needs to be order 0 which it is unlikely to be @@ -2792,6 +2810,59 @@ exit_sg(void) idr_destroy(&sg_index_idr); } +static struct bio * +sg_mk_kern_bio(int bvec_cnt) +{ + struct bio *biop; + + if (bvec_cnt > BIO_MAX_VECS) + return NULL; + biop = bio_kmalloc(bvec_cnt, GFP_ATOMIC); + if (!biop) + return NULL; + return biop; +} + +/* + * Setup to move data between kernel buffers managed by this driver and a SCSI device. Note that + * there is no corresponding 'unmap' call as is required by blk_rq_map_user() . Uses a single + * bio with an expanded appended bvec if necessary. + */ +static int +sg_rq_map_kern(struct sg_request *srp, struct request_queue *q, struct request *rqq, int rw_ind) +{ + struct sg_scatter_hold *schp = &srp->sgat_h; + struct bio *bio; + int k, ln; + int op_flags = 0; + int num_sgat = schp->num_sgat; + int dlen = schp->dlen; + int pg_sz = 1 << (PAGE_SHIFT + schp->page_order); + + SG_LOG(4, srp->parentfp, "%s: dlen=%d, pg_sz=%d\n", __func__, dlen, pg_sz); + if (num_sgat <= 0) + return 0; + if (rw_ind == WRITE) + op_flags = REQ_SYNC | REQ_IDLE; + bio = sg_mk_kern_bio(num_sgat); + if (!bio) + return -ENOMEM; + bio_init(bio, NULL, bio->bi_inline_vecs, num_sgat, (req_op(rqq) | op_flags)); + bio->bi_end_io = sg_bio_put; + + for (k = 0; k < num_sgat && dlen > 0; ++k, dlen -= ln) { + ln = min_t(int, dlen, pg_sz); + if (bio_add_pc_page(q, bio, schp->pages[k], ln, 0) < ln) { + sg_bio_put(bio); + return -EINVAL; + } + } + /* used blk_rq_append_bio() before but this is simpler */ + blk_rq_bio_prep(rqq, bio, num_sgat); + rqq->nr_phys_segments = (1 << schp->page_order) * num_sgat; + return 0; +} + static inline void sg_set_map_data(const struct sg_scatter_hold *schp, bool up_valid, struct rq_map_data *mdp) @@ -2916,8 +2987,11 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) md->from_user = (dxfer_dir == SG_DXFER_TO_FROM_DEV); } - res = blk_rq_map_user_io(rqq, md, up, dxfer_len, GFP_ATOMIC, - iov_count, iov_count, 1, r0w); + if (us_xfer) + res = blk_rq_map_user_io(rqq, md, up, dxfer_len, GFP_ATOMIC, + iov_count, iov_count, 1, r0w); + else /* transfer data to/from kernel buffers */ + res = sg_rq_map_kern(srp, q, rqq, r0w); fini: if (unlikely(res)) { /* failure, free up resources */ WRITE_ONCE(srp->rqq, NULL); From patchwork Mon Oct 24 03:20:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016573 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67D64FA373D for ; Mon, 24 Oct 2022 03:26:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230319AbiJXD0P (ORCPT ); Sun, 23 Oct 2022 23:26:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55682 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230139AbiJXDX2 (ORCPT ); Sun, 23 Oct 2022 23:23:28 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 53DA8786C4 for ; Sun, 23 Oct 2022 20:22:02 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 966022041C0; Mon, 24 Oct 2022 05:22:02 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EBYQY7fs80tf; Mon, 24 Oct 2022 05:22:02 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 18B8D2041AF; Mon, 24 Oct 2022 05:22:00 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 41/44] sg: add bio_poll support Date: Sun, 23 Oct 2022 23:20:55 -0400 Message-Id: <20221024032058.14077-42-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Adds the SGV4_FLAG_POLLED command flag which causes REQ_POLLED to be set on the request. This will attempt to do polling in the associated SCSI low level driver (LLD) for completion. An example of an LLD is the driver for a SAS HBA (host bus adapter) such as mpt3sas. Before waiting on an inflight request, a check is made to see if the SGV4_FLAG_POLLED is set and if so, bio_poll() is called instead of the wait. In situations where only the file descriptor is known (e.g. sg_poll() and ioctl(SG_GET_NUM_WAITING)) all inflight requests associated with the file descriptor that have SGV4_FLAG_POLLED set, have bio_poll() called on them. It is important to know blk_execute_rq_nowait() has finished before sending bio_poll() on that request. The SG_RS_INFLIGHT state is set just before blk_execute_rq_nowait() is called so a new bit setting SG_FRQ_ISSUED has been added that is set just after that calls returns. bio_poll() is char device/driver unfriendly. It needs information such as the associated request_queue placed in a struct block_device object, something the a char device (e.g. a sg device) does not normally have. Enter the new dummy_bdev pointer placed in struct sg_device. It remains NULL until the first time a SGV4_FLAG_POLLED flag is set on a command for a given device. Only then is the dummy block_object created. It would be much easier if bio_poll() just accepted a request_queue argument. Note that the implementation of bio_poll() calls mq_poll() in the LLD associated with the request. Then for any request found to be ready, bio_poll() invokes the scsi_done() callback. When bio_poll() returns > 0 , sg_rq_end_io() may have been called on the given request. If so the given request will be in await_rcv state. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 205 ++++++++++++++++++++++++++++++++++++----- include/uapi/scsi/sg.h | 1 + 2 files changed, 182 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 94f55ce27c64..496e7a89bf8e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -120,12 +120,15 @@ enum sg_rq_state { /* N.B. sg_rq_state_arr assumes SG_RS_AWAIT_RCV==2 */ #define SG_FRQ_RECEIVING 7 /* guard against multiple receivers */ #define SG_FRQ_FOR_MMAP 8 /* request needs PAGE_SIZE elements */ #define SG_FRQ_COUNT_ACTIVE 9 /* sfp->submitted + waiting active */ +#define SG_FRQ_ISSUED 10 /* blk_execute_rq_nowait() finished */ +#define SG_FRQ_POLLING 11 /* to stop co-incident bio_poll()s */ /* Bit positions (flags) for sg_fd::ffd_bm bitmask follow */ #define SG_FFD_FORCE_PACKID 0 /* receive only given pack_id/tag */ #define SG_FFD_CMD_Q 1 /* clear: only 1 active req per fd */ #define SG_FFD_KEEP_ORPHAN 2 /* policy for this fd */ -#define SG_FFD_Q_AT_TAIL 3 /* set: queue reqs at tail of blk q */ +#define SG_FFD_POLLED_SEEN 3 /* could have POLLED requests active */ +#define SG_FFD_Q_AT_TAIL 4 /* set: queue reqs at tail of blk q */ /* Bit positions (flags) for sg_device::fdev_bm bitmask follow */ #define SG_FDEV_EXCLUDE 0 /* have fd open with O_EXCL */ @@ -259,6 +262,7 @@ struct sg_device { /* holds the state of each scsi generic device */ unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ char name[DISK_NAME_LEN]; struct cdev *cdev; + struct block_device *dummy_bdev; /* hack for REQ_POLLED */ struct xarray sfp_arr; struct kref d_ref; }; @@ -305,6 +309,9 @@ static struct sg_device *sg_get_dev(int min_dev); static void sg_device_destroy(struct kref *kref); static struct sg_request *sg_mk_srp_sgat(struct sg_fd *sfp, bool first, int db_len); +static int sg_sfp_bio_poll(struct sg_fd *sfp, int loop_count); +static int sg_srp_q_bio_poll(struct sg_request *srp, struct request_queue *q, + int loop_count); #if IS_ENABLED(CONFIG_SCSI_LOGGING) && IS_ENABLED(CONFIG_SG_DEBUG) static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #endif @@ -1009,6 +1016,7 @@ static void sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) { bool at_head, is_v4h, sync; + struct request *rqq = READ_ONCE(srp->rqq); is_v4h = test_bit(SG_FRQ_IS_V4I, srp->frq_bm); sync = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); @@ -1032,8 +1040,22 @@ sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) atomic_inc(&sfp->submitted); set_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm); } + if (srp->rq_flags & SGV4_FLAG_POLLED) { + if (test_bit(QUEUE_FLAG_POLL, &rqq->q->queue_flags)) { + set_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm); + rqq->cmd_flags |= REQ_POLLED; + if (srp->bio) + srp->bio->bi_opf |= REQ_POLLED; + } else { + if (srp->bio && (srp->bio->bi_opf & REQ_POLLED)) + srp->bio->bi_opf &= ~REQ_POLLED; + clear_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm); + srp->rq_flags &= ~SGV4_FLAG_POLLED; + } + } srp->rqq->end_io = sg_rq_end_io; - blk_execute_rq_nowait(READ_ONCE(srp->rqq), (int)at_head); + blk_execute_rq_nowait(rqq, (int)at_head); + set_bit(SG_FRQ_ISSUED, srp->frq_bm); } /* @@ -1689,6 +1711,13 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, if (atomic_read(&srp->rq_st) != SG_RS_INFLIGHT) goto skip_wait; /* and skip _acquire() */ + if (srp->rq_flags & SGV4_FLAG_POLLED) { + /* call bio_poll(), spinning till found */ + res = sg_srp_q_bio_poll(srp, sdp->device->request_queue, -1); + if (res != -ENODATA && unlikely(res < 0)) + return res; + goto skip_wait; + } SG_LOG(3, sfp, "%s: about to wait_event...()\n", __func__); /* usually will be woken up by sg_rq_end_io() callback */ res = wait_event_interruptible(sfp->read_wait, @@ -1968,6 +1997,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, SG_LOG(3, sfp, "%s: SG_GET_PACK_ID=%d\n", __func__, val); return put_user(val, ip); case SG_GET_NUM_WAITING: + if (test_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm)) + sg_sfp_bio_poll(sfp, 0); /* LLD may have some ready */ val = atomic_read(&sfp->waiting); if (val) return put_user(val, ip); @@ -2152,6 +2183,120 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return scsi_ioctl(sdp->device, filp->f_mode, cmd_in, p); } +static inline void +sg_bio_get(struct bio *bio) +{ + bio_get(bio); +} + +static void +sg_bio_put(struct bio *bio) +{ + if (unlikely(bio_flagged(bio, BIO_REFFED))) { + WARN_ON(!atomic_read(&bio->__bi_cnt)); + if (!atomic_dec_and_test(&bio->__bi_cnt)) + return; + } + bio_uninit(bio); + kfree(bio); +} + +/* + * If the sg_request object is in SG_RS_AWAIT_RCV state, return 1. Otherwise, + * if it is not inflight, return -ENODATA. This function returns 1 if the + * given object was in inflight state and is in await_rcv state after + * bio_poll() returns 1 or more. If bio_poll() fails, then that (negative) + * value is returned. Otherwise returns 0. Note that bio_poll() may complete + * unrelated requests that share the same q. + */ +static int +sg_srp_q_bio_poll(struct sg_request *srp, struct request_queue *q, int loop_count) +{ + int n = 0; + int k, num; + struct bio *biop; + struct sg_fd *sfp = srp->parentfp; + enum sg_rq_state sr_st = atomic_read(&srp->rq_st); + + if (sr_st == SG_RS_AWAIT_RCV) + return 1; + else if (sr_st != SG_RS_INFLIGHT) + return -ENODATA; + if (test_and_set_bit(SG_FRQ_POLLING, srp->frq_bm)) + return 0; /* something else is polling this request */ + biop = READ_ONCE(srp->bio); + if (!biop) { + clear_bit(SG_FRQ_POLLING, srp->frq_bm); + return 0; + } + sg_bio_get(biop); + if (READ_ONCE(biop->bi_cookie) == BLK_QC_T_NONE) { + if (atomic_read(&srp->rq_st) == SG_RS_AWAIT_RCV) + n = 1; + else + SG_LOG(6, sfp, "%s: cookie==BLK_QC_T_NONE\n", __func__); + goto fini; + } + if (!READ_ONCE(biop->bi_bdev)) + WRITE_ONCE(biop->bi_bdev, sfp->parentdp->dummy_bdev); + + num = (loop_count < 1) ? 1 : loop_count; + for (n = 0, k = 0; k < num; ++k) { + n = bio_poll(biop, NULL, (loop_count < 0) ? BLK_POLL_NOSLEEP : 0); + if (n > 0) { + n = !!(atomic_read(&srp->rq_st) == SG_RS_AWAIT_RCV); + break; + } else if (n < 0) { + break; + } + if (atomic_read(&srp->rq_st) != SG_RS_INFLIGHT) + break; + } +fini: + clear_bit(SG_FRQ_POLLING, srp->frq_bm); + sg_bio_put(biop); + return n; +} + +/* + * Check all requests on this sfp that are both inflight and POLLED. That check involves calling + * bio_poll(spin<-false) loop_count times. If loop_count is 0 then call bio_poll once. + * If loop_count is negative then call bio_poll(spin <- true)) once for each request. + * Returns number found (could be 0) or a negated errno value. + */ +static int +sg_sfp_bio_poll(struct sg_fd *sfp, int loop_count) +{ + int res = 0; + int n; + unsigned long idx, iflags; + struct sg_request *srp; + struct scsi_device *sdev = sfp->parentdp->device; + struct request_queue *q = sdev ? sdev->request_queue : NULL; + struct xarray *xafp = &sfp->srp_arr; + + if (!q) + return -EINVAL; + xa_lock_irqsave(xafp, iflags); + xa_for_each(xafp, idx, srp) { + if ((srp->rq_flags & SGV4_FLAG_POLLED) && + !test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm) && + atomic_read(&srp->rq_st) == SG_RS_INFLIGHT && + test_bit(SG_FRQ_ISSUED, srp->frq_bm)) { + xa_unlock_irqrestore(xafp, iflags); + n = sg_srp_q_bio_poll(srp, q, loop_count); + if (n == -ENODATA) + n = 0; + if (unlikely(n < 0)) + return n; + xa_lock_irqsave(xafp, iflags); + res += n; + } + } + xa_unlock_irqrestore(xafp, iflags); + return res; +} + /* * Implements the poll(2) system call for this driver. Returns various EPOLL* * flags OR-ed together. @@ -2163,6 +2308,8 @@ sg_poll(struct file *filp, poll_table * wait) __poll_t p_res = 0; struct sg_fd *sfp = filp->private_data; + if (test_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm)) + sg_sfp_bio_poll(sfp, 0); /* LLD may have some ready to push up */ num = atomic_read(&sfp->waiting); if (num < 1) { poll_wait(filp, &sfp->read_wait, wait); @@ -2278,24 +2425,6 @@ static const struct vm_operations_struct sg_mmap_vm_ops = { .close = sg_vma_close, }; -static inline void -sg_bio_get(struct bio *bio) -{ - bio_get(bio); -} - -static void -sg_bio_put(struct bio *bio) -{ - if (unlikely(bio_flagged(bio, BIO_REFFED))) { - WARN_ON(!atomic_read(&bio->__bi_cnt)); - if (!atomic_dec_and_test(&bio->__bi_cnt)) - return; - } - bio_uninit(bio); - kfree(bio); -} - /* * Entry point for mmap(2) system call. For mmap(2) to work, request's * scatter gather list needs to be order 0 which it is unlikely to be @@ -2464,6 +2593,7 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) } } xa_lock_irqsave(&sfp->srp_arr, iflags); + __set_bit(SG_FRQ_ISSUED, srp->frq_bm); sg_rq_chg_state_force_ulck(srp, rqq_state); WRITE_ONCE(srp->rqq, NULL); if (test_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm)) { @@ -2489,7 +2619,8 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) if (likely(rqq_state == SG_RS_AWAIT_RCV)) { /* Wake any sg_read()/ioctl(SG_IORECEIVE) awaiting this req */ - wake_up_interruptible(&sfp->read_wait); + if (!(srp->rq_flags & SGV4_FLAG_POLLED)) + wake_up_interruptible(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kref_put(&sfp->f_ref, sg_remove_sfp); } else { /* clean up orphaned request that aren't being kept */ @@ -2529,6 +2660,7 @@ sg_add_device_helper(struct scsi_device *scsidp) sdp = kzalloc(sizeof(*sdp), GFP_KERNEL); if (!sdp) return ERR_PTR(-ENOMEM); + /* sdp->dummy_bdev starts as NULL until a POLLED command sent on this device */ idr_preload(GFP_KERNEL); write_lock_irqsave(&sg_index_lock, iflags); @@ -2634,6 +2766,7 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) write_lock_irqsave(&sg_index_lock, iflags); idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, iflags); + kfree(sdp->dummy_bdev); kfree(sdp); out: @@ -2662,6 +2795,7 @@ sg_device_destroy(struct kref *kref) idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, flags); + kfree(sdp->dummy_bdev); kfree(sdp); } @@ -3000,6 +3134,25 @@ sg_start_req(struct sg_request *srp, struct sg_comm_wr_t *cwrp, int dxfer_dir) blk_mq_free_request(rqq); } else { srp->bio = rqq->bio; + /* check if first POLLED command on this device */ + if (unlikely((rq_flags & SGV4_FLAG_POLLED) && !READ_ONCE(sdp->dummy_bdev))) { + bool success = false; + unsigned long iflags; + struct xarray *xadp = &sdp->sfp_arr; + struct block_device *bdevp = kzalloc(sizeof(*bdevp), GFP_KERNEL); + + xa_lock_irqsave(xadp, iflags); + if (bdevp && !sdp->dummy_bdev) { + bdevp->bd_queue = sdp->device->request_queue; + sdp->dummy_bdev = bdevp; + success = true; + } + xa_unlock_irqrestore(xadp, iflags); + if (!bdevp) + res = -ENOMEM; + if (!success) + kfree(bdevp); + } } SG_LOG((res ? 1 : 4), sfp, "%s: %s res=%d [0x%pK]\n", __func__, cp, res, srp); @@ -3023,7 +3176,8 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) SG_LOG(4, sfp, "%s: srp=0x%pK%s\n", __func__, srp, (srp->parentfp->rsv_srp == srp) ? " rsv" : ""); if (test_and_clear_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm)) { - atomic_dec(&sfp->submitted); + if (atomic_dec_and_test(&sfp->submitted)) + clear_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm); atomic_dec(&sfp->waiting); } @@ -3218,6 +3372,8 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) struct xarray *xafp = &sfp->srp_arr; *busy = false; + if (test_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm)) + sg_sfp_bio_poll(sfp, 0); /* LLD may have some ready to push up */ if (num_waiting < 1) { num_waiting = atomic_read_acquire(&sfp->waiting); if (num_waiting < 1) @@ -4036,8 +4192,9 @@ sg_proc_debug_sreq(struct sg_request *srp, int to, char *obp, int len) else if (dur < U32_MAX) /* in-flight or busy (so ongoing) */ n += scnprintf(obp + n, len - n, " t_o/elap=%us/%ums", to / 1000, dur); - n += scnprintf(obp + n, len - n, " sgat=%d op=0x%02x\n", - srp->sgat_h.num_sgat, srp->cmd_opcode); + cp = (srp->rq_flags & SGV4_FLAG_POLLED) ? "polled " : ""; + n += scnprintf(obp + n, len - n, " sgat=%d %sop=0x%02x\n", + srp->sgat_h.num_sgat, cp, srp->cmd_opcode); return n; } diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 9060c40957f9..2e1e0cf5d686 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -109,6 +109,7 @@ typedef struct sg_io_hdr { #define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL #define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD #define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR, ignored in SG_IOS */ +#define SGV4_FLAG_POLLED 0x800 /* request polls LLD for completion */ /* Output (potentially OR-ed together) in v3::info or v4::info field */ #define SG_INFO_OK_MASK 0x1 From patchwork Mon Oct 24 03:20:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016574 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C9FF6C3A59D for ; Mon, 24 Oct 2022 03:26:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230370AbiJXD0Q (ORCPT ); Sun, 23 Oct 2022 23:26:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59146 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230415AbiJXDYL (ORCPT ); Sun, 23 Oct 2022 23:24:11 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id E07316F268 for ; Sun, 23 Oct 2022 20:22:04 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 58C0A2041BB; Mon, 24 Oct 2022 05:22:04 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ntK0bEu3Xghc; Mon, 24 Oct 2022 05:22:04 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id C264F2041AF; Mon, 24 Oct 2022 05:22:02 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Damien Le Moal Subject: [PATCH v25 42/44] sg: add statistics similar to st Date: Sun, 23 Oct 2022 23:20:56 -0400 Message-Id: <20221024032058.14077-43-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Using the existing statistics gathering framework from the st driver, collect statistics for access via sysfs. The sysstat package already has a utility called tapestat for presenting st statistics. Its author is keen to use the existing tapestat code for showing sg statistics (rather than write a new utility). In keeping with the sg driver being SCSI command agnostic, the "read" statistics are compiled for requests that have "data-in" user data while write statistics are compiled for requests that have "data-out" user data. Reviewed-by: Hannes Reinecke Reviewed-by: Damien Le Moal Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 260 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 228 insertions(+), 32 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 496e7a89bf8e..4bf2363d1b17 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -217,7 +217,7 @@ struct sg_request { /* active SCSI command or inactive request */ int sense_len; /* actual sense buffer length (data-in) */ atomic_t rq_st; /* request state, holds a enum sg_rq_state */ u8 cmd_opcode; /* first byte of SCSI cdb */ - u64 start_ns; /* starting point of command duration calc */ + ktime_t start_dur; /* start time if before completion */ unsigned long frq_bm[1]; /* see SG_FRQ_* defines above */ u8 *sense_bp; /* mempool alloc-ed sense buffer, as needed */ struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ @@ -260,9 +260,11 @@ struct sg_device { /* holds the state of each scsi generic device */ u32 index; /* device index number */ atomic_t open_cnt; /* count of opens (perhaps < num(sfds) ) */ unsigned long fdev_bm[1]; /* see SG_FDEV_* defines above */ + spinlock_t stats_lock; char name[DISK_NAME_LEN]; struct cdev *cdev; struct block_device *dummy_bdev; /* hack for REQ_POLLED */ + struct sg_dev_stats *statsp; struct xarray sfp_arr; struct kref d_ref; }; @@ -280,6 +282,19 @@ struct sg_comm_wr_t { /* arguments to sg_common_write() */ const u8 __user *u_cmdp; }; +struct sg_dev_stats { /* copied from drivers/scsi/st.h scsi_tape_stats */ + u64 read_byte_cnt; /* data-in bytes */ + u64 write_byte_cnt; /* data-out bytes */ + u64 read_cnt; /* Count of data-in requests */ + u64 write_cnt; /* Count of data-out requests */ + u64 other_cnt; /* Count of non-data requests */ + u64 resid_cnt; /* Count of cmds with resid_len > 0 */ + u64 tot_read_time; /* time spent completing data-in requests */ + u64 tot_write_time; /* time spent completing data-out requests */ + u64 tot_ndata_time; /* time spent completing non-data requests */ + atomic_t in_flight; /* Number of I/Os in flight */ +}; + /* tasklet or soft irq callback */ static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status); /* Declarations of other static functions used before they are defined */ @@ -312,6 +327,8 @@ static struct sg_request *sg_mk_srp_sgat(struct sg_fd *sfp, bool first, static int sg_sfp_bio_poll(struct sg_fd *sfp, int loop_count); static int sg_srp_q_bio_poll(struct sg_request *srp, struct request_queue *q, int loop_count); +static u32 sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, + bool *is_durp); #if IS_ENABLED(CONFIG_SCSI_LOGGING) && IS_ENABLED(CONFIG_SG_DEBUG) static const char *sg_rq_st_str(enum sg_rq_state rq_st, bool long_str); #endif @@ -1017,11 +1034,17 @@ sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp) { bool at_head, is_v4h, sync; struct request *rqq = READ_ONCE(srp->rqq); + struct sg_device *sdp = sfp->parentdp; is_v4h = test_bit(SG_FRQ_IS_V4I, srp->frq_bm); sync = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); SG_LOG(3, sfp, "%s: is_v4h=%d\n", __func__, (int)is_v4h); - srp->start_ns = ktime_get_boottime_ns(); + if (sdp->statsp) { + atomic_inc(&sdp->statsp->in_flight); + WRITE_ONCE(srp->start_dur, ktime_get_boottime()); + } else { + WRITE_ONCE(srp->start_dur, 0); + } srp->duration = 0; if (!is_v4h && srp->s_hdr3.interface_id == '\0') @@ -1195,11 +1218,47 @@ sg_copy_sense(struct sg_request *srp, bool v4_active) return sb_len_ret; } +static void +sg_do_stats(struct sg_fd *sfp, struct sg_request *srp, bool v4_active) +{ + int dir = v4_active ? srp->s_hdr4.dir : srp->s_hdr3.dxfer_direction; + ktime_t kt = READ_ONCE(srp->start_dur); + u64 ns = (kt > 0) ? ktime_to_ns(kt) : 0; + struct sg_device *sdp = sfp->parentdp; + struct sg_dev_stats *statsp = sdp->statsp; + + if (!statsp) + return; + spin_lock(&sdp->stats_lock); + if (dir == SG_DXFER_TO_DEV) { /* data-out, write-like */ + statsp->tot_write_time += ns; + ++statsp->write_cnt; + statsp->write_byte_cnt += srp->sgat_h.dlen; + } else if (dir == SG_DXFER_FROM_DEV) { /* data-in, read-like */ + statsp->tot_read_time += ns; + ++statsp->read_cnt; + statsp->read_byte_cnt += srp->sgat_h.dlen; + if (srp->in_resid > 0) + ++statsp->resid_cnt; + } else { /* no data transfer (e.g. TEST UNIT READY) */ + statsp->tot_ndata_time += ns; + ++statsp->other_cnt; + } + atomic_dec(&statsp->in_flight); + spin_unlock(&sdp->stats_lock); +} + static int sg_rec_state_v3v4(struct sg_fd *sfp, struct sg_request *srp, bool v4_active) { u32 rq_res = srp->rq_result; + if (sfp->parentdp->statsp) { + const enum sg_rq_state sr_st = SG_RS_BUSY; + + sg_do_stats(sfp, srp, v4_active); + srp->duration = sg_get_dur(srp, &sr_st, NULL); + } if (unlikely(srp->rq_result & 0xff)) { int sb_len_wr = sg_copy_sense(srp, v4_active); @@ -1625,49 +1684,41 @@ sg_calc_sgat_param(struct sg_device *sdp) sdp->max_sgat_sz = sz; } -static u32 -sg_calc_rq_dur(const struct sg_request *srp) -{ - ktime_t ts0 = ns_to_ktime(srp->start_ns); - ktime_t now_ts; - s64 diff; - - if (ts0 == 0) - return 0; - if (unlikely(ts0 == S64_MAX)) /* _prior_ to issuing req */ - return 999999999; /* eye catching */ - now_ts = ktime_get_boottime(); - if (unlikely(ts0 > now_ts)) - return 999999998; - /* unlikely req duration will exceed 2**32 milliseconds */ - diff = ktime_ms_delta(now_ts, ts0); - return (diff > (s64)U32_MAX) ? 3999999999U : (u32)diff; -} - -/* Return of U32_MAX means srp is inactive state */ static u32 sg_get_dur(struct sg_request *srp, const enum sg_rq_state *sr_stp, bool *is_durp) { bool is_dur = false; - u32 res = U32_MAX; + s64 dur_ns; + ktime_t start_dur = READ_ONCE(srp->start_dur); + if (ktime_to_ns(start_dur) <= 0) { + is_dur = true; + dur_ns = 0; + goto fini; + } switch (sr_stp ? *sr_stp : atomic_read(&srp->rq_st)) { - case SG_RS_INFLIGHT: case SG_RS_BUSY: - res = sg_calc_rq_dur(srp); + if (test_bit(SG_FRQ_ISSUED, srp->frq_bm)) { + dur_ns = ktime_to_ns(start_dur); + is_dur = true; + break; + } + dur_ns = 1; + break; + case SG_RS_INFLIGHT: + dur_ns = ktime_sub(ktime_get_boottime(), start_dur); break; case SG_RS_AWAIT_RCV: case SG_RS_INACTIVE: - res = srp->duration; + dur_ns = ktime_to_ns(start_dur); is_dur = true; /* completion has occurred, timing finished */ break; - default: - break; } +fini: if (is_durp) *is_durp = is_dur; - return res; + return ktime_to_ms(ns_to_ktime(dur_ns)); } static void @@ -1678,8 +1729,6 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, xa_lock_irqsave(&sfp->srp_arr, iflags); rip->duration = sg_get_dur(srp, NULL, NULL); - if (rip->duration == U32_MAX) - rip->duration = 0; rip->orphan = test_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm); rip->sg_io_owned = test_bit(SG_FRQ_SYNC_INVOC, srp->frq_bm); rip->problem = !!(srp->rq_result & SG_ML_RESULT_MSK); @@ -2527,6 +2576,7 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) int a_resid, slen; u32 rq_result; unsigned long iflags; + ktime_t start_tm; struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rqq); struct sg_request *srp = rqq->end_io_data; struct sg_device *sdp; @@ -2553,7 +2603,9 @@ sg_rq_end_io(struct request *rqq, blk_status_t status) SG_LOG(6, sfp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->pack_id, rq_result); - srp->duration = sg_calc_rq_dur(srp); + start_tm = READ_ONCE(srp->start_dur); + if (start_tm > 0) + WRITE_ONCE(srp->start_dur, ktime_sub(ktime_get_boottime(), start_tm)); if (unlikely((rq_result & SG_ML_RESULT_MSK) && slen > 0 && test_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm))) { u32 scsi_stat = rq_result & 0xff; @@ -2701,9 +2753,12 @@ sg_add_device_helper(struct scsi_device *scsidp) kfree(sdp); return ERR_PTR(error); } + spin_lock_init(&sdp->stats_lock); return sdp; } +static const struct attribute_group *sg_dev_groups[]; + static int sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) { @@ -2728,6 +2783,8 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) error = PTR_ERR(sdp); goto out; } + sdp->statsp = kzalloc(sizeof(*sdp->statsp), GFP_KERNEL); + /* don't worry if NULL, probably a lot of devices */ error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 1); if (error) @@ -2737,6 +2794,8 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) if (sg_sysfs_valid) { struct device *sg_class_member; + if (sdp->statsp) + sg_sysfs_class->dev_groups = sg_dev_groups; sg_class_member = device_create(sg_sysfs_class, cl_dev->parent, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), @@ -2763,6 +2822,7 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) return 0; cdev_add_err: + kfree(sdp->statsp); write_lock_irqsave(&sg_index_lock, iflags); idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, iflags); @@ -2791,6 +2851,7 @@ sg_device_destroy(struct kref *kref) */ xa_destroy(&sdp->sfp_arr); + kfree(sdp->statsp); write_lock_irqsave(&sg_index_lock, flags); idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, flags); @@ -3963,6 +4024,141 @@ sg_rq_st_str(enum sg_rq_state rq_st, bool long_str) } #endif +static ssize_t read_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->read_cnt); +} +static DEVICE_ATTR_RO(read_cnt); + +static ssize_t read_byte_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->read_byte_cnt); +} +static DEVICE_ATTR_RO(read_byte_cnt); + +static ssize_t read_ns_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->tot_read_time); +} +static DEVICE_ATTR_RO(read_ns); + +static ssize_t write_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->write_cnt); +} +static DEVICE_ATTR_RO(write_cnt); + +static ssize_t write_byte_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->write_byte_cnt); +} +static DEVICE_ATTR_RO(write_byte_cnt); + +static ssize_t write_ns_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->tot_write_time); +} +static DEVICE_ATTR_RO(write_ns); + +static ssize_t in_flight_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%d\n", atomic_read(&sp->in_flight)); +} +static DEVICE_ATTR_RO(in_flight); + +static ssize_t io_ns_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", + sp->tot_read_time + sp->tot_write_time + sp->tot_ndata_time); +} +static DEVICE_ATTR_RO(io_ns); + +static ssize_t other_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->other_cnt); +} +static DEVICE_ATTR_RO(other_cnt); + +static ssize_t resid_cnt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sg_device *sdp = dev_get_drvdata(dev); + struct sg_dev_stats *sp = sdp->statsp; + + if (!sdp || !sp) + return -EINVAL; + return sysfs_emit(buf, "%llu\n", sp->resid_cnt); +} +static DEVICE_ATTR_RO(resid_cnt); + +static struct attribute *sg_stats_attrs[] = { + &dev_attr_read_cnt.attr, + &dev_attr_read_byte_cnt.attr, + &dev_attr_read_ns.attr, + &dev_attr_write_cnt.attr, + &dev_attr_write_byte_cnt.attr, + &dev_attr_write_ns.attr, + &dev_attr_in_flight.attr, + &dev_attr_io_ns.attr, + &dev_attr_other_cnt.attr, + &dev_attr_resid_cnt.attr, + NULL, +}; + +static struct attribute_group sg_stats_group = { + .name = "stats", + .attrs = sg_stats_attrs, +}; + +static const struct attribute_group *sg_dev_groups[] = { + &sg_stats_group, + NULL, +}; + #if IS_ENABLED(CONFIG_SG_PROC_OR_DEBUG_FS) #define SG_SNAPSHOT_DEV_MAX 4 From patchwork Mon Oct 24 03:20:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016575 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D8CFDC433FE for ; Mon, 24 Oct 2022 03:26:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230430AbiJXD0X (ORCPT ); Sun, 23 Oct 2022 23:26:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59256 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230436AbiJXDYS (ORCPT ); Sun, 23 Oct 2022 23:24:18 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 9A9F67963B for ; Sun, 23 Oct 2022 20:22:06 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 0E47C2041BD; Mon, 24 Oct 2022 05:22:06 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EVAOLcW3-VQf; Mon, 24 Oct 2022 05:22:05 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 844B32041AF; Mon, 24 Oct 2022 05:22:04 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org, Tony Battersby Subject: [PATCH v25 43/44] sg: rework command completion when removed device Date: Sun, 23 Oct 2022 23:20:57 -0400 Message-Id: <20221024032058.14077-44-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org On advice from Tony Battersby, the author of mainline patch: 3455607fd7be10b449f5135c00dc306b85dc0d21 rework the logic introduced in that patch. Previously when there were no requests in SG_RS_AWAIT_RCV state, and IS_DETACHING() is true, and there was at least one request in SG_RS_INFLIGHT state then the passed output parameter *busy was set to true. Replace *busy with ERR_PTR(-ENODEV), which is returned only when SG_IS_DETACHING() and there are no SG_RS_AWAIT_RCV requests to return or SG_RS_INFLIGHT requests to wait for. To summarize, if IS_DETACHING() is true: - don't accept any new requests on that device - process all requests already in SG_RS_AWAIT_RCV state - if there are any requests in SG_RS_INFLIGHT state spin until they transition to SG_RS_AWAIT_RCV state or time out Reported-by: Tony Battersby Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 180 +++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 83 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 4bf2363d1b17..9b362583b610 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -315,8 +315,7 @@ static int sg_read_append(struct sg_request *srp, void __user *outp, static void sg_remove_sgat(struct sg_request *srp); static struct sg_fd *sg_add_sfp(struct sg_device *sdp); static void sg_remove_sfp(struct kref *); -static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int id, - bool *busy); +static struct sg_request *sg_find_srp_by_id(struct sg_fd *sfp, int id); static struct sg_request *sg_setup_req(struct sg_comm_wr_t *cwrp, int dxfr_len); static void sg_deact_request(struct sg_fd *sfp, struct sg_request *srp); @@ -457,11 +456,11 @@ sg_wait_open_event(struct sg_device *sdp, bool o_excl) * Returns 0 for ok (i.e. allow), -EPROTO if sdp is NULL, otherwise -ENXIO . */ static inline int -sg_allow_if_err_recovery(struct sg_device *sdp, bool non_block) +sg_allow_if_err_recovery(struct sg_device *sdp, bool non_block, bool ioreceive) { if (!sdp) return -EPROTO; - if (SG_IS_DETACHING(sdp)) + if (SG_IS_DETACHING(sdp) && !ioreceive) return -ENODEV; if (non_block) return 0; @@ -504,7 +503,7 @@ sg_open(struct inode *inode, struct file *filp) res = scsi_autopm_get_device(sdp->device); if (res) goto sdp_put; - res = sg_allow_if_err_recovery(sdp, non_block); + res = sg_allow_if_err_recovery(sdp, non_block, false); if (res) goto error_out; @@ -625,7 +624,7 @@ sg_write(struct file *filp, const char __user *p, size_t count, loff_t *ppos) sfp = filp->private_data; sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: write(3rd arg) count=%d\n", __func__, (int)count); - res = sg_allow_if_err_recovery(sdp, !!(filp->f_flags & O_NONBLOCK)); + res = sg_allow_if_err_recovery(sdp, !!(filp->f_flags & O_NONBLOCK), false); if (res) return res; if (count < SZ_SG_HEADER || count > SG_WRITE_COUNT_LIMIT) @@ -870,7 +869,7 @@ sg_ctl_iosubmit(struct file *filp, struct sg_fd *sfp, void __user *p) struct sg_io_v4 *h4p = (struct sg_io_v4 *)hdr_store; struct sg_device *sdp = sfp->parentdp; - res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK)); + res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK), false); if (res) return res; if (copy_from_user(hdr_store, p, SZ_SG_IO_V4)) @@ -888,7 +887,7 @@ sg_ctl_iosubmit_v3(struct file *filp, struct sg_fd *sfp, void __user *p) struct sg_io_hdr *h3p = (struct sg_io_hdr *)hdr_store; struct sg_device *sdp = sfp->parentdp; - res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK)); + res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK), false); if (unlikely(res)) return res; if (copy_from_user(h3p, p, SZ_SG_IO_HDR)) @@ -1167,10 +1166,9 @@ sg_common_write(struct sg_comm_wr_t *cwrp) * returns true (or an event like a signal (e.g. control-C) occurs). */ static inline bool -sg_get_ready_srp(struct sg_fd *sfp, struct sg_request **srpp, int pack_id, - bool *busy) +sg_get_ready_srp(struct sg_fd *sfp, struct sg_request **srpp, int pack_id) { - struct sg_request *srp = sg_find_srp_by_id(sfp, pack_id, busy); + struct sg_request *srp = sg_find_srp_by_id(sfp, pack_id); *srpp = srp; return !!srp; @@ -1367,7 +1365,6 @@ static int sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) { bool non_block = !!(filp->f_flags & O_NONBLOCK); - bool busy; int res, id; int pack_id = SG_PACK_ID_WILDCARD; u8 v4_holder[SZ_SG_IO_V4]; @@ -1375,7 +1372,7 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) struct sg_device *sdp = sfp->parentdp; struct sg_request *srp; - res = sg_allow_if_err_recovery(sdp, non_block); + res = sg_allow_if_err_recovery(sdp, non_block, true); if (res) return res; /* Get first three 32 bit integers: guard, proto+subproto */ @@ -1390,19 +1387,19 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) /* read in part of v3 or v4 header for pack_id or tag based find */ id = pack_id; try_again: - srp = sg_find_srp_by_id(sfp, id, &busy); - while (!srp) { /* nothing available so wait on packet or */ - if (unlikely(!busy && SG_IS_DETACHING(sdp))) - return -ENODEV; + srp = sg_find_srp_by_id(sfp, id); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (!srp) { /* nothing available so wait on packet or */ if (non_block) return -EAGAIN; res = wait_event_interruptible(sfp->read_wait, sg_get_ready_srp(sfp, &srp, - id, &busy)); - if (unlikely(!busy && SG_IS_DETACHING(sdp))) - return -ENODEV; - if (res) - return res; /* signal --> -ERESTARTSYS */ + id)); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (unlikely(!srp)) + return res; /* signal --> -ERESTARTSYS */ } if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { cpu_relax(); @@ -1421,7 +1418,6 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, void __user *p) static int sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) { - bool busy; bool non_block = !!(filp->f_flags & O_NONBLOCK); int res; int pack_id = SG_PACK_ID_WILDCARD; @@ -1430,7 +1426,7 @@ sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) struct sg_device *sdp = sfp->parentdp; struct sg_request *srp; - res = sg_allow_if_err_recovery(sdp, non_block); + res = sg_allow_if_err_recovery(sdp, non_block, true); if (unlikely(res)) return res; /* Get first three 32 bit integers: guard, proto+subproto */ @@ -1446,17 +1442,19 @@ sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p) if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm)) pack_id = h3p->pack_id; try_again: - srp = sg_find_srp_by_id(sfp, pack_id, &busy); - while (!srp) { /* nothing available so wait on packet or */ - if (unlikely(!busy && SG_IS_DETACHING(sdp))) - return -ENODEV; + srp = sg_find_srp_by_id(sfp, pack_id); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (!srp) { /* nothing available so wait on packet or */ if (non_block) return -EAGAIN; res = wait_event_interruptible (sfp->read_wait, - sg_get_ready_srp(sfp, &srp, pack_id, &busy)); - if (unlikely(res)) - return res; /* signal --> -ERESTARTSYS */ + sg_get_ready_srp(sfp, &srp, pack_id)); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (unlikely(!srp)) + return res; /* signal --> -ERESTARTSYS */ } if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { cpu_relax(); @@ -1587,10 +1585,8 @@ static ssize_t sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { bool non_block = !!(filp->f_flags & O_NONBLOCK); - bool busy; int ret; int req_pack_id = -1; - struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; @@ -1603,7 +1599,6 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) return ret; sfp = filp->private_data; - sdp = sfp->parentdp; SG_LOG(3, sfp, "%s: read() count=%d\n", __func__, (int)count); if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm)) { @@ -1613,20 +1608,19 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) } try_again: - srp = sg_find_srp_by_id(sfp, req_pack_id, &busy); - while (!srp) { /* now wait on packet to arrive */ - if (unlikely(!busy && SG_IS_DETACHING(sdp))) - return -ENODEV; + srp = sg_find_srp_by_id(sfp, req_pack_id); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (!srp) { /* now wait on packet to arrive */ if (non_block) /* O_NONBLOCK or v3::flags & SGV4_FLAG_IMMED */ return -EAGAIN; ret = wait_event_interruptible(sfp->read_wait, - ((srp = sg_find_srp_by_id(sfp, req_pack_id, &busy)) || - (!busy && SG_IS_DETACHING(sdp)))); - if (unlikely(SG_IS_DETACHING(sdp))) - return -ENODEV; - if (ret) /* -ERESTARTSYS as signal hit process */ - return ret; - /* otherwise srp should be valid */ + sg_get_ready_srp(sfp, &srp, + req_pack_id)); + if (IS_ERR(srp)) + return PTR_ERR(srp); + if (unlikely(!srp)) + return ret; /* signal --> -ERESTARTSYS */ } if (test_and_set_bit(SG_FRQ_RECEIVING, srp->frq_bm)) { cpu_relax(); @@ -1740,10 +1734,9 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, } static inline bool -sg_rq_landed(struct sg_device *sdp, struct sg_request *srp) +sg_rq_landed(struct sg_request *srp) { - return atomic_read_acquire(&srp->rq_st) != SG_RS_INFLIGHT || - unlikely(SG_IS_DETACHING(sdp)); + return atomic_read_acquire(&srp->rq_st) != SG_RS_INFLIGHT; } /* @@ -1770,7 +1763,7 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, SG_LOG(3, sfp, "%s: about to wait_event...()\n", __func__); /* usually will be woken up by sg_rq_end_io() callback */ res = wait_event_interruptible(sfp->read_wait, - sg_rq_landed(sdp, srp)); + sg_rq_landed(srp)); if (unlikely(res)) { /* -ERESTARTSYS because signal hit thread */ set_bit(SG_FRQ_IS_ORPHAN, srp->frq_bm); /* orphans harvested when sfp->keep_orphan is false */ @@ -1780,11 +1773,6 @@ sg_wait_event_srp(struct file *filp, struct sg_fd *sfp, void __user *p, return res; } skip_wait: - if (unlikely(SG_IS_DETACHING(sdp))) { - sg_rq_chg_state_force(srp, SG_RS_INACTIVE); - atomic_inc(&sfp->inactives); - return -ENODEV; - } sr_st = atomic_read(&srp->rq_st); if (unlikely(sr_st != SG_RS_AWAIT_RCV)) return -EPROTO; /* Logic error */ @@ -1818,7 +1806,7 @@ sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, SG_LOG(3, sfp, "%s: SG_IO%s\n", __func__, ((filp->f_flags & O_NONBLOCK) ? " O_NONBLOCK ignored" : "")); - res = sg_allow_if_err_recovery(sdp, false); + res = sg_allow_if_err_recovery(sdp, false, false); if (res) return res; if (get_sg_io_hdr(h3p, buf)) @@ -2007,8 +1995,6 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, SG_LOG(6, sfp, "%s: cmd=0x%x, O_NONBLOCK=%d\n", __func__, cmd_in, !!(filp->f_flags & O_NONBLOCK)); - if (unlikely(SG_IS_DETACHING(sdp))) - return -ENODEV; sdev = sdp->device; switch (cmd_in) { @@ -2027,6 +2013,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, SG_LOG(3, sfp, "%s: SG_IORECEIVE_V3\n", __func__); return sg_ctl_ioreceive_v3(filp, sfp, p); case SG_GET_SCSI_ID: + if (unlikely(SG_IS_DETACHING(sdp))) + return -ENODEV; return sg_ctl_scsi_id(sdev, sfp, p); case SG_SET_FORCE_PACK_ID: SG_LOG(3, sfp, "%s: SG_SET_FORCE_PACK_ID\n", __func__); @@ -2158,6 +2146,8 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return put_user(sdev->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: SG_LOG(3, sfp, "%s: SCSI_IOCTL_SEND_COMMAND\n", __func__); + if (unlikely(SG_IS_DETACHING(sdp))) + return -ENODEV; return scsi_ioctl(sdp->device, filp->f_mode, cmd_in, p); case SG_SET_DEBUG: SG_LOG(3, sfp, "%s: SG_SET_DEBUG\n", __func__); @@ -2207,7 +2197,7 @@ sg_ioctl_common(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp, return -EPERM; /* don't know, so take safer approach */ break; } - result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY); + result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY, false); if (unlikely(result)) return result; return -ENOIOCTLCMD; @@ -2422,7 +2412,6 @@ sg_vma_fault(struct vm_fault *vmf) struct page *page; struct sg_scatter_hold *rsv_schp; struct sg_request *srp; - struct sg_device *sdp; struct sg_fd *sfp; const char *nbp = "==NULL, bad"; @@ -2435,11 +2424,6 @@ sg_vma_fault(struct vm_fault *vmf) pr_warn("%s: sfp%s\n", __func__, nbp); goto out_err; } - sdp = sfp->parentdp; - if (sdp && unlikely(SG_IS_DETACHING(sdp))) { - SG_LOG(1, sfp, "%s: device detaching\n", __func__); - goto out_err; - } srp = sfp->rsv_srp; if (!srp) { SG_LOG(1, sfp, "%s: srp%s\n", __func__, nbp); @@ -3237,8 +3221,17 @@ sg_finish_scsi_blk_rq(struct sg_request *srp) SG_LOG(4, sfp, "%s: srp=0x%pK%s\n", __func__, srp, (srp->parentfp->rsv_srp == srp) ? " rsv" : ""); if (test_and_clear_bit(SG_FRQ_COUNT_ACTIVE, srp->frq_bm)) { - if (atomic_dec_and_test(&sfp->submitted)) + if (atomic_dec_and_test(&sfp->submitted)) { clear_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm); + + /* + * If the device is detaching, wakeup any ioreceives in + * case we just finished the last response, which would + * leave nothing for them to return other than -ENODEV. + */ + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) + wake_up_interruptible_all(&sfp->read_wait); + } atomic_dec(&sfp->waiting); } @@ -3410,15 +3403,14 @@ sg_read_append(struct sg_request *srp, void __user *outp, int num_xfer) * If there are multiple requests outstanding, the speed of this function is * important. SG_PACK_ID_WILDCARD is -1 and that case is typically the fast * path. This function is only used in the non-blocking cases. Returns pointer - * to (first) matching sg_request or NULL. If found, sg_request state is moved - * from SG_RS_AWAIT_RCV to SG_RS_BUSY. - * The 'busy' pointer is only set to true when no requests are found in the - * SG_RS_AWAIT_RCV state and SG_IS_DETACHING() is true and there is at least - * one request in SG_RS_INFLIGHT state; in all other cases *busy is set false. + * to (first) matching sg_request, or NULL if the caller should wait for + * request completion, or an ERR_PTR if the caller should return an error + * without waiting for request completion. If a request is found, sg_request + * state is moved from SG_RS_AWAIT_RCV to SG_RS_BUSY. */ static struct sg_request * -sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) +sg_find_srp_by_id(struct sg_fd *sfp, int pack_id) { __maybe_unused bool is_bad_st = false; __maybe_unused enum sg_rq_state bad_sr_st = SG_RS_INACTIVE; @@ -3432,13 +3424,12 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) struct sg_request *srp = NULL; struct xarray *xafp = &sfp->srp_arr; - *busy = false; if (test_bit(SG_FFD_POLLED_SEEN, sfp->ffd_bm)) sg_sfp_bio_poll(sfp, 0); /* LLD may have some ready to push up */ if (num_waiting < 1) { num_waiting = atomic_read_acquire(&sfp->waiting); if (num_waiting < 1) - return NULL; + goto check_detach; } s_idx = (l_await_idx < 0) ? 0 : l_await_idx; @@ -3502,16 +3493,6 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) } } /* here if one of above loops does _not_ find a match */ - if (unlikely(SG_IS_DETACHING(sfp->parentdp))) { - xa_for_each(xafp, idx, srp) { - if (!srp) - continue; - if (atomic_read(&srp->rq_st) == SG_RS_INFLIGHT) { - *busy = true; - return NULL; - } - } - } if (IS_ENABLED(CONFIG_SCSI_PROC_FS)) { if (search_for_1) { __maybe_unused const char *cptp = "pack_id="; @@ -3525,6 +3506,39 @@ sg_find_srp_by_id(struct sg_fd *sfp, int pack_id, bool *busy) __func__, cptp, pack_id); } } +check_detach: + if (unlikely(SG_IS_DETACHING(sfp->parentdp))) { + /* + * The device is being removed, but we still want to allow + * inflight commands to complete. Return NULL when there are + * still inflight commands that will generate responses to + * receive. Return ERR_PTR(-ENODEV) when there are no commands + * left. + */ + if (atomic_read(&sfp->submitted) == 0) + return ERR_PTR(-ENODEV); + if (search_for_1) { + xa_for_each(xafp, idx, srp) { + if (srp && pack_id == srp->pack_id) { + switch (atomic_read(&srp->rq_st)) { + case SG_RS_INFLIGHT: + case SG_RS_AWAIT_RCV: + /* + * Wait to return this + * response. + */ + return NULL; + + default: + break; + } + } + } + /* No command matching pack_id. */ + return ERR_PTR(-ENODEV); + } + /* else return NULL below */ + } return NULL; good: SG_LOG(5, sfp, "%s: %s%d found [srp=0x%pK]\n", __func__, "pack_id=", From patchwork Mon Oct 24 03:20:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 13016576 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D436CFA373D for ; Mon, 24 Oct 2022 03:26:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230450AbiJXD0Y (ORCPT ); Sun, 23 Oct 2022 23:26:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38298 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230453AbiJXDYU (ORCPT ); Sun, 23 Oct 2022 23:24:20 -0400 Received: from smtp.infotech.no (smtp.infotech.no [82.134.31.41]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 72CCE796AD for ; Sun, 23 Oct 2022 20:22:08 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 8980F2041C0; Mon, 24 Oct 2022 05:22:07 +0200 (CEST) X-Virus-Scanned: by amavisd-new-2.6.6 (20110518) (Debian) at infotech.no Received: from smtp.infotech.no ([127.0.0.1]) by localhost (smtp.infotech.no [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 10Ex5dta122O; Mon, 24 Oct 2022 05:22:07 +0200 (CEST) Received: from treten.bingwo.ca (unknown [10.16.20.11]) by smtp.infotech.no (Postfix) with ESMTPA id 4536D2041AF; Mon, 24 Oct 2022 05:22:06 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, hare@suse.de, bvanassche@acm.org Subject: [PATCH v25 44/44] sg: bump version to 4.0.14 Date: Sun, 23 Oct 2022 23:20:58 -0400 Message-Id: <20221024032058.14077-45-dgilbert@interlog.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221024032058.14077-1-dgilbert@interlog.com> References: <20221024032058.14077-1-dgilbert@interlog.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org Now that the sg version 4 interface is supported: - with ioctl(SG_IO) for synchronous/blocking use - with ioctl(SG_IOSUBMIT) and ioctl(SG_IORECEIVE) for async/non-blocking use Plus new ioctl(SG_IOSUBMIT_V3) and ioctl(SG_IORECEIVE_V3) potentially replace write() and read() for the sg version 3 interface. Bump major driver version number from 3 to 4. The main new feature is the removal of the fixed 16 element array of requests per file descriptor. It is replaced by a xarray (eXtensible array) in their parent which is a sg_fd object (i.e. a file descriptor). The sg_request objects are not freed until the owning file descriptor is closed; instead these objects are re-used when multiple commands are sent to the same file descriptor. Reviewed-by: Hannes Reinecke Signed-off-by: Douglas Gilbert --- drivers/scsi/sg.c | 11 ++++++----- include/uapi/scsi/sg.h | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9b362583b610..421aa678e9a6 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -7,13 +7,14 @@ * * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard - * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2019 Douglas Gilbert + * Version 2, 3 and 4 extensions to driver: + * Copyright (C) 1998 - 2022 Douglas Gilbert + * */ -static int sg_version_num = 30901; /* [x]xyyzz where [x] empty when x=0 */ -#define SG_VERSION_STR "3.9.01" /* [x]x.[y]y.zz */ -static char *sg_version_date = "20190606"; +static int sg_version_num = 40014; /* [x]xyyzz where [x] empty when x=0 */ +#define SG_VERSION_STR "4.0.14" /* [x]x.[y]y.zz */ +static char *sg_version_date = "20221016"; #include diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 2e1e0cf5d686..b9c2d9783347 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -12,10 +12,10 @@ * Copyright (C) 1992 Lawrence Foard * * Later extensions (versions 2, 3 and 4) to driver: - * Copyright (C) 1998 - 2018 Douglas Gilbert + * Copyright (C) 1998 - 2022 Douglas Gilbert * - * Version 4.0.11 (20190502) - * This version is for Linux 4 and 5 series kernels. + * Version 4.0.14 (20221009) + * This version is for Linux 5 and 6 series kernels. * * Documentation * =============