From patchwork Fri Oct 26 11:48:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657285 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 24EC014E2 for ; Fri, 26 Oct 2018 11:48:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 128F32BE93 for ; Fri, 26 Oct 2018 11:48:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 06B0D2BEA1; Fri, 26 Oct 2018 11:48:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B315B2BE93 for ; Fri, 26 Oct 2018 11:48:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727566AbeJZUZ2 (ORCPT ); Fri, 26 Oct 2018 16:25:28 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56687 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727498AbeJZUZ2 (ORCPT ); Fri, 26 Oct 2018 16:25:28 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id BB7D1204258; Fri, 26 Oct 2018 13:48: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 tG+ukcRjAT42; Fri, 26 Oct 2018 13:48:32 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id E9BF720423A; Fri, 26 Oct 2018 13:48:31 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 1/8] sg: types and naming cleanup Date: Fri, 26 Oct 2018 12:48:23 +0100 Message-Id: <20181026114830.13506-2-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Remove typedefs and use better type names like bool and u8 where appropriate. Rename some variables and functions for clarity. Adjust formatting (e.g. function definitions) to be more consistent across the driver. Signed-off-by: Douglas Gilbert --- The intention is to move to sg_version_num 40001 when a second patchset implementing SG_IOSUBMIT and friends is applied. In the meantime, move from the latest version number in the kernel (30536) to 30901 to indicate a significant change but not yet implementing the sg v4 interface. drivers/scsi/sg.c | 827 ++++++++++++++++++++++++++-------------------- 1 file changed, 467 insertions(+), 360 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c6ad00703c5b..78a35e63d177 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -7,7 +7,7 @@ * 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 - 2018 Douglas Gilbert * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,16 +16,9 @@ * */ -static int sg_version_num = 30536; /* 2 digits for each component */ -#define SG_VERSION_STR "3.5.36" +static int sg_version_num = 30901; /* 2 digits for each component */ +#define SG_VERSION_STR "3.9.01" -/* - * 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 @@ -52,6 +45,8 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include /* for sg_check_file_access() */ +#include +#include #include "scsi.h" #include @@ -64,7 +59,7 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include -static char *sg_version_date = "20140603"; +static char *sg_version_date = "20181019"; static int sg_proc_init(void); #endif @@ -73,7 +68,8 @@ static int sg_proc_init(void); #define SG_MAX_DEVS 32768 -/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type +/* + * 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 */ @@ -100,44 +96,54 @@ 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 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; +struct sg_v4_hold { /* parts of sg_io_v4 object needed in async usage */ + __user u8 *usr_ptr; /* derived from sg_io_v4::usr_ptr */ + __user u8 *sbp; /* derived from sg_io_v4::response */ + u16 cmd_len; /* truncated of sg_io_v4::request_len */ + u16 max_sb_len; /* truncated of sg_io_v4::max_response_len */ + u32 flags; /* copy of sg_io_v4::flags */ +}; + +struct sg_scatter_hold { /* holding area for scsi scatter gather info */ + struct page **pages; /* num_sgat element array of struct page* */ + int page_order; /* byte_len = (page_size * (2**page_order)) */ + int dlen; /* Byte length of data buffer */ + unsigned short num_sgat;/* actual number of scatter-gather segments */ + bool dio_in_use; /* false->indirect IO (or mmap), true->dio */ + 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 */ - sg_io_hdr_t header; /* scsi command+info, see */ - unsigned char 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 */ + struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ + union { + struct sg_io_hdr header; /* see */ + struct sg_io_v4 hdr_v4; /* see */ + }; + u8 sense_b[SCSI_SENSE_BUFFERSIZE]; + bool hdr_v4_active; /* selector for anonymous union above */ + bool res_used; /* true -> use reserve buffer, false -> don't */ + bool orphan; /* true -> drop on sight, false -> normal */ + bool sg_io_owned; /* true -> 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; -} 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 */ @@ -145,21 +151,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; /* one held 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 */ - 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() */ - 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 sg_request req_arr[SG_MAX_QUEUE];/* used as singly-linked list */ + bool force_packid; /* true -> pack_id input to read() */ + bool cmd_q; /* true -> allow command queuing, false -> don't */ + u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ + bool keep_orphan;/* false -> drop (def), true -> keep for read() */ + bool mmap_called; /* false -> mmap() never called on this fd */ + bool res_in_use; /* true -> '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() */ @@ -174,37 +180,41 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ struct gendisk *disk; struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg] */ struct kref d_ref; -} Sg_device; +}; /* tasklet or soft irq callback */ static void sg_rq_end_io(struct request *rq, blk_status_t status); -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); -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_read(struct sg_fd *sfp, char __user *buf, size_t count, + struct sg_request *srp); +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); -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_pack_id(struct sg_fd *sfp, int pack_id); +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) -#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_IO_HDR sizeof(struct sg_io_hdr) +/* #define SZ_SG_IOVEC sizeof(struct sg_iovec) synonym for 'struct iovec' */ +#define SZ_SG_REQ_INFO sizeof(struct sg_req_info) #define sg_printk(prefix, sdp, fmt, a...) \ sdev_prefix_printk(prefix, (sdp)->device, \ @@ -221,8 +231,13 @@ static void sg_device_destroy(struct kref *kref); * * This function provides protection for the legacy API by restricting the * calling context. + * + * N.B. In this driver EACCES is used when the caller does not have sufficient + * privilege (e.g. not the root user) while EPERM indicates what has been + * requested cannot be done, even if the root user is the caller. */ -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", @@ -237,7 +252,8 @@ 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) +static int +sg_allow_access(struct file *filp, u8 *cmd) { struct sg_fd *sfp = filp->private_data; @@ -248,7 +264,7 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) } static int -open_wait(Sg_device *sdp, int flags) +open_wait(struct sg_device *sdp, int flags) { int retval = 0; @@ -287,17 +303,17 @@ open_wait(Sg_device *sdp, int flags) static int sg_open(struct inode *inode, struct file *filp) { - int dev = iminor(inode); + int min_dev = iminor(inode); int 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))) 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); @@ -353,8 +369,8 @@ sg_open(struct inode *inode, struct file *filp) sdp->sg_tablesize = queue_max_segments(q); } sfp = sg_add_sfp(sdp); - if (IS_ERR(sfp)) { - retval = PTR_ERR(sfp); + if (IS_ERR_OR_NULL(sfp)) { + retval = IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; goto out_undo; } @@ -381,17 +397,26 @@ 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. Returns 0 on success, else a negated errno value. + */ 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))) - return -ENXIO; + sfp = filp->private_data; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } + sdp = sfp->parentdp; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); @@ -413,12 +438,12 @@ sg_release(struct inode *inode, struct file *filp) 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; - sg_io_hdr_t *hp; - struct sg_header *old_hdr = NULL; + struct sg_io_hdr *hp; + struct sg_header *ohdr = NULL; int retval = 0; /* @@ -429,24 +454,32 @@ 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))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_read: count=%d\n", (int) count)); + sfp = filp->private_data; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } + sdp = sfp->parentdp; + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "%s: count=%d\n", + __func__, (int)count)); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; if (sfp->force_packid && (count >= SZ_SG_HEADER)) { - old_hdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL); - if (!old_hdr) + ohdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL); + if (!ohdr) return -ENOMEM; - if (__copy_from_user(old_hdr, buf, SZ_SG_HEADER)) { + /* even though this is a read(), this code is cheating */ + if (__copy_from_user(ohdr, buf, SZ_SG_HEADER)) { retval = -EFAULT; goto free_old_hdr; } - if (old_hdr->reply_len < 0) { + if (ohdr->reply_len < 0) { if (count >= SZ_SG_IO_HDR) { - sg_io_hdr_t *new_hdr; + struct sg_io_hdr *new_hdr; + new_hdr = kmalloc(SZ_SG_IO_HDR, GFP_KERNEL); if (!new_hdr) { retval = -ENOMEM; @@ -462,10 +495,10 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } } } else - req_pack_id = old_hdr->pack_id; + req_pack_id = ohdr->pack_id; } - srp = sg_get_rq_mark(sfp, req_pack_id); - if (!srp) { /* now wait on packet to arrive */ + srp = sg_get_rq_pack_id(sfp, req_pack_id); + if (!srp) { /* nothing available so wait on packet to arrive */ if (atomic_read(&sdp->detaching)) { retval = -ENODEV; goto free_old_hdr; @@ -476,7 +509,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } retval = wait_event_interruptible(sfp->read_wait, (atomic_read(&sdp->detaching) || - (srp = sg_get_rq_mark(sfp, req_pack_id)))); + (srp = sg_get_rq_pack_id(sfp, req_pack_id)))); if (atomic_read(&sdp->detaching)) { retval = -ENODEV; goto free_old_hdr; @@ -492,64 +525,64 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } hp = &srp->header; - if (old_hdr == NULL) { - old_hdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL); - if (! old_hdr) { + if (!ohdr) { + ohdr = kmalloc(SZ_SG_HEADER, GFP_KERNEL); + if (!ohdr) { retval = -ENOMEM; goto free_old_hdr; } } - memset(old_hdr, 0, SZ_SG_HEADER); - 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; + memset(ohdr, 0, SZ_SG_HEADER); + ohdr->reply_len = (int)hp->timeout; + ohdr->pack_len = ohdr->reply_len; /* old, strange behaviour */ + ohdr->pack_id = hp->pack_id; + ohdr->twelve_byte = (srp->data.cmd_opcode >= 0xc0 && + hp->cmd_len == 12); + ohdr->target_status = hp->masked_status; + ohdr->host_status = hp->host_status; + ohdr->driver_status = hp->driver_status; if ((CHECK_CONDITION & hp->masked_status) || (DRIVER_SENSE & hp->driver_status)) - memcpy(old_hdr->sense_buffer, srp->sense_b, - sizeof (old_hdr->sense_buffer)); + memcpy(ohdr->sense_buffer, srp->sense_b, + sizeof(ohdr->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; + ohdr->result = 0; break; case DID_NO_CONNECT: case DID_BUS_BUSY: case DID_TIME_OUT: - old_hdr->result = EBUSY; + ohdr->result = EBUSY; break; case DID_BAD_TARGET: case DID_ABORT: case DID_PARITY: case DID_RESET: case DID_BAD_INTR: - old_hdr->result = EIO; + ohdr->result = EIO; break; case DID_ERROR: - old_hdr->result = (srp->sense_b[0] == 0 && - hp->masked_status == GOOD) ? 0 : EIO; + ohdr->result = (srp->sense_b[0] == 0 && + hp->masked_status == GOOD) ? 0 : EIO; break; default: - old_hdr->result = EIO; + ohdr->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)) { + if (__copy_to_user(buf, ohdr, 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 > ohdr->reply_len) + count = ohdr->reply_len; if (count > SZ_SG_HEADER) { if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) { retval = -EFAULT; @@ -557,21 +590,22 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } } } else - count = (old_hdr->result == 0) ? 0 : -EIO; + count = (ohdr->result == 0) ? 0 : -EIO; sg_finish_rem_req(srp); sg_remove_request(sfp, srp); retval = count; free_old_hdr: - kfree(old_hdr); + kfree(ohdr); return retval; } 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; int len; + struct sg_io_hdr *hp = &srp->header; if (count < SZ_SG_IO_HDR) { err = -EINVAL; @@ -583,7 +617,8 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) (DRIVER_SENSE & hp->driver_status)) { 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 */ + /* Additional sense length field */ + len = 8 + (int)srp->sense_b[7]; len = (len > sb_len) ? sb_len : len; if (copy_to_user(hp->sbp, srp->sense_b, len)) { err = -EFAULT; @@ -609,23 +644,29 @@ 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; - struct sg_header old_hdr; - sg_io_hdr_t *hp; - unsigned char cmnd[SG_MAX_CDB_SIZE]; + u8 opcode; + struct sg_device *sdp; + struct sg_fd *sfp; + struct sg_request *srp; + struct sg_io_hdr *hp; + u8 cmnd[SG_MAX_CDB_SIZE]; int retval; + struct sg_header ohdr; 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_write: count=%d\n", (int) count)); + sfp = filp->private_data; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } + sdp = sfp->parentdp; + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "%s: count=%d\n", + __func__, (int)count)); if (atomic_read(&sdp->detaching)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || @@ -633,17 +674,17 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -ENXIO; if (!access_ok(VERIFY_READ, buf, count)) - return -EFAULT; /* protects following copy_from_user()s + get_user()s */ + return -EFAULT; if (count < SZ_SG_HEADER) return -EIO; - if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) + if (__copy_from_user(&ohdr, buf, SZ_SG_HEADER)) return -EFAULT; blocking = !(filp->f_flags & O_NONBLOCK); - if (old_hdr.reply_len < 0) + if (ohdr.reply_len < 0) return sg_new_write(sfp, filp, buf, count, blocking, 0, 0, NULL); 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 */ if (!(srp = sg_add_request(sfp))) { SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp, @@ -655,34 +696,35 @@ sg_write(struct file *filp, const char __user *buf, 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); /* based on SCSI command group */ - if ((opcode >= 0xc0) && old_hdr.twelve_byte) + cmd_size = COMMAND_SIZE(opcode);/* 'old; SCSI command group */ + if (opcode >= 0xc0 && ohdr.twelve_byte) 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. */ + "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (int)opcode, cmd_size)); input_size = count - cmd_size; - mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; + mxsize = (input_size > ohdr.reply_len) ? input_size : ohdr.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. */ + return -EIO; /* Insufficient bytes passed for this command. */ } hp = &srp->header; - hp->interface_id = '\0'; /* indicator of old interface tunnelled */ - hp->cmd_len = (unsigned char) cmd_size; + hp->interface_id = '\0'; /* indicator of old interface tunnelled */ + hp->cmd_len = (u8)cmd_size; hp->iovec_count = 0; hp->mx_sb_len = 0; if (input_size > 0) - hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ? + hp->dxfer_direction = (ohdr.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_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)) @@ -690,9 +732,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) else hp->dxferp = NULL; hp->sbp = NULL; - hp->timeout = old_hdr.reply_len; /* structure abuse ... */ + hp->timeout = ohdr.reply_len; /* structure abuse ... */ hp->flags = input_size; /* structure abuse ... */ - hp->pack_id = old_hdr.pack_id; + hp->pack_id = ohdr.pack_id; hp->usr_ptr = NULL; if (__copy_from_user(cmnd, buf, cmd_size)) return -EFAULT; @@ -707,8 +749,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) "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, - input_size, (unsigned int) cmnd[0], + ohdr.reply_len - (int)SZ_SG_HEADER, + input_size, (unsigned int)cmnd[0], current->comm); } k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); @@ -716,14 +758,14 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) } 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; - sg_io_hdr_t *hp; - unsigned char cmnd[SG_MAX_CDB_SIZE]; + struct sg_request *srp; + struct sg_io_hdr *hp; + u8 cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -732,7 +774,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; /* protects following copy_from_user()s + get_user()s */ - sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ + sfp->cmd_q = true; /* 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")); @@ -749,7 +791,7 @@ sg_new_write(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.dlen) { sg_remove_request(sfp, srp); return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ } @@ -764,7 +806,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, } 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))) { + if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof(cmnd))) { sg_remove_request(sfp, srp); return -EMSGSIZE; } @@ -789,12 +831,13 @@ 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; - sg_io_hdr_t *hp = &srp->header; + bool at_head; + int k; + struct sg_device *sdp = sfp->parentdp; + struct sg_io_hdr *hp = &srp->header; srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ hp->status = 0; @@ -806,7 +849,7 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, 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)); + (int)cmnd[0], (int)hp->cmd_len)); if (hp->dxfer_len >= SZ_256M) return -EINVAL; @@ -832,20 +875,19 @@ sg_common_write(Sg_fd * sfp, 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; - else - at_head = 1; + /* at tail if v3 or later interface and tail flag set */ + at_head = !(hp->interface_id != '\0' && + (SG_FLAG_Q_AT_TAIL & hp->flags)); srp->rq->timeout = timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, - srp->rq, at_head, sg_rq_end_io); + srp->rq, (int)at_head, sg_rq_end_io); return 0; } -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; @@ -856,7 +898,8 @@ static int srp_done(Sg_fd *sfp, Sg_request *srp) return ret; } -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); @@ -866,9 +909,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; @@ -904,16 +947,20 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) void __user *p = (void __user *)arg; int __user *ip = p; int result, val, read_only; - Sg_device *sdp; - Sg_fd *sfp; - Sg_request *srp; + struct sg_device *sdp; + struct sg_fd *sfp; + struct sg_request *srp; unsigned long iflags; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + if (!sfp) + return -ENXIO; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_ioctl: cmd=0x%x\n", (int) cmd_in)); + "%s: cmd=0x%x\n", __func__, (int)cmd_in)); read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); switch (cmd_in) { @@ -939,7 +986,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; } - srp->orphan = 1; + srp->orphan = true; write_unlock_irq(&sfp->rq_list_lock); return result; /* -ERESTARTSYS because signal hit process */ case SG_SET_TIMEOUT: @@ -966,25 +1013,25 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) */ return 0; case SG_GET_LOW_DMA: - return put_user((int) sdp->device->host->unchecked_isa_dma, ip); + return put_user((int)sdp->device->host->unchecked_isa_dma, ip); case SG_GET_SCSI_ID: - if (!access_ok(VERIFY_WRITE, p, sizeof (sg_scsi_id_t))) + if (!access_ok(VERIFY_WRITE, p, sizeof(struct sg_scsi_id))) return -EFAULT; else { - sg_scsi_id_t __user *sg_idp = p; + struct sg_scsi_id __user *sg_idp = p; if (atomic_read(&sdp->detaching)) return -ENODEV; - __put_user((int) sdp->device->host->host_no, + __put_user((int)sdp->device->host->host_no, &sg_idp->host_no); - __put_user((int) sdp->device->channel, + __put_user((int)sdp->device->channel, &sg_idp->channel); - __put_user((int) sdp->device->id, &sg_idp->scsi_id); - __put_user((int) sdp->device->lun, &sg_idp->lun); - __put_user((int) sdp->device->type, &sg_idp->scsi_type); - __put_user((short) sdp->device->host->cmd_per_lun, + __put_user((int)sdp->device->id, &sg_idp->scsi_id); + __put_user((int)sdp->device->lun, &sg_idp->lun); + __put_user((int)sdp->device->type, &sg_idp->scsi_type); + __put_user((short)sdp->device->host->cmd_per_lun, &sg_idp->h_cmd_per_lun); - __put_user((short) sdp->device->queue_depth, + __put_user((short)sdp->device->queue_depth, &sg_idp->d_queue_depth); __put_user(0, &sg_idp->unused[0]); __put_user(0, &sg_idp->unused[1]); @@ -994,10 +1041,10 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) result = get_user(val, ip); if (result) return result; - sfp->force_packid = val ? 1 : 0; + sfp->force_packid = !!val; return 0; case SG_GET_PACK_ID: - if (!access_ok(VERIFY_WRITE, ip, sizeof (int))) + if (!access_ok(VERIFY_WRITE, ip, sizeof(int))) return -EFAULT; read_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(srp, &sfp->rq_list, entry) { @@ -1031,7 +1078,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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.dlen) { if (sfp->mmap_called || sfp->res_in_use) { mutex_unlock(&sfp->f_mutex); @@ -1044,25 +1091,25 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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.dlen, max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q: result = get_user(val, ip); if (result) return result; - sfp->cmd_q = val ? 1 : 0; + sfp->cmd_q = !!val; return 0; case SG_GET_COMMAND_Q: - return put_user((int) sfp->cmd_q, ip); + return put_user((int)sfp->cmd_q, ip); case SG_SET_KEEP_ORPHAN: result = get_user(val, ip); if (result) return result; - sfp->keep_orphan = val; + sfp->keep_orphan = !!val; return 0; case SG_GET_KEEP_ORPHAN: - return put_user((int) sfp->keep_orphan, ip); + return put_user((int)sfp->keep_orphan, ip); case SG_NEXT_CMD_LEN: result = get_user(val, ip); if (result) @@ -1081,7 +1128,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!access_ok(VERIFY_WRITE, p, SZ_SG_REQ_INFO * SG_MAX_QUEUE)) return -EFAULT; else { - sg_req_info_t *rinfo; + struct sg_req_info *rinfo; rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, GFP_KERNEL); @@ -1146,13 +1193,18 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) } #ifdef CONFIG_COMPAT -static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) +static long +sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; struct scsi_device *sdev; - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + sfp = filp->private_data; + if (!sfp) + return -ENXIO; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; sdev = sdp->device; @@ -1172,9 +1224,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; @@ -1209,10 +1261,14 @@ 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; + if (!sfp) + return -ENXIO; + sdp = sfp->parentdp; + if (!sdp) return -ENXIO; SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_fasync: mode=%d\n", mode)); @@ -1224,23 +1280,26 @@ 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; - if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data))) + if (!vma) + return VM_FAULT_SIGBUS; + sfp = vma->vm_private_data; + if (!sfp) return VM_FAULT_SIGBUS; rsv_schp = &sfp->reserve; offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rsv_schp->bufflen) + if (offset >= rsv_schp->dlen) 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)); + offset, rsv_schp->num_sgat)); 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; if (offset < len) { @@ -1264,36 +1323,39 @@ 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) 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)); + (void *)vma->vm_start, (int)req_sz)); if (vma->vm_pgoff) 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->dlen) { 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; } - sfp->mmap_called = 1; + sfp->mmap_called = true; vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; @@ -1302,6 +1364,14 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } +/* + * This user context function is needed to clean up a request that has been + * interrupted (e.g. by control-C at keyboard). That leads to a request + * being an 'orphan' and will be cleared here unless the 'keep_orphan' flag + * has been set on the owning file descriptor. In that case the user is + * expected to call read() or ioctl(SG_IORECEIVE) to receive the response + * and free resources held by the interrupted request. + */ static void sg_rq_end_io_usercontext(struct work_struct *work) { @@ -1314,19 +1384,22 @@ sg_rq_end_io_usercontext(struct work_struct *work) } /* - * This function is a "bottom half" handler that is called by the mid - * level when a command is completed (or has failed). + * This function is a "bottom half" handler that is called by the mid-level + * when a command is completed (or has failed). The function is a callback + * registered in a blk_execute_rq_nowait() call at the end of the + * sg_common_write() function. For synchronous usage with ioctl(SG_IO) + * the function sg_sg_io() waits to be woken up by this callback. */ static void sg_rq_end_io(struct request *rq, blk_status_t status) { struct sg_request *srp = rq->end_io_data; struct scsi_request *req = scsi_req(rq); - Sg_device *sdp; - Sg_fd *sfp; + struct sg_device *sdp; + struct sg_fd *sfp; unsigned long iflags; unsigned int ms; - char *sense; + u8 *sense; int result, resid, done = 1; if (WARN_ON(srp->done != 0)) @@ -1435,21 +1508,18 @@ static struct class *sg_sysfs_class; static int sg_sysfs_valid = 0; -static Sg_device * +static struct sg_device * sg_alloc(struct gendisk *disk, 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(struct sg_device), GFP_KERNEL); + if (!sdp) return ERR_PTR(-ENOMEM); - } idr_preload(GFP_KERNEL); write_lock_irqsave(&sg_index_lock, iflags); @@ -1462,8 +1532,8 @@ sg_alloc(struct gendisk *disk, 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 allocation sg_device failure: %d\n", __func__, error); } goto out_unlock; @@ -1502,7 +1572,7 @@ sg_add_device(struct device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); struct gendisk *disk; - Sg_device *sdp = NULL; + struct sg_device *sdp = NULL; struct cdev * cdev = NULL; int error; unsigned long iflags; @@ -1601,9 +1671,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) @@ -1699,23 +1769,24 @@ exit_sg(void) idr_destroy(&sg_index_idr); } +/* Returns 0 if okay, otherwise negated errno value */ static int -sg_start_req(Sg_request *srp, unsigned char *cmd) +sg_start_req(struct sg_request *srp, u8 *cmd) { int res; struct request *rq; struct scsi_request *req; - Sg_fd *sfp = srp->parentfp; - sg_io_hdr_t *hp = &srp->header; - int dxfer_len = (int) hp->dxfer_len; + 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; - 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; - unsigned char *long_cmdp = NULL; + u8 *long_cmdp = NULL; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_start_req: dxfer_len=%d\n", @@ -1768,13 +1839,13 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) if (md) { mutex_lock(&sfp->f_mutex); - if (dxfer_len <= rsv_schp->bufflen && + if (dxfer_len <= rsv_schp->dlen && !sfp->res_in_use) { - sfp->res_in_use = 1; + sfp->res_in_use = true; 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) + res = -EBUSY; /* sfp->res_in_use == true */ + if (dxfer_len > rsv_schp->dlen) res = -ENOMEM; mutex_unlock(&sfp->f_mutex); return res; @@ -1789,7 +1860,7 @@ sg_start_req(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) @@ -1822,7 +1893,7 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) srp->bio = rq->bio; if (!md) { - req_schp->dio_in_use = 1; + req_schp->dio_in_use = true; hp->info |= SG_INFO_DIRECT_IO; } } @@ -1830,16 +1901,16 @@ 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", - (int) srp->res_used)); + (int)srp->res_used)); if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1857,7 +1928,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; @@ -1865,12 +1937,12 @@ sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) schp->pages = kzalloc(sg_bufflen, gfp_flags); if (!schp->pages) return -ENOMEM; - schp->sglist_len = sg_bufflen; return tablesize; /* number of scat_gath elements allocated */ } 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,12 +2004,12 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) } /* end of for loop */ schp->page_order = order; - schp->k_use_sg = k; + schp->num_sgat = k; SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, - "sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", + "%s: num_sgat=%d, rem_sz=%d\n", __func__, k, rem_sz)); - schp->bufflen = blk_size; + schp->dlen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; return 0; @@ -1952,15 +2024,15 @@ 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)); - if (schp->pages && schp->sglist_len > 0) { + "sg_remove_scat: num_sgat=%d\n", schp->num_sgat)); + if (schp->pages) { 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++) { SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, "sg_remove_scat: k=%d, pg=0x%p\n", @@ -1971,13 +2043,13 @@ sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp) kfree(schp->pages); } } - memset(schp, 0, sizeof (*schp)); + memset(schp, 0, sizeof(*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, @@ -1987,7 +2059,7 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_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_read_xfer) { if (__copy_to_user(outp, page_address(schp->pages[k]), num_read_xfer)) @@ -2008,9 +2080,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)); @@ -2026,58 +2098,56 @@ 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; + srp->res_used = true; SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_link_reserve: size=%d\n", 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->sglist_len = rsv_schp->sglist_len; + req_schp->num_sgat = k + 1; req_schp->pages = rsv_schp->pages; - req_schp->bufflen = size; + req_schp->dlen = 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) SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, "sg_link_reserve: BAD size\n")); } 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", - (int) req_schp->k_use_sg)); - req_schp->k_use_sg = 0; - req_schp->bufflen = 0; + "sg_unlink_reserve: req->num_sgat=%d\n", + (int)req_schp->num_sgat)); + req_schp->num_sgat = 0; + req_schp->dlen = 0; req_schp->pages = NULL; req_schp->page_order = 0; - req_schp->sglist_len = 0; - srp->res_used = 0; + srp->res_used = false; /* Called without mutex lock to avoid deadlock */ - sfp->res_in_use = 0; + sfp->res_in_use = false; } -static Sg_request * -sg_get_rq_mark(Sg_fd * sfp, int pack_id) +static struct sg_request * +sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id) { - Sg_request *resp; + struct sg_request *resp; unsigned long iflags; write_lock_irqsave(&sfp->rq_list_lock, iflags); @@ -2094,13 +2164,18 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) return NULL; } -/* always adds to end of list */ -static Sg_request * -sg_add_request(Sg_fd * sfp) +/* + * Adds 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). Returns a valid pointer if successful. On failure returns a + * negated errno value twisted by ERR_PTR() macro. + */ +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)) { @@ -2114,7 +2189,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(*rp)); rp->parentfp = sfp; rp->header.duration = jiffies_to_msecs(jiffies); list_add_tail(&rp->entry, &sfp->rq_list); @@ -2125,9 +2200,16 @@ sg_add_request(Sg_fd * sfp) return NULL; } -/* Return of 1 for found; 0 for not found */ +/* + * Moves a completed sg_request object to the free list and set it to + * SG_RQ_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. If the + * data length exceeds rem_sgat_thresh then the data (or sgat) is + * cleared and the request is appended to the tail of the free list. + */ 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; @@ -2144,10 +2226,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; @@ -2162,9 +2244,9 @@ sg_add_sfp(Sg_device * sdp) 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; + sfp->force_packid = !!SG_DEF_FORCE_PACK_ID; + sfp->cmd_q = !!SG_DEF_COMMAND_Q; + sfp->keep_orphan = !!SG_DEF_KEEP_ORPHAN; sfp->parentdp = sdp; write_lock_irqsave(&sdp->sfd_lock, iflags); if (atomic_read(&sdp->detaching)) { @@ -2183,38 +2265,47 @@ sg_add_sfp(Sg_device * sdp) 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)); + "%s: dlen=%d, num_sgat=%d\n", __func__, + sfp->reserve.dlen, + sfp->reserve.num_sgat)); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); return sfp; } +/* + * 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) { 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; } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (sfp->reserve.bufflen > 0) { + if (sfp->reserve.dlen > 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_remove_sfp: dlen=%d, num_sgat=%d\n", + (int)sfp->reserve.dlen, + (int)sfp->reserve.num_sgat)); sg_remove_scat(sfp, &sfp->reserve); } @@ -2268,12 +2359,17 @@ 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 * +/* + * Returns valid pointer to a sg_device object on success or a negated + * errno value on failure. Does not return NULL. + */ +static struct sg_device * sg_get_dev(int dev) { struct sg_device *sdp; @@ -2370,14 +2466,15 @@ sg_proc_init(void) return 0; } - -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); } @@ -2398,7 +2495,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); } @@ -2423,14 +2521,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; @@ -2441,7 +2541,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); @@ -2456,7 +2557,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; @@ -2464,15 +2566,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_proc_deviter *it = (struct sg_proc_deviter *)v; + struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2485,20 +2589,21 @@ static int sg_proc_seq_show_dev(struct seq_file *s, void *v) scsidp = sdp->device; seq_printf(s, "%d\t%d\t%d\t%llu\t%d\t%d\t%d\t%d\t%d\n", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, (int) scsidp->type, + scsidp->id, scsidp->lun, (int)scsidp->type, 1, - (int) scsidp->queue_depth, - (int) atomic_read(&scsidp->device_busy), - (int) scsi_device_online(scsidp)); + (int)scsidp->queue_depth, + (int)atomic_read(&scsidp->device_busy), + (int)scsi_device_online(scsidp)); } read_unlock_irqrestore(&sg_index_lock, iflags); 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_proc_deviter *it = (struct sg_proc_deviter *)v; + struct sg_device *sdp; struct scsi_device *scsidp; unsigned long iflags; @@ -2515,12 +2620,13 @@ 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; - const sg_io_hdr_t *hp; + struct sg_request *srp; + struct sg_fd *fp; + const struct sg_io_hdr *hp; const char * cp; unsigned int ms; @@ -2528,15 +2634,15 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) list_for_each_entry(fp, &sdp->sfds, sfd_siblings) { k++; read_lock(&fp->rq_list_lock); /* irqs already disabled */ - seq_printf(s, " FD(%d): timeout=%dms bufflen=%d " + seq_printf(s, " FD(%d): timeout=%dms dlen=%d " "(res)sgat=%d low_dma=%d\n", k, jiffies_to_msecs(fp->timeout), - fp->reserve.bufflen, - (int) fp->reserve.k_use_sg, - (int) sdp->device->host->unchecked_isa_dma); + fp->reserve.dlen, + (int)fp->reserve.num_sgat, + (int)sdp->device->host->unchecked_isa_dma); 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); + (int)fp->cmd_q, (int)fp->force_packid, + (int)fp->keep_orphan); list_for_each_entry(srp, &fp->rq_list, entry) { hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; @@ -2553,8 +2659,8 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) cp = " "; } seq_puts(s, cp); - blen = srp->data.bufflen; - usg = srp->data.k_use_sg; + blen = srp->data.dlen; + usg = srp->data.num_sgat; seq_puts(s, srp->done ? ((1 == srp->done) ? "rcv:" : "fin:") : "act:"); @@ -2570,7 +2676,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) (ms > hp->duration ? ms - hp->duration : 0)); } seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, - (int) srp->data.cmd_opcode); + (int)srp->data.cmd_opcode); } if (list_empty(&fp->rq_list)) seq_puts(s, " No requests active\n"); @@ -2578,10 +2684,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_proc_deviter *it = (struct sg_proc_deviter *)v; + struct sg_device *sdp; unsigned long iflags; if (it && (0 == it->index)) From patchwork Fri Oct 26 11:48:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657281 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 08F6B14BD for ; Fri, 26 Oct 2018 11:48:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC5AF2BE93 for ; Fri, 26 Oct 2018 11:48:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DEAC42BE97; Fri, 26 Oct 2018 11:48:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8DEA62BE93 for ; Fri, 26 Oct 2018 11:48:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727545AbeJZUZ0 (ORCPT ); Fri, 26 Oct 2018 16:25:26 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56677 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727333AbeJZUZ0 (ORCPT ); Fri, 26 Oct 2018 16:25:26 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 95159204255; Fri, 26 Oct 2018 13:48:36 +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 XKxkCv+4MvkX; Fri, 26 Oct 2018 13:48:34 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id 074A320423F; Fri, 26 Oct 2018 13:48:32 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 2/8] sg: introduce sg_log macro Date: Fri, 26 Oct 2018 12:48:24 +0100 Message-Id: <20181026114830.13506-3-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Introduce the SG_LOG macro to replace long-winded 'SCSI_LOG_TIMEOUT(3, sg_printk ...' debug messages. Use __func__ wherever appropriate to make the debug messages more portable. Signed-off-by: Douglas Gilbert --- Reviewers want SCSI_LOG_* style logging replaced. But with what? One suggestion is the trace subsystem but that seems data and event oriented whereas the current logging is call based with extra messages when error paths are taken. There are many debugging hours of work crafted into the current SCSI_LOG_* messages that should not be thrown out for some pretty solution without additional benefits. drivers/scsi/sg.c | 162 +++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 90 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 78a35e63d177..94e13a1d21a5 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -216,9 +216,24 @@ static void sg_device_destroy(struct kref *kref); /* #define SZ_SG_IOVEC sizeof(struct sg_iovec) synonym for 'struct iovec' */ #define SZ_SG_REQ_INFO sizeof(struct sg_req_info) -#define sg_printk(prefix, sdp, fmt, a...) \ - sdev_prefix_printk(prefix, (sdp)->device, \ - (sdp)->disk->disk_name, fmt, ##a) +/* + * 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 sdp is NULL the macro reverts to a pr_info + * and ignores CONFIG_SCSI_LOGGING and always prints to the log. + */ +#define SG_LOG(depth, sdp, fmt, a...) \ + do { \ + if (IS_ERR_OR_NULL(sdp)) { \ + pr_info("sg: sdp=NULL_or_ERR, " fmt, ##a); \ + } else { \ + SCSI_LOG_TIMEOUT(depth, sdev_prefix_printk( \ + KERN_INFO, (sdp)->device, \ + (sdp)->disk->disk_name, fmt, \ + ##a)); \ + } \ + } while (0) /* * The SCSI interfaces that use read() and write() as an asynchronous variant of @@ -316,9 +331,8 @@ sg_open(struct inode *inode, struct file *filp) 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_LOG(3, sdp, "%s: flags=0x%x; device open count prior=%d\n", + __func__, flags, sdp->open_cnt); /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ @@ -414,9 +428,10 @@ sg_release(struct inode *inode, struct file *filp) return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; } sdp = sfp->parentdp; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); if (IS_ERR_OR_NULL(sdp)) return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; + SG_LOG(3, sdp, "%s: device open count prior=%d\n", __func__, + sdp->open_cnt); mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); @@ -462,8 +477,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) sdp = sfp->parentdp; if (IS_ERR_OR_NULL(sdp)) return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "%s: count=%d\n", - __func__, (int)count)); + SG_LOG(3, sdp, "%s: read() count=%d\n", __func__, (int)count); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -663,10 +677,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; } sdp = sfp->parentdp; + SG_LOG(3, sdp, "%s: write(3rd arg) count=%d\n", __func__, (int)count); if (IS_ERR_OR_NULL(sdp)) return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "%s: count=%d\n", - __func__, (int)count)); if (atomic_read(&sdp->detaching)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || @@ -687,8 +700,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -EIO; /* minimum scsi command length is 6 bytes */ if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp, - "sg_write: queue full\n")); + SG_LOG(1, sdp, "%s: queue full\n", __func__); return -EDOM; } buf += SZ_SG_HEADER; @@ -703,9 +715,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, - "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, - (int)opcode, cmd_size)); + SG_LOG(4, sdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (unsigned int)opcode, cmd_size); input_size = count - cmd_size; mxsize = (input_size > ohdr.reply_len) ? input_size : ohdr.reply_len; mxsize -= SZ_SG_HEADER; @@ -776,8 +787,7 @@ sg_new_write(struct sg_fd *sfp, struct file *file, const char __user *buf, sfp->cmd_q = true; /* 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->parentdp, "%s: queue full\n", __func__); return -EDOM; } srp->sg_io_owned = sg_io_owned; @@ -847,17 +857,16 @@ 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->parentdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", + __func__, (int)cmnd[0], (int)hp->cmd_len); if (hp->dxfer_len >= SZ_256M) return -EINVAL; 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->parentdp, "%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 */ @@ -959,8 +968,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!sdp) return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "%s: cmd=0x%x\n", __func__, (int)cmd_in)); + SG_LOG(3, sdp, "%s: cmd=0x%x\n", __func__, (int)cmd_in); read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); switch (cmd_in) { @@ -1253,8 +1261,7 @@ 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, sdp, "%s: res=0x%x\n", __func__, (__force u32)res); return res; } @@ -1270,8 +1277,7 @@ sg_fasync(int fd, struct file *filp, int mode) sdp = sfp->parentdp; if (!sdp) return -ENXIO; - SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, - "sg_fasync: mode=%d\n", mode)); + SG_LOG(3, sdp, "%s: mode=%d\n", __func__, mode); return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1294,9 +1300,8 @@ sg_vma_fault(struct vm_fault *vmf) offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rsv_schp->dlen) 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->num_sgat)); + SG_LOG(3, sfp->parentdp, "%s: offset=%lu, scatg=%d\n", __func__, + offset, rsv_schp->num_sgat); 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++) { @@ -1335,9 +1340,8 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) if (!sfp) 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->parentdp, "%s starting, 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; @@ -1417,9 +1421,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) result = req->result; resid = req->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)); + SG_LOG(4, sdp, "%s: pack_id=%d, res=0x%x\n", __func__, + srp->header.pack_id, result); srp->header.resid = resid; ms = jiffies_to_msecs(jiffies); srp->header.duration = (ms > srp->header.duration) ? @@ -1660,8 +1663,7 @@ 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")); + SG_LOG(3, sdp, "%s\n", __func__); put_disk(sdp->disk); kfree(sdp); @@ -1683,8 +1685,7 @@ 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__)); + SG_LOG(3, sdp, "%s\n", __func__); read_lock_irqsave(&sdp->sfd_lock, iflags); list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { @@ -1788,9 +1789,7 @@ sg_start_req(struct sg_request *srp, u8 *cmd) int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; u8 *long_cmdp = NULL; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_start_req: dxfer_len=%d\n", - dxfer_len)); + SG_LOG(4, sfp->parentdp, "%s: dxfer_len=%d\n", __func__, dxfer_len); if (hp->cmd_len > BLK_MAX_CDB) { long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL); @@ -1908,9 +1907,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->parentdp, "%s: res_used=%d\n", __func__, + (int)srp->res_used); if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1956,9 +1954,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->parentdp, "%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); @@ -1998,16 +1995,14 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, } } - 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->parentdp, "%s: k=%d, num=%d, ret_sz=%d\n", + __func__, k, num, ret_sz); } /* end of for loop */ schp->page_order = order; schp->num_sgat = k; - SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, - "%s: num_sgat=%d, rem_sz=%d\n", __func__, - k, rem_sz)); + SG_LOG(5, sfp->parentdp, "%s: num_sgat=%d, rem_sz=%d\n", __func__, k, + rem_sz); schp->dlen = blk_size; if (rem_sz > 0) /* must have failed */ @@ -2026,17 +2021,15 @@ 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: num_sgat=%d\n", schp->num_sgat)); + SG_LOG(4, sfp->parentdp, "%s: num_sgat=%d\n", __func__, + schp->num_sgat); if (schp->pages) { if (!schp->dio_in_use) { int k; for (k = 0; k < schp->num_sgat && 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->parentdp, "%s: k=%d, pg=0x%p\n", + __func__, k, schp->pages[k]); __free_pages(schp->pages[k], schp->page_order); } @@ -2052,9 +2045,8 @@ 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->parentdp, "%s: num_read_xfer=%d\n", __func__, + num_read_xfer); if ((!outp) || (num_read_xfer <= 0)) return 0; @@ -2084,8 +2076,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(4, sfp->parentdp, "%s: req_size=%d\n", __func__, req_size); do { if (req_size < PAGE_SIZE) req_size = PAGE_SIZE; @@ -2105,8 +2096,7 @@ sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) int k, num, rem; srp->res_used = true; - SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, - "sg_link_reserve: size=%d\n", size)); + SG_LOG(4, sfp->parentdp, "%s: size=%d\n", __func__, size); rem = size; num = 1 << (PAGE_SHIFT + rsv_schp->page_order); @@ -2123,8 +2113,7 @@ sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) } if (k >= rsv_schp->num_sgat) - SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, - "sg_link_reserve: BAD size\n")); + SG_LOG(1, sfp->parentdp, "%s: BAD size\n", __func__); } static void @@ -2132,9 +2121,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->num_sgat=%d\n", - (int)req_schp->num_sgat)); + SG_LOG(4, srp->parentfp->parentdp, "%s: req->num_sgat=%d\n", __func__, + (int)req_schp->num_sgat); req_schp->num_sgat = 0; req_schp->dlen = 0; req_schp->pages = NULL; @@ -2256,18 +2244,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, sdp, "%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, - "%s: dlen=%d, num_sgat=%d\n", __func__, - sfp->reserve.dlen, - sfp->reserve.num_sgat)); + SG_LOG(3, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__, + sfp->reserve.dlen, sfp->reserve.num_sgat); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); @@ -2302,15 +2287,13 @@ sg_remove_sfp_usercontext(struct work_struct *work) write_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (sfp->reserve.dlen > 0) { - SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, - "sg_remove_sfp: dlen=%d, num_sgat=%d\n", - (int)sfp->reserve.dlen, - (int)sfp->reserve.num_sgat)); + SG_LOG(6, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__, + (int)sfp->reserve.dlen, + (int)sfp->reserve.num_sgat); 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, sdp, "%s: sfp=0x%p\n", __func__, sfp); kfree(sfp); scsi_device_put(sdp->device); @@ -2560,7 +2543,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; @@ -2582,8 +2565,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 || atomic_read(&sdp->detaching)) 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; From patchwork Fri Oct 26 11:48:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657287 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3547D14BD for ; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 24DE92BE95 for ; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 188A62BEA2; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AC9B02BE95 for ; Fri, 26 Oct 2018 11:48:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727582AbeJZUZa (ORCPT ); Fri, 26 Oct 2018 16:25:30 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56704 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727555AbeJZUZ3 (ORCPT ); Fri, 26 Oct 2018 16:25:29 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id B8A8720417C; Fri, 26 Oct 2018 13:48: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 43x367TxtNDb; Fri, 26 Oct 2018 13:48:36 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id 94D5C204248; Fri, 26 Oct 2018 13:48:33 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 3/8] sg: split header, expand and correct descriptions Date: Fri, 26 Oct 2018 12:48:25 +0100 Message-Id: <20181026114830.13506-4-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Split scsi/sg.h into a smaller scsi/sg.h which includes a new header: uapi/scsi/sg.h . Overall expand the twin header files with new functionality in this patchset and functionality to be added in the next patchset to implement SG_IOSUBMIT and friends. Adjust format to modern kernel conventions. Correct and expand some comments. Signed-off-by: Douglas Gilbert --- The new header file: uapi/scsi/sg.h is expected to be in the kernel's public interface. This takes time (i.e. months or years) to find its way into glibc and Linux distributions. So the sooner it is presented and accepted the better. From the C perspective, nothing is removed or changed (or shouldn't be), only additions. This means that the original typedefs stay (but they are not used in the driver source file: sg.c) since user space programs may be using them. include/scsi/sg.h | 252 ++----------------------- include/uapi/scsi/sg.h | 419 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 430 insertions(+), 241 deletions(-) create mode 100644 include/uapi/scsi/sg.h diff --git a/include/scsi/sg.h b/include/scsi/sg.h index f91bcca604e4..596f68746f66 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -7,23 +7,23 @@ /* * History: * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user - * process control of SCSI devices. + * process control of SCSI devices. * Development Sponsored by Killy Corp. NY NY * * Original driver (sg.h): - * Copyright (C) 1992 Lawrence Foard + * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2014 Douglas Gilbert + * Copyright (C) 1998 - 2018 Douglas Gilbert * - * Version: 3.5.36 (20140603) - * This version is for 2.6 and 3 series kernels. + * Version: 3.9.01 (20181016) + * This version is for 2.6, 3 and 4 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] + * 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 + * http://sg.danny.cz/sg/p/sg_v3_ho.html * Also see: /Documentation/scsi/scsi-generic.txt * * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html @@ -33,242 +33,12 @@ extern int sg_big_buff; /* for sysctl */ #endif +#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) */ - -#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 */ - - -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 interruped 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 /* cause 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..37588e1c4e3a --- /dev/null +++ b/include/uapi/scsi/sg.h @@ -0,0 +1,419 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _UAPI_SCSI_SG_H +#define _UAPI_SCSI_SG_H + +/* + * Want to block the original sg.h header from also being included. That + * causes lots of multiple definition errors. This will only work if this + * header is included _before_ the original sg.h header . + */ +#define _SCSI_GENERIC_H /* original kernel header guard */ +#define _SCSI_SG_H /* glibc header guard */ + +/* + * Other UAPI headers include linux/compiler.h to resolve "__" types but that + * doesn't always work, perhaps in the future. Fall back to linux/types.h . + */ +/* #include */ +#include +#include + +/* Still problems with __user so define to nothing */ +#define __user + +#include + +/* + * 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 - 2018 Douglas Gilbert + * + * Version: 3.9.01 (20181021) + * This version is for Linux 2.6, 3 and 4 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.txt + * + * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html + */ + + +/* + * Same structure as used by readv() call. It defines one scatter-gather + * element. "Scatter-gather" is abbreviated to "sgat" in thsi 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 scatter gather */ + unsigned int dxfer_len; /* [i] byte count of data transfer */ + /* 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 */ +/* + * 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 */ +#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 */ +/* no transfer of kernel buffers to/from user space; to debug indirect IO */ +#define SG_FLAG_NO_DXFER 0x10000 +/* 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 + +/* + * Flags used by ioctl(SG_IOSUBMIT) [abbrev: SG_IOS] and ioctl(SG_IORECEIVE) + * [abbrev: SG_IOR] OR-ed into sg_io_v4::flags, also with ioctl(SG_IO) when + * sg_io_v4::guard is 'Q' in which case SGV4_FLAG_SYNC is implicitly set. + * 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_SYNC 0x8 /* similar to ioctl(SG_IO) */ +#define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL +#define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD +/* Following 2 flags for request::tag (type: int) manipulations */ +#define SGV4_NO_TAG_REQUIRED 0x100 /* default: SG_IOS yields tag */ +#define SGV4_USE_GIVEN_TAG 0x200 /* SG_IOR selector else ignored */ +#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR else ignored */ +#define SGV4_FLAG_ASYNC 0x800 /* make sync ioctl (e.g. SG_IO) async */ +#define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */ +#define SGV4_FLAG_NO_DXFER SG_FLAG_NO_DXFER + +/* 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 */ +#define SG_INFO_DEVICE_DETACHING 0x8 /* completed successfully but ... */ + + +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; + +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 interruped 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) */ + /* + * millisecs elapsed since the command started (req_state==1) or + * command duration (req_state==2). May be in nanoseconds after + * the SG_SET_GET_EXTENDED ioctl. + */ + unsigned int duration; + int unused; +} sg_req_info_t; + +/* + * The following defines may help when using struct sg_extended_info which + * is abbreviated to "SEI". A following "M" (i.e. "_SEIM_") indicates a + * mask. Most mask values correspond to a integer (usually a __u32) apart + * from SG_SEIM_CTL_FLAGS which is for a collection of boolean values + * packed into an integer. The mask values for those booleans start with + * "SG_CTL_FLAGM_". The scope of these settings, like most other ioctls, + * is usually that of the file descriptor the ioctl is executed on. Masks + * marked with "rd" are read-only, attempts to write to them are ignored. + */ +#define SG_SEIM_RESERVED_SIZE 0x1 /* reserved_sz field valid */ +#define SG_SEIM_RQ_REM_THRESH 0x2 /* rq_rem_sgat_thresh field valid */ +#define SG_SEIM_TOT_FD_THRESH 0x4 /* tot_fd_thresh field valid */ +#define SG_SEIM_CTL_FLAGS 0x8 /* ctl_flags_mask bits in ctl_flags */ +#define SG_SEIM_MINOR_INDEX 0x10 /* sg device minor index number */ +#define SG_SEIM_ALL_BITS 0x1f /* should be OR of previous items */ + +#define SG_CTL_FLAGM_TIME_IN_NS 0x1 /* time: nanosecs (def: millisecs) */ +#define SG_CTL_FLAGM_TAG_FOR_PACK_ID 0x2 +#define SG_CTL_FLAGM_OTHER_OPENS 0x4 /* rd: other sg fd_s on this dev */ +#define SG_CTL_FLAGM_ORPHANS 0x8 /* rd: orphaned requests on this fd */ + +/* + * A pointer to the following structure is passed as the third argument to + * ioctl(SG_SET_GET_EXTENDED). Each bit in the *_wr_mask fields causes the + * corresponding integer (e.g. reserved_sz) or bit (e.g. the + * SG_CTL_FLAG_TIME_IN_NS bit in ctl_flags) to be read from the user space + * and modify the driver. Each bit in the *_rd_mask fields causes the + * corresponding integer or bit to be fetched from the driver and written + * back to the user space. If the same bit is set in both the *_wr_mask and + * corresponding *_rd_mask fields, then the write action takes place before + * the read action and no other operation will split the two. This structure + * is padded to 64 bytes to allow for new values to be added in the future. + */ +struct sg_extended_info { + __u32 valid_wr_mask; /* OR-ed SG_SEIM_* values */ + __u32 valid_rd_mask; /* OR-ed SG_SEIM_* values */ + __u32 reserved_sz; /* data/sgl size of pre-allocated request */ + __u32 rq_rem_sgat_thresh;/* request re-use: clear data/sgat if > */ + __u32 tot_fd_thresh; /* total data/sgat for this fd, 0: no limit */ + __u32 ctl_flags_wr_mask; /* OR-ed SG_CTL_FLAGM_* values */ + __u32 ctl_flags_rd_mask; /* OR-ed SG_CTL_FLAGM_* values */ + __u32 ctl_flags; /* bit values OR-ed, see SG_CTL_FLAGM_* */ + __u32 minor_index; /* rd: kernel's sg device minor number */ + __u8 pad_to_64[28]; /* pad so struct is 64 bytes long */ +}; + +/* + * 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 */ + +/* + * 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 3.9.01 sg driver. + */ +#define SG_IOCTL_MAGIC_NUM 0x22 + +#define SG_SET_GET_EXTENDED _IOWR(SG_IOCTL_MAGIC_NUM, 0x51, \ + struct sg_extended_info) + +/* 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. 20181014 No + * longer available, always yields 1. + */ +#define SG_GET_ACCESS_COUNT 0x2289 + + +/* + * 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_SCATTER_SZ (8 * 4096) + +#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 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. + */ +}; + +/* + * 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 */ + +/* + * override SCSI command length with given number on the next write() on + * this file descriptor + */ +#define SG_NEXT_CMD_LEN 0x2283 + +/* + * New ioctls to replace async write()/read() interface. Present in version + * 4 and later of the sg driver [>20181014]. These two ioctls accept both + * the sg_v3 interface (structure defined above) and the sg_v4 interface + * (structure defined in ). Following "read" and + * "write" terms are from the driver's POV, the _IO macros from users' POV. + */ +/* via pointer reads sg v3 or v4 object, optionally writes tag, so _IOWR */ +#define SG_IOSUBMIT _IOWR(SG_IOCTL_MAGIC_NUM, 0x41, struct sg_io_v4) +/* via pointer optionally reads tag, writes sg v3 or v4 object, so _IOWR */ +#define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4) +/* via pointer reads v4 object (including tag), writes nothing, so _IOW */ +#define SG_IOABORT _IOW(SG_IOCTL_MAGIC_NUM, 0x43, struct sg_io_v4) + +/* command queuing is always on when the new interface is used */ +#define SG_DEF_COMMAND_Q 0 + +#define SG_DEF_UNDERRUN_FLAG 0 + +#define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */ + +#endif From patchwork Fri Oct 26 11:48:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657283 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7056414E2 for ; Fri, 26 Oct 2018 11:48:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 292AA2BE95 for ; Fri, 26 Oct 2018 11:48:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1D95F2BEA1; Fri, 26 Oct 2018 11:48:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A2D492BE95 for ; Fri, 26 Oct 2018 11:48:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727570AbeJZUZ3 (ORCPT ); Fri, 26 Oct 2018 16:25:29 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56697 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727333AbeJZUZ3 (ORCPT ); Fri, 26 Oct 2018 16:25:29 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 1672E20423A; Fri, 26 Oct 2018 13:48: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 1GX6e-Tbhb-B; Fri, 26 Oct 2018 13:48:37 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id 5EFFF20417C; Fri, 26 Oct 2018 13:48:34 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 4/8] sg: expand request states Date: Fri, 26 Oct 2018 12:48:26 +0100 Message-Id: <20181026114830.13506-5-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Introduce the new sg_rq_state enumerations for tracking the lifetime of a sg_request. SG_RQ_DATA_THRESHOLD is a default value that if the data length of a request exceeds then, after that request is completed, the data buffer will be freed up as the sg_request object is placed on the free list. SG_TOT_FD_THRESHOLD is a default, per file descriptor value that the sum of outstanding command data lengths is not allowed to exceed. Signed-off-by: Douglas Gilbert --- The '#if 0's are temporary and removed in a later patch. They allow the following large and complex patch (5 of 8) to be a bit shorter and still compile. drivers/scsi/sg.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 94e13a1d21a5..a76395f16fb1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -75,6 +75,24 @@ static int sg_proc_init(void); */ #define SG_MAX_CDB_SIZE 252 +/* Following enum contains the states of sg_request::rq_state */ +enum sg_rq_state { + SG_RQ_INACTIVE = 0, /* request not in use (e.g. on fl) */ + SG_RQ_INFLIGHT, /* SCSI request issued, no response yet */ + SG_RQ_AWAIT_READ, /* response received, awaiting read */ + SG_RQ_DONE_READ, /* read is ongoing or done */ + SG_RQ_BUSY, /* example: reserve request changing size */ +}; + +/* free up requests larger than this dlen size after use */ +#define SG_RQ_DATA_THRESHOLD (128 * 1024) + +/* If sum_of(dlen) of a fd exceeds this, write() will yield E2BIG */ +#define SG_TOT_FD_THRESHOLD (16 * 1024 * 1024) + +#define SG_TIME_UNIT_MS 0 /* milliseconds */ +#define SG_TIME_UNIT_NS 1 /* nanoseconds */ +#define SG_DEF_TIME_UNIT SG_TIME_UNIT_MS #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) int sg_big_buff = SG_DEF_RESERVED_SIZE; @@ -950,6 +968,7 @@ sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo) } } +#if 0 /* temporary to shorten big patch */ static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -1227,6 +1246,7 @@ sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENOIOCTLCMD; } #endif +#endif /* temporary to shorten big patch */ static __poll_t sg_poll(struct file *filp, poll_table * wait) @@ -1496,10 +1516,12 @@ static const struct file_operations sg_fops = { .read = sg_read, .write = sg_write, .poll = sg_poll, +#if 0 /* temporary to shorten big patch */ .unlocked_ioctl = sg_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sg_compat_ioctl, #endif +#endif /* temporary to shorten big patch */ .open = sg_open, .mmap = sg_mmap, .release = sg_release, @@ -2422,12 +2444,16 @@ static const struct seq_operations devstrs_seq_ops = { .show = sg_proc_seq_show_devstrs, }; +#if 0 /* temporary to shorten big patch */ static int sg_proc_seq_show_debug(struct seq_file *s, void *v); +#endif /* temporary to shorten big patch */ static const struct seq_operations debug_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, +#if 0 /* temporary to shorten big patch */ .show = sg_proc_seq_show_debug, +#endif /* temporary to shorten big patch */ }; static int @@ -2601,6 +2627,8 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) return 0; } +#if 0 /* temporary to shorten big patch */ + /* must be called while holding sg_index_lock */ static void sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) @@ -2704,6 +2732,7 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } +#endif /* temporary to shorten big patch */ #endif /* CONFIG_SCSI_PROC_FS */ From patchwork Fri Oct 26 11:48:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657293 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D606C14E2 for ; Fri, 26 Oct 2018 11:48:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C199E2BE97 for ; Fri, 26 Oct 2018 11:48:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B57C72C142; Fri, 26 Oct 2018 11:48:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 66FFB2BE97 for ; Fri, 26 Oct 2018 11:48:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727598AbeJZUZg (ORCPT ); Fri, 26 Oct 2018 16:25:36 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56727 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727583AbeJZUZg (ORCPT ); Fri, 26 Oct 2018 16:25:36 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 1FB9B20419B; Fri, 26 Oct 2018 13:48:45 +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 C0gUnXrcyq7v; Fri, 26 Oct 2018 13:48:40 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id D87CA204198; Fri, 26 Oct 2018 13:48:34 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 5/8] sg: add free list, rework locking Date: Fri, 26 Oct 2018 12:48:27 +0100 Message-Id: <20181026114830.13506-6-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Remove fixed 16 sg_request object array and replace with an active rq_list plus a request free list. Add finer grained spin lock to sg_request and do a major rework on locking. sg_request objects now are only de-allocated when the owning file descriptor is closed. This simplifies locking issues. Signed-off-by: Douglas Gilbert --- This patch is big and complex. Towards the end the diff program completely loses the plot. Better to use difftool on a two pane window, or simply view the before sg.c and the after sg.c . The requirement to keep the patch small enough to be reviewable but at the same time both compilable and buildable (i.e. no undefines when kernel is built) are in conflict, especially when the changes touch almost all functions in a driver. IMO you should be able to mark a patch (in the middle of a patchset) as "non compilable". drivers/scsi/sg.c | 1305 ++++++++++++++++++++++++++++----------------- 1 file changed, 805 insertions(+), 500 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index a76395f16fb1..5fbdb0f40739 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -141,46 +141,58 @@ struct sg_scatter_hold { /* holding area for scsi scatter gather info */ 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_fd *parentfp; /* NULL -> not in use */ +/* + * For any file descriptor: at any time a sg_request object must be a member + * of sg_fd::rq_list or sg_fd::rq_free_list. The only exception is within a + * rq_list_lock write lock when it is moving between those two lists. + */ + +struct sg_request { /* active SCSI command or inactive on free list (fl) */ + struct list_head rq_entry; /* member of rq_list (active cmd) */ + struct list_head free_entry; /* member of rq_free_list */ + spinlock_t rq_entry_lck; struct sg_scatter_hold data; /* hold buffer, perhaps scatter list */ union { struct sg_io_hdr header; /* see */ - struct sg_io_v4 hdr_v4; /* see */ + struct sg_v4_hold v4_hold;/* related to */ }; - u8 sense_b[SCSI_SENSE_BUFFERSIZE]; - bool hdr_v4_active; /* selector for anonymous union above */ - bool res_used; /* true -> use reserve buffer, false -> don't */ + ktime_t start_ts; /* used when sg_fd::time_in_ns is true */ + enum sg_rq_state rq_state;/* tracks lifetime of each request */ + bool v4_active; /* selector for autonomous union above */ bool orphan; /* true -> drop on sight, false -> normal */ - bool sg_io_owned; /* true -> packet belongs to SG_IO */ - /* done protected by rq_list_lock */ - char done; /* 0->before bh, 1->before read, 2->read */ + bool sync_invoc;/* true -> synchronous (e.g. from ioctl(SG_IO)) */ + u8 sense_b[SCSI_SENSE_BUFFERSIZE]; + struct sg_fd *parentfp; /* pointer to owning fd, even when on fl */ + struct sg_scatter_hold *d2p; /* optional 2nd data buffer for bidi */ struct request *rq; struct bio *bio; - struct execute_work ew; + 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 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 */ struct mutex f_mutex; /* protect against changes in this fd */ + rwlock_t rq_list_lock; /* protect access to sg_request lists */ + struct list_head rq_list; /* head of inflight sg_request list */ + struct list_head rq_free_list; /* head of sg_request free list */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ - struct sg_scatter_hold reserve; /* one held 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];/* used as singly-linked list */ + int rem_sgat_thresh; /* > this, request's sgat cleared after use */ + int tot_fd_thresh; /* E2BIG if sum_of(dlen) > this, 0: ignore */ + atomic_t sum_fd_dlens; /* when tot_fd_thresh>0 this is sum_of(dlen) */ bool force_packid; /* true -> pack_id input to read() */ bool cmd_q; /* true -> allow command queuing, false -> don't */ - u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ bool keep_orphan;/* false -> drop (def), true -> keep for read() */ bool mmap_called; /* false -> mmap() never called on this fd */ - bool res_in_use; /* true -> 'reserve' array in use */ + bool sse_seen; /* SG_SET_EXTENDED ioctl seen */ + bool time_in_ns; /* report times in nanoseconds */ + u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ + struct sg_request *reserve_srp; /* allocate on open(), starts on fl */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ struct kref f_ref; - struct execute_work ew; + struct execute_work ew; /* harvest all active and free list requests */ }; struct sg_device { /* holds the state of each scsi generic device */ @@ -189,8 +201,8 @@ struct sg_device { /* holds the state of each scsi generic device */ 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 */ + struct list_head sfds; /* head of sg_fd::sfd_entry list */ + rwlock_t sfd_lock; /* protect access to sfds list */ atomic_t detaching; /* 0->device usable, 1->device detaching */ bool exclude; /* 1->open(O_EXCL) succeeded and is active */ int open_cnt; /* count of opens (perhaps < num(sfds) ) */ @@ -203,36 +215,37 @@ struct sg_device { /* holds the state of each scsi generic device */ /* tasklet or soft irq callback */ static void sg_rq_end_io(struct request *rq, blk_status_t status); 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 void sg_finish_scsi_blk_rq(struct sg_request *srp); +static int sg_mk_sgat_dlen(struct sg_request *srp, struct sg_fd *sfp, + int dlen); static ssize_t sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, struct sg_request *srp); -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 ssize_t sg_v3_write(struct sg_fd *sfp, struct file *file, + const char __user *buf, size_t count, + bool read_only, bool sync, + struct sg_request **o_srp); +static struct sg_request *sg_common_write(struct sg_fd *sfp, + const struct sg_io_hdr *hp, + struct sg_io_v4 *h4p, u8 *cmnd, + bool sync, int timeout); 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); + int num_xfer); +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_get_rq_pack_id(struct sg_fd *sfp, int pack_id); -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 struct sg_request *sg_add_request(struct sg_fd *sfp, int dxfr_len, + bool sync); +static void sg_remove_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); -#define SZ_SG_HEADER sizeof(struct sg_header) -#define SZ_SG_IO_HDR sizeof(struct sg_io_hdr) +#define SZ_SG_HEADER sizeof(struct sg_header) /* v1 and v2 header */ +#define SZ_SG_IO_HDR sizeof(struct sg_io_hdr) /* v3 header */ +#define SZ_SG_IO_V4 sizeof(struct sg_io_v4) /* v4 header (in bsg.h) */ /* #define SZ_SG_IOVEC sizeof(struct sg_iovec) synonym for 'struct iovec' */ #define SZ_SG_REQ_INFO sizeof(struct sg_req_info) +#define SZ_SG_EXTENDED_INFO sizeof(struct sg_extended_info) /* * Kernel needs to be built with CONFIG_SCSI_LOGGING to see log messages. @@ -297,7 +310,8 @@ sg_allow_access(struct file *filp, u8 *cmd) } static int -open_wait(struct sg_device *sdp, int flags) +wait_open_event(struct sg_device *sdp, int flags) + __must_hold(&sdp->open_rel_lock) { int retval = 0; @@ -386,7 +400,7 @@ sg_open(struct inode *inode, struct file *filp) } } } else { - retval = open_wait(sdp, flags); + retval = wait_open_event(sdp, flags); if (retval) /* -ERESTARTSYS or -ENODEV */ goto error_mutex_locked; } @@ -623,7 +637,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } } else count = (ohdr->result == 0) ? 0 : -EIO; - sg_finish_rem_req(srp); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); retval = count; free_old_hdr: @@ -635,13 +649,13 @@ static ssize_t sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, struct sg_request *srp) { - int err = 0, err2; + int err = 0; int len; struct sg_io_hdr *hp = &srp->header; if (count < SZ_SG_IO_HDR) { err = -EINVAL; - goto err_out; + goto out; } hp->sb_len_wr = 0; if ((hp->mx_sb_len > 0) && hp->sbp) { @@ -654,7 +668,7 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, len = (len > sb_len) ? sb_len : len; if (copy_to_user(hp->sbp, srp->sense_b, len)) { err = -EFAULT; - goto err_out; + goto out; } hp->sb_len_wr = len; } @@ -663,27 +677,28 @@ sg_new_read(struct sg_fd *sfp, char __user *buf, size_t count, hp->info |= SG_INFO_CHECK; if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) { err = -EFAULT; - goto err_out; + goto out; } -err_out: - err2 = sg_finish_rem_req(srp); + if (atomic_read(&sfp->parentdp->detaching))/* okay but on thin ice */ + hp->info |= SG_INFO_DEVICE_DETACHING; +out: + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); - return err ? : err2 ? : count; + return err ? err : count; } static ssize_t sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) { - int mxsize, cmd_size, k; - int input_size, blocking; + int mxsize, cmd_size, input_size, retval; u8 opcode; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - struct sg_io_hdr *hp; u8 cmnd[SG_MAX_CDB_SIZE]; - int retval; struct sg_header ohdr; + struct sg_io_hdr v3hdr; + struct sg_io_hdr *hp = &v3hdr; retval = sg_check_file_access(filp, __func__); if (retval) @@ -710,17 +725,11 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -EIO; if (__copy_from_user(&ohdr, buf, SZ_SG_HEADER)) return -EFAULT; - blocking = !(filp->f_flags & O_NONBLOCK); if (ohdr.reply_len < 0) - return sg_new_write(sfp, filp, buf, count, - blocking, 0, 0, NULL); + return sg_v3_write(sfp, filp, buf, count, false, false, NULL); if (count < (SZ_SG_HEADER + 6)) return -EIO; /* minimum scsi command length is 6 bytes */ - if (!(srp = sg_add_request(sfp))) { - SG_LOG(1, sdp, "%s: queue full\n", __func__); - return -EDOM; - } buf += SZ_SG_HEADER; __get_user(opcode, buf); mutex_lock(&sfp->f_mutex); @@ -739,12 +748,10 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) mxsize = (input_size > ohdr.reply_len) ? input_size : ohdr.reply_len; mxsize -= SZ_SG_HEADER; input_size -= SZ_SG_HEADER; - if (input_size < 0) { - sg_remove_request(sfp, srp); + if (input_size < 0) return -EIO; /* Insufficient bytes passed for this command. */ - } - hp = &srp->header; - hp->interface_id = '\0'; /* indicator of old interface tunnelled */ + memset(hp, 0, sizeof(*hp)); + hp->interface_id = '\0';/* indicate old interface tunnelled */ hp->cmd_len = (u8)cmd_size; hp->iovec_count = 0; hp->mx_sb_len = 0; @@ -762,111 +769,95 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) hp->dxferp = NULL; hp->sbp = NULL; hp->timeout = ohdr.reply_len; /* structure abuse ... */ - hp->flags = input_size; /* structure abuse ... */ + hp->flags = input_size; /* structure abuse ... */ hp->pack_id = ohdr.pack_id; hp->usr_ptr = NULL; if (__copy_from_user(cmnd, buf, cmd_size)) return -EFAULT; /* * 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 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) { 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", - ohdr.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; + "%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__, ohdr.reply_len - (int)SZ_SG_HEADER, + input_size, (unsigned int)cmnd[0], current->comm); + } + srp = sg_common_write(sfp, hp, NULL, cmnd, false, sfp->timeout); + return (IS_ERR(srp)) ? PTR_ERR(srp) : count; } 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_v3_write(struct sg_fd *sfp, struct file *file, const char __user *buf, + size_t count, bool read_only, bool sync, + struct sg_request **o_srp) { - int k; - struct sg_request *srp; - struct sg_io_hdr *hp; - u8 cmnd[SG_MAX_CDB_SIZE]; + struct sg_io_hdr v3hdr; int timeout; unsigned long ul_timeout; + struct sg_io_hdr *hp = &v3hdr; + struct sg_request *srp; + u8 cmnd[SG_MAX_CDB_SIZE]; if (count < SZ_SG_IO_HDR) return -EINVAL; if (!access_ok(VERIFY_READ, buf, count)) - return -EFAULT; /* protects following copy_from_user()s + get_user()s */ - - sfp->cmd_q = true; /* when sg_io_hdr seen, set command queuing on */ - if (!(srp = sg_add_request(sfp))) { - SG_LOG(1, sfp->parentdp, "%s: queue full\n", __func__); - return -EDOM; - } - srp->sg_io_owned = sg_io_owned; - hp = &srp->header; - if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { - sg_remove_request(sfp, srp); return -EFAULT; - } - if (hp->interface_id != 'S') { - sg_remove_request(sfp, srp); + if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) + return -EFAULT; + if (hp->interface_id == 'Q') + return -EOPNOTSUPP; /* placeholder for sgv4 interface */ + else if (hp->interface_id != 'S') return -ENOSYS; - } if (hp->flags & SG_FLAG_MMAP_IO) { - if (hp->dxfer_len > sfp->reserve.dlen) { - sg_remove_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); - return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ - } - if (sfp->res_in_use) { - sg_remove_request(sfp, srp); - return -EBUSY; /* reserve buffer already being used */ - } + if (!list_empty(&sfp->rq_list)) + return -EBUSY; /* already active requests on fd */ + if (hp->dxfer_len > sfp->reserve_srp->data.dlen) + return -ENOMEM; /* MMAP_IO size must fit in reserve */ + if (hp->flags & SG_FLAG_DIRECT_IO) + return -EINVAL; /* not both MMAP_IO and DIRECT_IO */ } - ul_timeout = msecs_to_jiffies(srp->header.timeout); + sfp->cmd_q = true; /* when sg_io_hdr seen, set command queuing on */ + ul_timeout = msecs_to_jiffies(hp->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); + if (!hp->cmdp || hp->cmd_len < 6 || hp->cmd_len > sizeof(cmnd)) return -EMSGSIZE; - } - if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) { - sg_remove_request(sfp, srp); + if (!access_ok(VERIFY_READ, hp->cmdp, hp->cmd_len)) return -EFAULT; /* protects following copy_from_user()s + get_user()s */ - } - if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) { - sg_remove_request(sfp, srp); + if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) return -EFAULT; - } - if (read_only && sg_allow_access(file, cmnd)) { - sg_remove_request(sfp, srp); + if (read_only && sg_allow_access(file, cmnd)) return -EPERM; - } - k = sg_common_write(sfp, srp, cmnd, timeout, blocking); - if (k < 0) - return k; + srp = sg_common_write(sfp, hp, NULL, cmnd, sync, timeout); + if (IS_ERR(srp)) + return PTR_ERR(srp); if (o_srp) *o_srp = srp; return count; } -static int -sg_common_write(struct sg_fd *sfp, struct sg_request *srp, - u8 *cmnd, int timeout, int blocking) + +static struct sg_request * +sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p, + struct sg_io_v4 *h4p, u8 *cmnd, bool sync, int timeout) { bool at_head; - int k; + int res; struct sg_device *sdp = sfp->parentdp; - struct sg_io_hdr *hp = &srp->header; + struct sg_request *srp; + struct sg_io_hdr *hp; + if (h4p || !hi_p) + return ERR_PTR(-EOPNOTSUPP); + srp = sg_add_request(sfp, hi_p->dxfer_len, false); + if (IS_ERR(srp)) + return srp; + srp->header = *hi_p; /* structure assignment, could memcpy */ + hp = &srp->header; srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ hp->status = 0; hp->masked_status = 0; @@ -875,19 +866,18 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, hp->host_status = 0; hp->driver_status = 0; hp->resid = 0; - SG_LOG(4, sfp->parentdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", - __func__, (int)cmnd[0], (int)hp->cmd_len); + SG_LOG(4, sdp, "%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (int)cmnd[0], (int)hp->cmd_len); if (hp->dxfer_len >= SZ_256M) - return -EINVAL; + return ERR_PTR(-EINVAL); - k = sg_start_req(srp, cmnd); - if (k) { - SG_LOG(1, sfp->parentdp, "%s: start_req err=%d\n", __func__, - k); - sg_finish_rem_req(srp); + res = sg_start_req(srp, cmnd); + if (res) { + SG_LOG(1, sdp, "%s: start_req err=%d\n", __func__, -res); + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); - return k; /* probably out of space --> ENOMEM */ + return ERR_PTR(res); /* probably out of space --> ENOMEM */ } if (atomic_read(&sdp->detaching)) { if (srp->bio) { @@ -896,12 +886,15 @@ 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; + return ERR_PTR(-ENODEV); } - hp->duration = jiffies_to_msecs(jiffies); + if (sfp->time_in_ns) + srp->start_ts = ktime_get_with_offset(TK_OFFS_BOOT); + else + hp->duration = jiffies_to_msecs(jiffies); /* at tail if v3 or later interface and tail flag set */ at_head = !(hp->interface_id != '\0' && (SG_FLAG_Q_AT_TAIL & hp->flags)); @@ -910,19 +903,8 @@ sg_common_write(struct sg_fd *sfp, struct sg_request *srp, kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, srp->rq, (int)at_head, sg_rq_end_io); - return 0; -} - -static int -srp_done(struct sg_fd *sfp, struct sg_request *srp) -{ - unsigned long flags; - int ret; - - read_lock_irqsave(&sfp->rq_list_lock, flags); - ret = srp->done; - read_unlock_irqrestore(&sfp->rq_list_lock, flags); - return ret; + /* u32 tag = blk_mq_unique_tag(srp->rq); should now be available */ + return srp; } static int @@ -935,49 +917,132 @@ max_sectors_bytes(struct request_queue *q) return max_sectors << 9; } +/* + * For backward compatibility the duration in nanoseconds is placed in a + * 32 bit unsigned integer. This limits the maximum duration that can + * be represented (without wrapping) to about 4.3 seconds. + */ +static inline u32 +sg_ktime_sub_trunc(ktime_t now_ts, ktime_t ts0) +{ + if (ktime_after(now_ts, ts0)) + return (u32)ktime_to_ns(ktime_sub(now_ts, ts0)); + else + return 0; +} + +/* + * Annotation under function arguments (i.e. '__must_hold...') states that + * this function expects that lock to be held, a read lock is sufficient in + * this case. + */ 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) + __must_hold(&sfp->rq_list_lock) +{ + spin_lock(&srp->rq_entry_lck); + rip->req_state = (int)srp->rq_state; + rip->problem = srp->header.masked_status & srp->header.host_status & + srp->header.driver_status; + switch (srp->rq_state) { + case SG_RQ_INFLIGHT: + if (sfp->time_in_ns) { + ktime_t now_ts = + ktime_get_with_offset(TK_OFFS_BOOT); + ktime_t ts0 = srp->start_ts; + + /* N.B. truncation to fit in 32 bit field */ + rip->duration = sg_ktime_sub_trunc(now_ts, ts0); + } else { + unsigned int ms = jiffies_to_msecs(jiffies); + + rip->duration = (ms > srp->header.duration) ? + (ms - srp->header.duration) : 0; + } + break; + case SG_RQ_AWAIT_READ: + case SG_RQ_DONE_READ: + rip->duration = srp->header.duration; + break; + case SG_RQ_INACTIVE: + case SG_RQ_BUSY: + default: + rip->duration = 0; + break; + } + rip->orphan = srp->orphan; + rip->sg_io_owned = srp->sync_invoc; + rip->pack_id = srp->header.pack_id; + rip->usr_ptr = srp->header.usr_ptr; + spin_unlock(&srp->rq_entry_lck); +} + +/* + * Populate up to max_num struct sg_req_info objects, first from the active + * list then, if there is still room, from the free list. All elements in + * the free list should have SG_RQ_INACTIVE status. + * See sg_fill_request_element() for note about __must_hold annotation. + */ +static void +sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo, + int max_num) + __must_hold(&sfp->rq_list_lock) { struct sg_request *srp; + struct sg_req_info *rip; 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; - if (srp->done) - rinfo[val].duration = - srp->header.duration; - else { - ms = jiffies_to_msecs(jiffies); - rinfo[val].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; + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { + if (val >= max_num) + return; + rip = &rinfo[val]; + sg_fill_request_element(sfp, srp, rip); + val++; + } + list_for_each_entry(srp, &sfp->rq_free_list, free_entry) { + if (val >= max_num) + return; + rip = &rinfo[val]; + sg_fill_request_element(sfp, srp, rip); val++; } } +/* + * This function is called from one place: the wait_event_interruptible() + * in the synchronous ioctl(SG_IO) call (see sg_ioctl()). Since only one + * simple value (a u8) is being read, one argument is that the spinlock + * should not be needed. The repercussions of being alerted but not seeing + * the new state in srp->rq_state are quite nasty. A middle ground is to + * use wait_event_interruptible_lock_irq() . + */ +static inline bool +srp_state_or_detaching(struct sg_device *sdp, struct sg_request *srp) +{ + /* unsigned long flags; */ + bool ret; + + /* spin_lock_irqsave(&srp->rq_entry_lck, flags); */ + ret = srp->rq_state != SG_RQ_INFLIGHT || + atomic_read(&sdp->detaching); + /* spin_unlock_irqrestore(&srp->rq_entry_lck, flags); */ + return ret; +} + #if 0 /* temporary to shorten big patch */ static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { + bool leave; void __user *p = (void __user *)arg; int __user *ip = p; int result, val, read_only; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; + const char *cp; unsigned long iflags; sfp = filp->private_data; @@ -1251,38 +1316,42 @@ sg_compat_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; + __poll_t pres = 0; + bool empty; + unsigned long iflags; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - int count = 0; - unsigned long iflags; sfp = filp->private_data; - if (!sfp) + if (IS_ERR_OR_NULL(sfp)) return EPOLLERR; sdp = sfp->parentdp; - if (!sdp) + if (IS_ERR_OR_NULL(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) { + empty = list_empty(&sfp->rq_list); + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { /* if any read waiting, flag it */ - if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) - res = EPOLLIN | EPOLLRDNORM; - ++count; + spin_lock(&srp->rq_entry_lck); + if (srp->rq_state == SG_RQ_AWAIT_READ && !srp->sync_invoc) { + spin_unlock(&srp->rq_entry_lck); + pres = EPOLLIN | EPOLLRDNORM; + break; + } + spin_unlock(&srp->rq_entry_lck); } 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, sdp, "%s: res=0x%x\n", __func__, (__force u32)res); - return res; + pres |= EPOLLHUP; + else if (sfp->cmd_q) + pres |= EPOLLOUT | EPOLLWRNORM; + else if (empty) + pres |= EPOLLOUT | EPOLLWRNORM; + SG_LOG(3, sdp, "%s: pres=0x%x\n", __func__, (__force u32)pres); + return pres; } static int @@ -1292,12 +1361,14 @@ sg_fasync(int fd, struct file *filp, int mode) struct sg_fd *sfp; sfp = filp->private_data; - if (!sfp) - return -ENXIO; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } sdp = sfp->parentdp; - if (!sdp) - return -ENXIO; SG_LOG(3, sdp, "%s: mode=%d\n", __func__, mode); + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1306,25 +1377,46 @@ static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; - struct sg_fd *sfp; - unsigned long offset, len, sa; struct sg_scatter_hold *rsv_schp; + struct sg_request *srp; + struct sg_device *sdp; + struct sg_fd *sfp; int k, length; + unsigned long offset, len, sa; + const char *nbp = "==NULL, bad"; - if (!vma) + if (!vma) { + pr_warn("%s: vma%s\n", __func__, nbp); return VM_FAULT_SIGBUS; + } sfp = vma->vm_private_data; - if (!sfp) + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("%s: sfp%s\n", __func__, nbp); return VM_FAULT_SIGBUS; - rsv_schp = &sfp->reserve; - offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rsv_schp->dlen) + } + sdp = sfp->parentdp; + if (sdp && unlikely(atomic_read(&sdp->detaching))) { + SG_LOG(1, sdp, "%s: device deatching\n", __func__); return VM_FAULT_SIGBUS; - SG_LOG(3, sfp->parentdp, "%s: offset=%lu, scatg=%d\n", __func__, - offset, rsv_schp->num_sgat); + } + /* guard against ioctl(SG_SET_RESERVED_SIZE) and the like */ + mutex_lock(&sfp->f_mutex); + srp = sfp->reserve_srp; + if (!srp) { + SG_LOG(1, sdp, "%s: srp%s\n", __func__, nbp); + goto out_err; + } + rsv_schp = &srp->data; + offset = vmf->pgoff << PAGE_SHIFT; + if (offset >= rsv_schp->dlen) { + SG_LOG(1, sdp, "%s: offset>reserve.dlen\n", __func__); + goto out_err; + } sa = vma->vm_start; + SG_LOG(3, sdp, "%s: vm_start=0x%lx, offset=%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; if (offset < len) { @@ -1332,12 +1424,14 @@ sg_vma_fault(struct vm_fault *vmf) offset >> PAGE_SHIFT); get_page(page); /* increment page count */ vmf->page = page; + mutex_unlock(&sfp->f_mutex); return 0; /* success */ } sa += len; offset -= len; } - +out_err: + mutex_unlock(&sfp->f_mutex); return VM_FAULT_SIGBUS; } @@ -1348,32 +1442,44 @@ static const struct vm_operations_struct sg_mmap_vm_ops = { 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, iflags; + struct sg_scatter_hold *rsv_schp; + struct sg_fd *sfp; + struct sg_request *srp; if (!filp || !vma) return -ENXIO; sfp = filp->private_data; - if (!sfp) - return -ENXIO; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } req_sz = vma->vm_end - vma->vm_start; - SG_LOG(3, sfp->parentdp, "%s starting, vm_start=%p, len=%d\n", - __func__, (void *)vma->vm_start, (int)req_sz); - if (vma->vm_pgoff) + SG_LOG(3, sfp->parentdp, "%s: vm_start=%p, len=%d\n", __func__, + (void *)vma->vm_start, (int)req_sz); + if (vma->vm_pgoff || IS_ERR_OR_NULL(sfp->parentdp)) return -EINVAL; /* want no offset */ - rsv_schp = &sfp->reserve; + /* + * Assume no requests active on this file descriptor (sfp) so that + * the reserve request is on free list + */ mutex_lock(&sfp->f_mutex); + srp = sfp->reserve_srp; + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + if (srp->rq_state != SG_RQ_INACTIVE) { + ret = -EBUSY; + goto out; + } + rsv_schp = &srp->data; if (req_sz > rsv_schp->dlen) { - ret = -ENOMEM; /* cannot map more than reserved buffer */ + 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; sa += len; @@ -1384,6 +1490,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; out: + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); mutex_unlock(&sfp->f_mutex); return ret; } @@ -1399,10 +1506,20 @@ 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_fd *sfp = srp->parentfp; + struct sg_request *srp = container_of(work, struct sg_request, + ew_orph.work); + struct sg_fd *sfp; - sg_finish_rem_req(srp); + if (!srp) { + WARN_ONCE("s: srp unexpectedly NULL\n", __func__); + return; + } + sfp = srp->parentfp; + if (!sfp) { + WARN_ONCE(1, "%s: sfp unexpectedly NULL\n", __func__); + return; + } + sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); } @@ -1418,36 +1535,46 @@ static void sg_rq_end_io(struct request *rq, blk_status_t status) { struct sg_request *srp = rq->end_io_data; - struct scsi_request *req = scsi_req(rq); + struct scsi_request *scsi_rp = scsi_req(rq); struct sg_device *sdp; struct sg_fd *sfp; - unsigned long iflags; - unsigned int ms; u8 *sense; - int result, resid, done = 1; + unsigned long iflags; + int result, resid; + enum sg_rq_state rqq_state = SG_RQ_AWAIT_READ; - if (WARN_ON(srp->done != 0)) + if (WARN_ON(srp->rq_state != SG_RQ_INFLIGHT)) return; - sfp = srp->parentfp; - if (WARN_ON(sfp == NULL)) + if (unlikely(!sfp)) { + WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__); return; - + } sdp = sfp->parentdp; if (unlikely(atomic_read(&sdp->detaching))) pr_info("%s: device detaching\n", __func__); - sense = req->sense; - result = req->result; - resid = req->resid_len; + sense = scsi_rp->sense; + result = scsi_rp->result; + resid = scsi_rp->resid_len; SG_LOG(4, sdp, "%s: pack_id=%d, res=0x%x\n", __func__, srp->header.pack_id, result); srp->header.resid = resid; - ms = jiffies_to_msecs(jiffies); - srp->header.duration = (ms > srp->header.duration) ? - (ms - srp->header.duration) : 0; - if (0 != result) { + if (sfp->time_in_ns) { + ktime_t now_ts = ktime_get_with_offset(TK_OFFS_BOOT); + ktime_t ts0 = srp->start_ts; + + /* N.B. truncation to fit in 32 bit field */ + srp->header.duration = ktime_after(now_ts, ts0) ? + (u32)ktime_sub(now_ts, ts0) : 0; + } else { + unsigned int ms = jiffies_to_msecs(jiffies); + + srp->header.duration = (ms > srp->header.duration) ? + (ms - srp->header.duration) : 0; + } + if (unlikely(result)) { struct scsi_sense_hdr sshdr; srp->header.status = 0xff & result; @@ -1473,8 +1600,8 @@ sg_rq_end_io(struct request *rq, blk_status_t status) } } - if (req->sense_len) - memcpy(srp->sense_b, req->sense, SCSI_SENSE_BUFFERSIZE); + if (scsi_rp->sense_len) + memcpy(srp->sense_b, scsi_rp->sense, SCSI_SENSE_BUFFERSIZE); /* Rely on write phase to clean out srp status values, so no "else" */ @@ -1485,29 +1612,30 @@ sg_rq_end_io(struct request *rq, blk_status_t status) * blk_rq_unmap_user() can be called from user context. */ srp->rq = NULL; - scsi_req_free_cmd(scsi_req(rq)); + scsi_req_free_cmd(scsi_rp); __blk_put_request(rq->q, rq); - write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock_irqsave(&srp->rq_entry_lck, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan) - srp->sg_io_owned = 0; + srp->sync_invoc = false; else - done = 0; + rqq_state = SG_RQ_BUSY; } - srp->done = done; - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + srp->rq_state = rqq_state; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - if (likely(done)) { - /* Now wake up any sg_read() that is waiting for this - * packet. + if (likely(rqq_state == SG_RQ_AWAIT_READ)) { + /* + * Now wake up any sg_read() or ioctl(SG_IORECEIVE) that is + * waiting for this packet. */ wake_up_interruptible(&sfp->read_wait); 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); + } 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); } } @@ -1565,8 +1693,8 @@ sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) } k = error; - SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, - "sg_alloc: dev=%d \n", k)); + SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, "%s: dev=%d\n", + __func__, k)); sprintf(disk->disk_name, "sg%d", k); disk->first_minor = k; sdp->disk = disk; @@ -1710,7 +1838,7 @@ sg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) SG_LOG(3, sdp, "%s\n", __func__); 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); } @@ -1796,22 +1924,31 @@ exit_sg(void) static int sg_start_req(struct sg_request *srp, u8 *cmd) { - int res; struct request *rq; - struct scsi_request *req; + struct scsi_request *scsi_rp; struct sg_fd *sfp = srp->parentfp; + struct sg_device *sdp; struct sg_io_hdr *hp = &srp->header; + struct sg_scatter_hold *req_schp = &srp->data; + struct request_queue *q; + struct rq_map_data *md; + u8 *long_cmdp = NULL; + bool reserved; + int res; 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 rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; - u8 *long_cmdp = NULL; + unsigned long iflags; + unsigned int iov_count = hp->iovec_count; + struct rq_map_data map_data; - SG_LOG(4, sfp->parentdp, "%s: dxfer_len=%d\n", __func__, dxfer_len); + if (unlikely(!sfp)) { + WARN_ONCE(1, "%s: sfp unexpectedly NULL", __func__); + return -EBADF; + } + sdp = sfp->parentdp; + SG_LOG(4, sdp, "%s: dxfer_len=%d\n", __func__, dxfer_len); + q = sdp->device->request_queue; if (hp->cmd_len > BLK_MAX_CDB) { long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL); @@ -1831,51 +1968,51 @@ sg_start_req(struct sg_request *srp, u8 *cmd) * not expect an EWOULDBLOCK from this condition. */ rq = blk_get_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ? - REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0); + REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0); if (IS_ERR(rq)) { kfree(long_cmdp); return PTR_ERR(rq); } - req = scsi_req(rq); + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + scsi_rp = scsi_req(rq); if (hp->cmd_len > BLK_MAX_CDB) - req->cmd = long_cmdp; - memcpy(req->cmd, cmd, hp->cmd_len); - req->cmd_len = hp->cmd_len; + scsi_rp->cmd = long_cmdp; + memcpy(scsi_rp->cmd, cmd, hp->cmd_len); + scsi_rp->cmd_len = hp->cmd_len; srp->rq = rq; rq->end_io_data = srp; - req->retries = SG_DEFAULT_RETRIES; + scsi_rp->retries = SG_DEFAULT_RETRIES; + srp->rq_state = SG_RQ_INFLIGHT; + reserved = (sfp->reserve_srp == srp); + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; - if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO && + if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && dxfer_dir != SG_DXFER_UNKNOWN && !iov_count && - !sfp->parentdp->device->host->unchecked_isa_dma && + !sdp->device->host->unchecked_isa_dma && blk_rq_aligned(q, (unsigned long)hp->dxferp, dxfer_len)) - md = NULL; + md = NULL; /* direct IO activate */ else md = &map_data; if (md) { mutex_lock(&sfp->f_mutex); - if (dxfer_len <= rsv_schp->dlen && - !sfp->res_in_use) { - sfp->res_in_use = true; - sg_link_reserve(sfp, srp, dxfer_len); - } else if (hp->flags & SG_FLAG_MMAP_IO) { - res = -EBUSY; /* sfp->res_in_use == true */ - if (dxfer_len > rsv_schp->dlen) - res = -ENOMEM; - mutex_unlock(&sfp->f_mutex); - return res; - } else { - res = sg_build_indirect(req_schp, sfp, dxfer_len); - if (res) { + if (hp->flags & SG_FLAG_MMAP_IO) { + if (!reserved || dxfer_len > req_schp->dlen) { + res = reserved ? -ENOMEM : -EBUSY; mutex_unlock(&sfp->f_mutex); return res; } + } else if (req_schp->dlen == 0) { + res = sg_mk_sgat_dlen(srp, sfp, dxfer_len); + if (res) { + mutex_unlock(&sfp->f_mutex); + return res; /* will be negated errno */ + } } mutex_unlock(&sfp->f_mutex); @@ -1918,70 +2055,70 @@ sg_start_req(struct sg_request *srp, u8 *cmd) hp->info |= SG_INFO_DIRECT_IO; } } + SG_LOG(6, sdp, "%s: started, %siovec_count=%u\n", __func__, + (md ? "" : "direct_io, "), iov_count); return res; } -static int -sg_finish_rem_req(struct sg_request *srp) +/* clean up mid-level + block layer objects associate with finished request */ +static void +sg_finish_scsi_blk_rq(struct sg_request *srp) { int ret = 0; - struct sg_fd *sfp = srp->parentfp; - struct sg_scatter_hold *req_schp = &srp->data; - SG_LOG(4, sfp->parentdp, "%s: res_used=%d\n", __func__, - (int)srp->res_used); - if (srp->bio) + if (unlikely(!sfp)) + pr_warn("sg: %s: sfp unexpectedly NULL", __func__); + else + SG_LOG(4, sfp->parentdp, "%s: srp=0x%p%s\n", __func__, srp, + (sfp->reserve_srp == srp) ? " reserve" : ""); + if (srp->bio) { ret = blk_rq_unmap_user(srp->bio); - + srp->bio = NULL; + } if (srp->rq) { scsi_req_free_cmd(scsi_req(srp->rq)); blk_put_request(srp->rq); + srp->rq = NULL; } - - if (srp->res_used) - sg_unlink_reserve(sfp, srp); - else - sg_remove_scat(sfp, req_schp); - - return ret; } 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 sgat_arrlen = tablesize * sizeof(struct page *); gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; - schp->pages = kzalloc(sg_bufflen, gfp_flags); - if (!schp->pages) + schp->pages = kzalloc(sgat_arrlen, gfp_flags); + if (unlikely(!schp->pages)) return -ENOMEM; return tablesize; /* number of scat_gath elements allocated */ } +/* Returns 0 for good, otherwise negated errno value */ static int -sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, - int buff_size) +sg_mk_sgat_dlen(struct sg_request *srp, struct sg_fd *sfp, int dlen) { - int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; + int i, k, rem_sz, num, mx_sc_elems, order, align_sz; + int blk_size = dlen; + int ret_sz = 0; int sg_tablesize = sfp->parentdp->sg_tablesize; - int blk_size = buff_size, order; gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; struct sg_device *sdp = sfp->parentdp; + struct sg_scatter_hold *schp = &srp->data; - if (blk_size < 0) + if (unlikely(blk_size < 0)) return -EFAULT; - if (0 == blk_size) - ++blk_size; /* don't know why */ + if (unlikely(blk_size == 0)) + ++blk_size; /* don't remember why */ /* round request up to next highest SG_SECTOR_SZ byte boundary */ - blk_size = ALIGN(blk_size, SG_SECTOR_SZ); - SG_LOG(4, sfp->parentdp, "%s: buff_size=%d, blk_size=%d\n", - __func__, buff_size, blk_size); + align_sz = ALIGN(blk_size, SG_SECTOR_SZ); + SG_LOG(4, sdp, "%s: dlen=%d, align_sz=%d\n", __func__, dlen, align_sz); /* N.B. ret_sz carried into this block ... */ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); - if (mx_sc_elems < 0) + if (unlikely(mx_sc_elems < 0)) return mx_sc_elems; /* most likely -ENOMEM */ num = scatter_elem_sz; @@ -1993,22 +2130,22 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, scatter_elem_sz_prev = num; } - if (sdp->device->host->unchecked_isa_dma) + if (sdp && sdp->device->host->unchecked_isa_dma) gfp_mask |= GFP_DMA; 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) { + for (k = 0, rem_sz = align_sz; 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); if (!schp->pages[k]) - goto out; + goto err_out; if (num == scatter_elem_sz_prev) { if (unlikely(ret_sz > scatter_elem_sz_prev)) { @@ -2017,20 +2154,20 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, } } - SG_LOG(5, sfp->parentdp, "%s: k=%d, num=%d, ret_sz=%d\n", - __func__, k, num, ret_sz); - } /* end of for loop */ + SG_LOG(5, sdp, "%s: k=%d, num=%d, ret_sz=%d\n", __func__, k, + num, ret_sz); + } /* end of for loop */ schp->page_order = order; schp->num_sgat = k; - SG_LOG(5, sfp->parentdp, "%s: num_sgat=%d, rem_sz=%d\n", __func__, k, - rem_sz); - - schp->dlen = blk_size; - if (rem_sz > 0) /* must have failed */ + SG_LOG(5, sdp, "%s: num_sgat=%d, rem_sz=%d\n", __func__, k, rem_sz); + if (unlikely(rem_sz > 0)) /* must have failed */ return -ENOMEM; + schp->dlen = align_sz; + if (sfp->tot_fd_thresh > 0) + atomic_add(align_sz, &sfp->sum_fd_dlens); return 0; -out: +err_out: for (i = 0; i < k; i++) __free_pages(schp->pages[i], order); @@ -2040,138 +2177,175 @@ sg_build_indirect(struct sg_scatter_hold *schp, struct sg_fd *sfp, return -ENOMEM; } +/* Remove the data (possibly a sgat list) held by srp, not srp itself */ static void -sg_remove_scat(struct sg_fd *sfp, struct sg_scatter_hold *schp) +sg_remove_sgat(struct sg_request *srp) { - SG_LOG(4, sfp->parentdp, "%s: num_sgat=%d\n", __func__, - schp->num_sgat); - if (schp->pages) { - if (!schp->dio_in_use) { - int k; - - for (k = 0; k < schp->num_sgat && schp->pages[k]; k++) { - SG_LOG(5, sfp->parentdp, "%s: k=%d, pg=0x%p\n", - __func__, k, schp->pages[k]); - __free_pages(schp->pages[k], schp->page_order); - } + int k; + void *p; + struct sg_scatter_hold *schp = &srp->data; + struct sg_fd *sfp = srp->parentfp; + struct sg_device *sdp; + + sdp = (sfp ? sfp->parentdp : NULL); + SG_LOG(4, sdp, "%s: num_sgat=%d%s\n", __func__, schp->num_sgat, + (srp->parentfp ? (srp == sfp->reserve_srp) : false) ? + " [reserve]" : ""); + if (schp->pages && !schp->dio_in_use) { + for (k = 0; k < schp->num_sgat; ++k) { + p = schp->pages[k]; + SG_LOG(5, sdp, "%s: pg[%d]=0x%p\n", __func__, k, p); + if (unlikely(!p)) + continue; + __free_pages(p, schp->page_order); + } + SG_LOG(5, sdp, "%s: pgs=0x%p\n", __func__, schp->pages); + } + if (sfp->tot_fd_thresh > 0) { + int dl = srp->data.dlen; - kfree(schp->pages); + /* this is a subtraction, error if it goes negative */ + if (atomic_add_negative(-dl, &sfp->sum_fd_dlens)) { + SG_LOG(2, sfp->parentdp, + "%s: logic error: this dlen > %s\n", + __func__, "sum_fd_dlens"); + atomic_set(&sfp->sum_fd_dlens, 0); } } memset(schp, 0, sizeof(*schp)); } +/* + * 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. Return 0 if okay, else + * a negated errno value. + */ static int -sg_read_oxfer(struct sg_request *srp, char __user *outp, int num_read_xfer) +sg_read_oxfer(struct sg_request *srp, char __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->parentdp, "%s: num_read_xfer=%d\n", __func__, - num_read_xfer); - if ((!outp) || (num_read_xfer <= 0)) - return 0; + SG_LOG(4, srp->parentfp->parentdp, "%s: num_xfer=%d\n", __func__, + num_xfer); + 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++) { - if (num > num_read_xfer) { - if (__copy_to_user(outp, page_address(schp->pages[k]), - num_read_xfer)) - return -EFAULT; + 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(pgp), num_xfer)) + res = -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) + 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(4, sfp->parentdp, "%s: req_size=%d\n", __func__, req_size); - do { - if (req_size < PAGE_SIZE) - req_size = PAGE_SIZE; - if (0 == sg_build_indirect(schp, sfp, req_size)) - return; - else - sg_remove_scat(sfp, schp); - req_size >>= 1; /* divide by 2 */ - } while (req_size > (PAGE_SIZE / 2)); + return res; } -static void -sg_link_reserve(struct sg_fd *sfp, struct sg_request *srp, int size) +static struct sg_request * +sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id) { - struct sg_scatter_hold *req_schp = &srp->data; - struct sg_scatter_hold *rsv_schp = &sfp->reserve; - int k, num, rem; - - srp->res_used = true; - SG_LOG(4, sfp->parentdp, "%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->pages = rsv_schp->pages; + struct sg_request *srp; + unsigned long iflags; - req_schp->dlen = size; - req_schp->page_order = rsv_schp->page_order; - break; - } else - rem -= num; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { + spin_lock(&srp->rq_entry_lck); + /* look for requests that are ready + not SG_IO owned */ + if ((srp->rq_state == SG_RQ_AWAIT_READ) && !srp->sync_invoc && + (pack_id == -1 || srp->header.pack_id == pack_id)) { + /* guard against other readers */ + srp->rq_state = SG_RQ_DONE_READ; + spin_unlock(&srp->rq_entry_lck); + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return srp; + } + spin_unlock(&srp->rq_entry_lck); } - - if (k >= rsv_schp->num_sgat) - SG_LOG(1, sfp->parentdp, "%s: BAD size\n", __func__); + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return NULL; } -static void -sg_unlink_reserve(struct sg_fd *sfp, struct sg_request *srp) +/* If rwlp and iflagsp non-NULL then release and re-take write lock */ +static struct sg_request * +sg_mk_srp(struct sg_fd *sfp, bool first, rwlock_t *rwlp, + unsigned long *iflagsp) { - struct sg_scatter_hold *req_schp = &srp->data; + struct sg_request *srp; + int gfp = __GFP_NOWARN; - SG_LOG(4, srp->parentfp->parentdp, "%s: req->num_sgat=%d\n", __func__, - (int)req_schp->num_sgat); - req_schp->num_sgat = 0; - req_schp->dlen = 0; - req_schp->pages = NULL; - req_schp->page_order = 0; - srp->res_used = false; - /* Called without mutex lock to avoid deadlock */ - sfp->res_in_use = false; + if (first) { /* prepared to wait if none already outstanding */ + if (rwlp && iflagsp) { + write_unlock_irqrestore(rwlp, *iflagsp); + srp = kzalloc(sizeof(*srp), gfp | GFP_KERNEL); + write_lock_irqsave(rwlp, *iflagsp); + } else + srp = kzalloc(sizeof(*srp), gfp | GFP_KERNEL); + } else + srp = kzalloc(sizeof(*srp), gfp | GFP_ATOMIC); + if (srp) { + spin_lock_init(&srp->rq_entry_lck); + srp->rq_state = SG_RQ_INACTIVE; + srp->parentfp = sfp; + return srp; + } else + return ERR_PTR(-ENOMEM); } +/* + * Irrespective of the given reserve buffer size, the minimum size requested + * will be PAGE_SIZE (often that is 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 dlen) is in srp->data.dlen . + * Note that this function is only called in contexts where locking is + * not required. + */ static struct sg_request * -sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id) +sg_build_reserve(struct sg_fd *sfp, int dlen) { - struct sg_request *resp; - unsigned long iflags; + bool go_out = false; + int res; + struct sg_device *sdp = sfp ? sfp->parentdp : NULL; + struct sg_request *srp; - write_lock_irqsave(&sfp->rq_list_lock, iflags); - list_for_each_entry(resp, &sfp->rq_list, entry) { - /* look for requests that are ready + not SG_IO owned */ - if ((1 == resp->done) && (!resp->sg_io_owned) && - ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { - resp->done = 2; /* guard against other readers */ - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + SG_LOG(3, sdp, "%s: dlen=%d\n", __func__, dlen); + srp = sg_mk_srp(sfp, list_empty(&sfp->rq_free_list), NULL, NULL); + if (IS_ERR(srp)) + return srp; + sfp->reserve_srp = srp; + do { + if (dlen < PAGE_SIZE) { + dlen = PAGE_SIZE; + go_out = true; } - } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return NULL; + res = sg_mk_sgat_dlen(srp, sfp, dlen); + if (res == 0) { + SG_LOG(4, sdp, "%s: final dlen=%d, srp=0x%p\n", + __func__, dlen, srp); + return srp; + } + if (go_out) + return ERR_PTR(res); + /* failed so remove, halve dlen, try again */ + sg_remove_sgat(srp); + dlen >>= 1; /* divide by 2 */ + } while (true); } /* @@ -2181,33 +2355,85 @@ sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id) * negated errno value twisted by ERR_PTR() macro. */ static struct sg_request * -sg_add_request(struct sg_fd *sfp) +sg_add_request(struct sg_fd *sfp, int dxfr_len, bool sync) { - int k; + bool done = false; + u32 sum_dlen; unsigned long iflags; - struct sg_request *rp = sfp->req_arr; + struct sg_request *srp = NULL; + struct sg_device *sdp; + const char *cp = "fail"; write_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; + sdp = sfp->parentdp; + if (!list_empty(&sfp->rq_free_list)) { + /* when no data xfer, take last if not reserve request */ + if (dxfr_len < 1) { + srp = list_last_entry(&sfp->rq_free_list, + struct sg_request, free_entry); + spin_lock(&srp->rq_entry_lck); + if (srp->rq_state == SG_RQ_INACTIVE && + sfp->reserve_srp != srp) { + srp->rq_state = SG_RQ_BUSY; + cp = "re-using last in fl"; + done = true; + } else + spin_unlock(&srp->rq_entry_lck); + } else { /* find request with large enough dlen */ + list_for_each_entry(srp, &sfp->rq_free_list, + free_entry) { + spin_lock(&srp->rq_entry_lck); + if (srp->rq_state == SG_RQ_INACTIVE && + srp->data.dlen >= dxfr_len) { + srp->rq_state = SG_RQ_BUSY; + cp = "re-using from start of fl"; + done = true; + break; + } + spin_unlock(&srp->rq_entry_lck); + } } - if (k >= SG_MAX_QUEUE) - goto out_unlock; + if (done) { + list_del(&srp->free_entry); + /* re-using request, may sure it's clean */ + srp->orphan = false; + srp->v4_active = false; + srp->rq_state = SG_RQ_INACTIVE; + srp->d2p = NULL; + } else + srp = NULL; } - memset(rp, 0, sizeof(*rp)); - 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); - return rp; -out_unlock: + if (!done) { /* Need new sg_request object */ + bool empty = list_empty(&sfp->rq_list); + + if (!sfp->cmd_q && !empty) { + srp = ERR_PTR(-EDOM); + SG_LOG(6, sdp, "%s: cmd_q false, trying second rq\n", + __func__); + goto out_wr_unlock; + } + if (sfp->tot_fd_thresh > 0) { + sum_dlen = atomic_read(&sfp->sum_fd_dlens) + dxfr_len; + if (sum_dlen > sfp->tot_fd_thresh) { + srp = ERR_PTR(-E2BIG); + SG_LOG(2, sdp, "%s: sum_of_dlen(%u) > %s\n", + __func__, sum_dlen, "tot_fd_thresh"); + goto out_wr_unlock; + } + } + srp = sg_mk_srp(sfp, empty, &sfp->rq_list_lock, &iflags); + if (IS_ERR(srp)) + goto out_wr_unlock; + cp = "new"; + } + srp->sync_invoc = sync; + if (done) + spin_unlock(&srp->rq_entry_lck); + list_add_tail(&srp->rq_entry, &sfp->rq_list); +out_wr_unlock: write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return NULL; + SG_LOG(6, sdp, "%s: %s srp=0x%p\n", __func__, cp, srp); + return srp; } /* @@ -2218,38 +2444,88 @@ sg_add_request(struct sg_fd *sfp) * data length exceeds rem_sgat_thresh then the data (or sgat) is * cleared and the request is appended to the tail of the free list. */ -static int +static void sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) { + bool reserve, done; unsigned long iflags; - int res = 0; + struct sg_request *t_srp; + const char *cp = "head"; + char b[64]; - if (!sfp || !srp || list_empty(&sfp->rq_list)) - return res; + if (WARN_ON(!sfp || !srp)) + return; write_lock_irqsave(&sfp->rq_list_lock, iflags); - if (!list_empty(&srp->entry)) { - list_del(&srp->entry); - srp->parentfp = NULL; - res = 1; - } + spin_lock(&srp->rq_entry_lck); + /* + * N.B. sg_request object is not de-allocated (freed). The contents + * of the rq_list and rq_free_list lists are de-allocated (freed) when + * the owning file descriptor is closed. The free list acts as a LIFO + * for same size (dlen) sg_request objects. This can improve the + * chance of a cache hit when the sg_request object is re-used. + */ + reserve = (sfp->reserve_srp == srp); + if (reserve || srp->data.dlen <= sfp->rem_sgat_thresh) { + list_del(&srp->rq_entry); + /* want ascending free list by dlen, but dlen==0 at end */ + done = false; + if (srp->data.dlen > 0) { + list_for_each_entry(t_srp, &sfp->rq_free_list, + free_entry) { + if (srp->data.dlen <= t_srp->data.dlen || + t_srp->data.dlen == 0) { + /* add srp _before_ t_srp on fl */ + list_add_tail(&srp->free_entry, + &t_srp->free_entry); + done = true; + break; + } + } + } + if (!done) { /* either empty list, dlen=0 or dlen highest */ + list_add_tail(&srp->free_entry, &sfp->rq_free_list); + cp = "tail"; + } + snprintf(b, sizeof(b), "%ssrp=0x%p move to fl %s", + (reserve ? "reserve " : ""), srp, cp); + } else { + srp->rq_state = SG_RQ_BUSY; + list_del(&srp->rq_entry); + spin_unlock(&srp->rq_entry_lck); + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + sg_remove_sgat(srp); + /* don't kfree(srp), move clear request to tail of fl */ + write_lock_irqsave(&sfp->rq_list_lock, iflags); + spin_lock(&srp->rq_entry_lck); + list_add_tail(&srp->free_entry, &sfp->rq_free_list); + snprintf(b, sizeof(b), "clear sgat srp=0x%p move to fl tail", + srp); + } + srp->rq_state = SG_RQ_INACTIVE; + spin_unlock(&srp->rq_entry_lck); write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return res; + SG_LOG(5, sfp->parentdp, "%s: %s\n", __func__, b); } static struct sg_fd * sg_add_sfp(struct sg_device *sdp) { - struct sg_fd *sfp; + bool reduced = false; + int dlen; unsigned long iflags; - int bufflen; + long err; + struct sg_fd *sfp; + struct sg_request *srp; sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); - if (!sfp) + if (!sfp) { + SG_LOG(1, sdp, "%s: sfp allocation failed\n", __func__); return ERR_PTR(-ENOMEM); - + } init_waitqueue_head(&sfp->read_wait); rwlock_init(&sfp->rq_list_lock); INIT_LIST_HEAD(&sfp->rq_list); + INIT_LIST_HEAD(&sfp->rq_free_list); kref_init(&sfp->f_ref); mutex_init(&sfp->f_mutex); sfp->timeout = SG_DEFAULT_TIMEOUT; @@ -2257,27 +2533,50 @@ 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; + dlen = (def_reserved_size > SG_RQ_DATA_THRESHOLD) ? + def_reserved_size : SG_RQ_DATA_THRESHOLD; + sfp->rem_sgat_thresh = dlen; + dlen *= 2; + sfp->tot_fd_thresh = (dlen > SG_TOT_FD_THRESHOLD) ? + dlen : SG_TOT_FD_THRESHOLD; + atomic_set(&sfp->sum_fd_dlens, 0); + sfp->time_in_ns = !!SG_DEF_TIME_UNIT; sfp->parentdp = sdp; - write_lock_irqsave(&sdp->sfd_lock, iflags); if (atomic_read(&sdp->detaching)) { - write_unlock_irqrestore(&sdp->sfd_lock, iflags); kfree(sfp); + SG_LOG(1, sdp, "%s: detaching\n", __func__); return ERR_PTR(-ENODEV); } - list_add_tail(&sfp->sfd_siblings, &sdp->sfds); - write_unlock_irqrestore(&sdp->sfd_lock, iflags); - SG_LOG(3, sdp, "%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, + dlen = min_t(int, sg_big_buff, max_sectors_bytes(sdp->device->request_queue)); - sg_build_reserve(sfp, bufflen); - SG_LOG(3, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__, - sfp->reserve.dlen, sfp->reserve.num_sgat); - + if (dlen > 0) { + srp = sg_build_reserve(sfp, dlen); + if (IS_ERR(srp)) { + kfree(sfp); + err = PTR_ERR(srp); + SG_LOG(1, sdp, "%s: build reserve err=%ld\n", __func__, + -err); + return ERR_PTR(err); + } + if (srp->data.dlen < dlen) { + reduced = true; + SG_LOG(2, sdp, + "%s: reserve reduced from %d to dlen=%d\n", + __func__, dlen, srp->data.dlen); + } + list_add_tail(&srp->free_entry, &sfp->rq_free_list); + } + if (!reduced) + SG_LOG(4, sdp, "%s: built reserve dlen=%d\n", __func__, dlen); + write_lock_irqsave(&sdp->sfd_lock, iflags); + list_add_tail(&sfp->sfd_entry, &sdp->sfds); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); + write_unlock_irqrestore(&sdp->sfd_lock, iflags); + SG_LOG(3, sdp, "%s: sfp=0x%p success\n", __func__, sfp); return sfp; } @@ -2295,31 +2594,35 @@ 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; struct sg_request *srp; - unsigned long iflags; + const char *cp = " srp=0x"; /* 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, struct sg_request, - entry); - sg_finish_rem_req(srp); - list_del(&srp->entry); - srp->parentfp = NULL; - } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - - if (sfp->reserve.dlen > 0) { - SG_LOG(6, sdp, "%s: dlen=%d, num_sgat=%d\n", __func__, - (int)sfp->reserve.dlen, - (int)sfp->reserve.num_sgat); - sg_remove_scat(sfp, &sfp->reserve); + srp = list_last_entry(&sfp->rq_list, struct sg_request, + rq_entry); + sg_finish_scsi_blk_rq(srp); + list_del(&srp->rq_entry); + if (srp->data.dlen > 0) + sg_remove_sgat(srp); + SG_LOG(6, sdp, "%s:%s%p\n", __func__, cp, srp); + kfree(srp); + } + while (!list_empty(&sfp->rq_free_list)) { + srp = list_last_entry(&sfp->rq_free_list, struct sg_request, + free_entry); + list_del(&srp->free_entry); + if (srp->data.dlen > 0) + sg_remove_sgat(srp); + SG_LOG(6, sdp, "%s: free list%s%p\n", __func__, cp, srp); + kfree(srp); } - SG_LOG(6, sdp, "%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); } @@ -2331,7 +2634,7 @@ 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); @@ -2341,6 +2644,7 @@ sg_remove_sfp(struct kref *kref) #ifdef CONFIG_SCSI_PROC_FS static int sg_idr_max_id(int id, void *p, void *data) + __must_hold(&sg_index_lock) { int *k = data; @@ -2363,9 +2667,9 @@ sg_last_dev(void) } #endif -/* must be called with sg_index_lock held */ static struct sg_device * sg_lookup_dev(int dev) + __must_hold(&sg_index_lock) { return idr_find(&sg_index_idr, dev); } @@ -2375,13 +2679,13 @@ sg_lookup_dev(int dev) * errno value on failure. Does not return NULL. */ static struct sg_device * -sg_get_dev(int dev) +sg_get_dev(int min_dev) { struct sg_device *sdp; unsigned long flags; read_lock_irqsave(&sg_index_lock, flags); - sdp = sg_lookup_dev(dev); + sdp = sg_lookup_dev(min_dev); if (!sdp) sdp = ERR_PTR(-ENXIO); else if (atomic_read(&sdp->detaching)) { @@ -2475,6 +2779,7 @@ sg_proc_init(void) return 0; } + static int sg_proc_seq_show_int(struct seq_file *s, void *v) { From patchwork Fri Oct 26 11:48:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657289 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 041DD14BD for ; Fri, 26 Oct 2018 11:48:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E9E792BE95 for ; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DE59E2BE97; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 030482BEA1 for ; Fri, 26 Oct 2018 11:48:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727590AbeJZUZd (ORCPT ); Fri, 26 Oct 2018 16:25:33 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56710 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727555AbeJZUZc (ORCPT ); Fri, 26 Oct 2018 16:25:32 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id 16EAF20423A; Fri, 26 Oct 2018 13:48:43 +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 hDOhbyv30S83; Fri, 26 Oct 2018 13:48:40 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id E00BB20419B; Fri, 26 Oct 2018 13:48:35 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 6/8] sg: complete locking changes on ioctl+debug Date: Fri, 26 Oct 2018 12:48:28 +0100 Message-Id: <20181026114830.13506-7-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Complete the locking and structure changes of ioctl and debug ('cat /proc/scsi/sg/debug') handling. Signed-off-by: Douglas Gilbert --- This was the code that was "#if 0'-ed out 2 patches ago. It also shuts checkpatch.pl up as it doesn't like that technique but offers no viable substitute. drivers/scsi/sg.c | 217 +++++++++++++++++++++++++++++----------------- 1 file changed, 136 insertions(+), 81 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 5fbdb0f40739..8b4a65340971 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -239,6 +239,7 @@ static struct sg_request *sg_add_request(struct sg_fd *sfp, int dxfr_len, static void sg_remove_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); +static const char *sg_rq_state_str(u8 rq_state, bool long_str); #define SZ_SG_HEADER sizeof(struct sg_header) /* v1 and v2 header */ #define SZ_SG_IO_HDR sizeof(struct sg_io_hdr) /* v3 header */ @@ -359,7 +360,7 @@ sg_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) - 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); if (IS_ERR(sdp)) return PTR_ERR(sdp); @@ -931,11 +932,6 @@ sg_ktime_sub_trunc(ktime_t now_ts, ktime_t ts0) return 0; } -/* - * Annotation under function arguments (i.e. '__must_hold...') states that - * this function expects that lock to be held, a read lock is sufficient in - * this case. - */ static void sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, struct sg_req_info *rip) @@ -982,7 +978,6 @@ sg_fill_request_element(struct sg_fd *sfp, struct sg_request *srp, * Populate up to max_num struct sg_req_info objects, first from the active * list then, if there is still room, from the free list. All elements in * the free list should have SG_RQ_INACTIVE status. - * See sg_fill_request_element() for note about __must_hold annotation. */ static void sg_fill_request_table(struct sg_fd *sfp, struct sg_req_info *rinfo, @@ -1031,7 +1026,6 @@ srp_state_or_detaching(struct sg_device *sdp, struct sg_request *srp) return ret; } -#if 0 /* temporary to shorten big patch */ static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -1063,24 +1057,37 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENXIO; if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) return -EFAULT; - result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, - 1, read_only, 1, &srp); + result = sg_v3_write(sfp, filp, p, SZ_SG_IO_HDR, read_only, + true, &srp); if (result < 0) return result; result = wait_event_interruptible(sfp->read_wait, - (srp_done(sfp, srp) || atomic_read(&sdp->detaching))); - if (atomic_read(&sdp->detaching)) + srp_state_or_detaching(sdp, srp)); + + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + if (unlikely(result)) { /* -ERESTARTSYS because signal hit */ + srp->orphan = true; + srp->rq_state = SG_RQ_INFLIGHT; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + SG_LOG(1, sdp, "%s: wait_event_interruptible-->%d\n", + __func__, result); + return result; + } + if (unlikely(atomic_read(&sdp->detaching))) { + srp->rq_state = SG_RQ_INACTIVE; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); return -ENODEV; - write_lock_irq(&sfp->rq_list_lock); - if (srp->done) { - srp->done = 2; - write_unlock_irq(&sfp->rq_list_lock); + } else if (likely(srp->rq_state == SG_RQ_AWAIT_READ)) { + srp->rq_state = SG_RQ_DONE_READ; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0; } - srp->orphan = true; - write_unlock_irq(&sfp->rq_list_lock); - return result; /* -ERESTARTSYS because signal hit process */ + cp = sg_rq_state_str(srp->rq_state, true); + SG_LOG(1, sdp, "%s: unexpected srp=0x%p state: %s\n", __func__, + srp, cp); + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + return -EPROTO; /* Logic error */ case SG_SET_TIMEOUT: result = get_user(val, ip); if (result) @@ -1139,25 +1146,36 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!access_ok(VERIFY_WRITE, ip, sizeof(int))) return -EFAULT; read_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, - iflags); - __put_user(srp->header.pack_id, ip); - return 0; + leave = false; + val = -1; + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { + spin_lock(&srp->rq_entry_lck); + if (srp->rq_state == SG_RQ_AWAIT_READ && + !srp->sync_invoc) { + val = srp->header.pack_id; + leave = true; } + spin_unlock(&srp->rq_entry_lck); + if (leave) + break; } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - __put_user(-1, ip); + __put_user(val, ip); + SG_LOG(3, sdp, "%s: SG_GET_PACK_ID=%d\n", __func__, val); return 0; 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)) + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { + spin_lock(&srp->rq_entry_lck); + if (srp->rq_state == SG_RQ_AWAIT_READ && + !srp->sync_invoc) ++val; + spin_unlock(&srp->rq_entry_lck); } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + SG_LOG(3, sdp, "%s: SG_GET_NUM_WAITING=%d\n", __func__, + val); return put_user(val, ip); case SG_GET_SG_TABLESIZE: return put_user(sdp->sg_tablesize, ip); @@ -1169,21 +1187,32 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -EINVAL; val = min_t(int, val, max_sectors_bytes(sdp->device->request_queue)); - mutex_lock(&sfp->f_mutex); - if (val != sfp->reserve.dlen) { - if (sfp->mmap_called || - sfp->res_in_use) { - mutex_unlock(&sfp->f_mutex); - return -EBUSY; + srp = sfp->reserve_srp; + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + if (srp->rq_state != SG_RQ_INACTIVE) { + result = -EBUSY; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + return result; + } else if (val != srp->data.dlen) { + if (sfp->mmap_called) { + result = -EBUSY; + spin_unlock_irqrestore(&srp->rq_entry_lck, + iflags); + return result; } - - sg_remove_scat(sfp, &sfp->reserve); - sg_build_reserve(sfp, val); - } - mutex_unlock(&sfp->f_mutex); - return 0; + srp->rq_state = SG_RQ_BUSY; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + sg_remove_sgat(srp); + if (val > 0) + result = sg_mk_sgat_dlen(srp, sfp, val); + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + srp->rq_state = SG_RQ_INACTIVE; + } else + result = 0; /* nothing needs to change */ + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + return result; case SG_GET_RESERVED_SIZE: - val = min_t(int, sfp->reserve.dlen, + val = min_t(int, sfp->reserve_srp->data.dlen, max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q: @@ -1227,7 +1256,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (!rinfo) return -ENOMEM; read_lock_irqsave(&sfp->rq_list_lock, iflags); - sg_fill_request_table(sfp, rinfo); + sg_fill_request_table(sfp, rinfo, SG_MAX_QUEUE); read_unlock_irqrestore(&sfp->rq_list_lock, iflags); result = __copy_to_user(p, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); @@ -1311,7 +1340,6 @@ sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENOIOCTLCMD; } #endif -#endif /* temporary to shorten big patch */ static __poll_t sg_poll(struct file *filp, poll_table * wait) @@ -1519,6 +1547,8 @@ sg_rq_end_io_usercontext(struct work_struct *work) WARN_ONCE(1, "%s: sfp unexpectedly NULL\n", __func__); return; } + SG_LOG(3, sfp->parentdp, "%s: clean srp=0x%p, rq_state: %s\n", + __func__, srp, sg_rq_state_str(srp->rq_state, true)); sg_finish_scsi_blk_rq(srp); sg_remove_request(sfp, srp); kref_put(&sfp->f_ref, sg_remove_sfp); @@ -1644,12 +1674,10 @@ static const struct file_operations sg_fops = { .read = sg_read, .write = sg_write, .poll = sg_poll, -#if 0 /* temporary to shorten big patch */ .unlocked_ioctl = sg_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sg_compat_ioctl, #endif -#endif /* temporary to shorten big patch */ .open = sg_open, .mmap = sg_mmap, .release = sg_release, @@ -2748,16 +2776,12 @@ static const struct seq_operations devstrs_seq_ops = { .show = sg_proc_seq_show_devstrs, }; -#if 0 /* temporary to shorten big patch */ static int sg_proc_seq_show_debug(struct seq_file *s, void *v); -#endif /* temporary to shorten big patch */ static const struct seq_operations debug_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, -#if 0 /* temporary to shorten big patch */ .show = sg_proc_seq_show_debug, -#endif /* temporary to shorten big patch */ }; static int @@ -2932,37 +2956,64 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) return 0; } -#if 0 /* temporary to shorten big patch */ +static const char * +sg_rq_state_str(u8 rq_state, bool long_str) +{ + switch (rq_state) { + case SG_RQ_INACTIVE: + return long_str ? "inactive" : "ina"; + case SG_RQ_INFLIGHT: + return long_str ? "inflight" : "act"; + case SG_RQ_AWAIT_READ: + return long_str ? "await_read" : "rcv"; + case SG_RQ_DONE_READ: + return long_str ? "read_done" : "fin"; + case SG_RQ_BUSY: + return long_str ? "busy" : "bsy"; + default: + return long_str ? "unknown" : "unk"; + } +} /* must be called while holding sg_index_lock */ static void sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) + __must_hold(&sg_index_lock) + __must_hold(&sdp->sfd_lock) { - int k, new_interface, blen, usg; + int k, dlen, usg, dur, to; + u32 pack_id; + unsigned int ms; + u8 rq_st, opcode; + bool new_ifc; + bool dur_valid; struct sg_request *srp; struct sg_fd *fp; const struct sg_io_hdr *hp; - const char * cp; - unsigned int ms; + const char *cp; + const char *csp; k = 0; - list_for_each_entry(fp, &sdp->sfds, sfd_siblings) { - k++; + 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 dlen=%d " - "(res)sgat=%d low_dma=%d\n", k, - jiffies_to_msecs(fp->timeout), - fp->reserve.dlen, - (int)fp->reserve.num_sgat, + /* sgat=-1 means unavailable */ + seq_printf(s, " FD(%d): timeout=%dms dlen=%d%slow_dma=%d\n", + k, jiffies_to_msecs(fp->timeout), + fp->reserve_srp->data.dlen, " (res)sgat=-1 ", (int)sdp->device->host->unchecked_isa_dma); 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); - list_for_each_entry(srp, &fp->rq_list, entry) { + seq_printf(s, " sse_seen=%d mmap_called=%d sum_fd_dlens=%u\n", + (int)fp->sse_seen, (int)fp->mmap_called, + atomic_read(&fp->sum_fd_dlens)); + list_for_each_entry(srp, &fp->rq_list, rq_entry) { + spin_lock(&srp->rq_entry_lck); hp = &srp->header; - new_interface = (hp->interface_id == '\0') ? 0 : 1; - if (srp->res_used) { - if (new_interface && + new_ifc = !(hp->interface_id == '\0'); + if (srp->parentfp->reserve_srp == srp) { + if (new_ifc && (SG_FLAG_MMAP_IO & hp->flags)) cp = " mmap>> "; else @@ -2973,25 +3024,31 @@ sg_proc_debug_helper(struct seq_file *s, struct sg_device *sdp) else cp = " "; } - seq_puts(s, cp); - blen = srp->data.dlen; + dlen = srp->data.dlen; 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 { + rq_st = srp->rq_state; + dur = hp->duration; + to = fp->timeout; + pack_id = (u32)srp->header.pack_id; + opcode = srp->data.cmd_opcode; + spin_unlock(&srp->rq_entry_lck); + csp = sg_rq_state_str(rq_st, false); + dur_valid = (rq_st == SG_RQ_AWAIT_READ || + rq_st == SG_RQ_DONE_READ); + seq_printf(s, "%s%s: id=%u dlen=%d", cp, csp, pack_id, + dlen); + if (dur_valid) + seq_printf(s, " dur=%d", dur); + else if (rq_st == SG_RQ_INFLIGHT) { ms = jiffies_to_msecs(jiffies); + if (dur == 0) /* hp->duration may not set */ + dur = ms; /* causes elap=0 */ seq_printf(s, " t_o/elap=%d/%d", - (new_interface ? hp->timeout : - jiffies_to_msecs(fp->timeout)), - (ms > hp->duration ? ms - hp->duration : 0)); + (new_ifc ? to : jiffies_to_msecs(to)), + (ms > dur ? ms - dur : 0)); } - seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, - (int)srp->data.cmd_opcode); + seq_printf(s, "%ss sgat=%d op=0x%02x\n", + (fp->time_in_ns ? "n" : "m"), usg, opcode); } if (list_empty(&fp->rq_list)) seq_puts(s, " No requests active\n"); @@ -3037,8 +3094,6 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } -#endif /* temporary to shorten big patch */ - #endif /* CONFIG_SCSI_PROC_FS */ module_init(init_sg); From patchwork Fri Oct 26 11:48:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657291 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5F3C21751 for ; Fri, 26 Oct 2018 11:48:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4B3B32BF83 for ; Fri, 26 Oct 2018 11:48:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3C45F2BEC7; Fri, 26 Oct 2018 11:48:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AB4822BE95 for ; Fri, 26 Oct 2018 11:48:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727595AbeJZUZf (ORCPT ); Fri, 26 Oct 2018 16:25:35 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56719 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727491AbeJZUZf (ORCPT ); Fri, 26 Oct 2018 16:25:35 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id DB61C20417C; Fri, 26 Oct 2018 13:48: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 YlnwBJkPD62B; Fri, 26 Oct 2018 13:48:43 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id 72C8120423D; Fri, 26 Oct 2018 13:48:36 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 7/8] sg: rework ioctl handling Date: Fri, 26 Oct 2018 12:48:29 +0100 Message-Id: <20181026114830.13506-8-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Rework ioctl handling, report clearly to the log which ioctl has been invoked. Add a new "IOWR" ioctl: SG_SET_GET_EXTENDED which permits several integer and boolean values to be "_SET_" (i.e. passed into the driver, potentially changing its actions) and/or read from the driver (the "_GET_" part) in a single operation. Signed-off-by: Douglas Gilbert --- One feature of the new SG_SET_GET_EXTENDED ioctl is ability to fetch the sg device minor number (e.g. the "3" in /dev/sg3) associated with the current file descriptor. A boolean addition is the ability to change command timekeeping on the current file descriptor from units of milliseconds (the default) to nanoseconds. drivers/scsi/sg.c | 535 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 409 insertions(+), 126 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 8b4a65340971..3e89bbd508de 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -90,8 +90,8 @@ enum sg_rq_state { /* If sum_of(dlen) of a fd exceeds this, write() will yield E2BIG */ #define SG_TOT_FD_THRESHOLD (16 * 1024 * 1024) -#define SG_TIME_UNIT_MS 0 /* milliseconds */ -#define SG_TIME_UNIT_NS 1 /* nanoseconds */ +#define SG_TIME_UNIT_MS 0 /* milliseconds */ +#define SG_TIME_UNIT_NS 1 /* nanoseconds */ #define SG_DEF_TIME_UNIT SG_TIME_UNIT_MS #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) @@ -186,7 +186,6 @@ struct sg_fd { /* holds the state of a file descriptor */ bool cmd_q; /* true -> allow command queuing, false -> don't */ bool keep_orphan;/* false -> drop (def), true -> keep for read() */ bool mmap_called; /* false -> mmap() never called on this fd */ - bool sse_seen; /* SG_SET_EXTENDED ioctl seen */ bool time_in_ns; /* report times in nanoseconds */ u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ struct sg_request *reserve_srp; /* allocate on open(), starts on fl */ @@ -240,6 +239,8 @@ static void sg_remove_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); static const char *sg_rq_state_str(u8 rq_state, bool long_str); +static struct sg_request *sg_mk_srp(struct sg_fd *sfp, bool first, + rwlock_t *rwlp, unsigned long *iflagsp); #define SZ_SG_HEADER sizeof(struct sg_header) /* v1 and v2 header */ #define SZ_SG_IO_HDR sizeof(struct sg_io_hdr) /* v3 header */ @@ -508,9 +509,9 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; } sdp = sfp->parentdp; + SG_LOG(3, sdp, "%s: read() count=%d\n", __func__, (int)count); if (IS_ERR_OR_NULL(sdp)) return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; - SG_LOG(3, sdp, "%s: read() count=%d\n", __func__, (int)count); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -815,11 +816,11 @@ sg_v3_write(struct sg_fd *sfp, struct file *file, const char __user *buf, return -ENOSYS; if (hp->flags & SG_FLAG_MMAP_IO) { if (!list_empty(&sfp->rq_list)) - return -EBUSY; /* already active requests on fd */ + return -EBUSY; /* already active requests on fd */ if (hp->dxfer_len > sfp->reserve_srp->data.dlen) - return -ENOMEM; /* MMAP_IO size must fit in reserve */ + return -ENOMEM; /* MMAP_IO size must fit in reserve */ if (hp->flags & SG_FLAG_DIRECT_IO) - return -EINVAL; /* not both MMAP_IO and DIRECT_IO */ + return -EINVAL; /* not both MMAP_IO and DIRECT_IO */ } sfp->cmd_q = true; /* when sg_io_hdr seen, set command queuing on */ ul_timeout = msecs_to_jiffies(hp->timeout); @@ -857,7 +858,7 @@ sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p, srp = sg_add_request(sfp, hi_p->dxfer_len, false); if (IS_ERR(srp)) return srp; - srp->header = *hi_p; /* structure assignment, could memcpy */ + srp->header = *hi_p; /* structure assignment, could memcpy */ hp = &srp->header; srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ hp->status = 0; @@ -1026,69 +1027,300 @@ srp_state_or_detaching(struct sg_device *sdp, struct sg_request *srp) return ret; } +/* For handling ioctl(SG_IO). Returns 0 on success else a negated errno */ +static int +sg_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 result; + unsigned long iflags; + struct sg_request *srp; + const char *cp; + + SG_LOG(3, sdp, "%s: ioctl(SG_IO)\n", __func__); + if (atomic_read(&sdp->detaching)) + return -ENODEV; + if (!scsi_block_when_processing_errors(sdp->device)) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) + return -EFAULT; + result = sg_v3_write(sfp, filp, p, SZ_SG_IO_HDR, read_only, + true, &srp); + if (unlikely(result < 0)) + return result; + /* usually will be woken up by sg_rq_end_io() callback */ + result = wait_event_interruptible(sfp->read_wait, + srp_state_or_detaching(sdp, srp)); + spin_lock_irqsave(&srp->rq_entry_lck, iflags); + if (unlikely(result)) { /* -ERESTARTSYS because signal hit thread */ + srp->orphan = true; + srp->rq_state = SG_RQ_INFLIGHT; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + SG_LOG(1, sdp, "%s: wait_event_interruptible gave %d\n", + __func__, result); + return result; + } + if (unlikely(atomic_read(&sdp->detaching))) { + srp->rq_state = SG_RQ_INACTIVE; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + return -ENODEV; + } else if (likely(srp->rq_state == SG_RQ_AWAIT_READ)) { + srp->rq_state = SG_RQ_DONE_READ; + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); + return (result < 0) ? result : 0; + } + cp = sg_rq_state_str(srp->rq_state, true); + SG_LOG(1, sdp, "%s: unexpected srp=0x%p state: %s\n", __func__, + srp, cp); + spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + return -EPROTO; /* Logic error */ +} + +/* Returns 0 on success, else a negated errno value */ +static int +sg_reserved_sz(struct sg_fd *sfp, struct sg_extended_info *seip) +{ + bool free_n_srp = false; + int result = 0; + int val, mx_sect_bytes; + unsigned long iflags; + struct sg_request *srp; /* prior reserve request */ + struct sg_request *n_srp; /* new sg_request, may be used */ + struct sg_request *flf_srp; /* free list first element */ + struct sg_device *sdp = sfp->parentdp; + + mx_sect_bytes = max_sectors_bytes(sdp->device->request_queue); + if (!(seip->valid_wr_mask & SG_SEIM_RESERVED_SIZE)) { /* read only */ + srp = sfp->reserve_srp; + seip->reserved_sz = (u32)min_t(int, srp->data.dlen, + mx_sect_bytes); + SG_LOG(3, sdp, "%s: rd val=%u\n", __func__, seip->reserved_sz); + return 0; + } + val = min_t(int, (int)seip->reserved_sz, mx_sect_bytes); + SG_LOG(3, sdp, "%s: val=%u modify to %d\n", __func__, + seip->reserved_sz, val); + /* Should sizes less than PAGE_SIZE be permitted? Round up? */ + n_srp = sg_mk_srp(sfp, true, NULL, NULL); + if (IS_ERR(n_srp)) + return PTR_ERR(n_srp); + if (val > 0) { + result = sg_mk_sgat_dlen(n_srp, sfp, val); + if (result) { + kfree(n_srp); + return result; + } + } + /* new sg_request object, sized correctly is now available */ + write_lock_irqsave(&sfp->rq_list_lock, iflags); + srp = sfp->reserve_srp; + spin_lock(&srp->rq_entry_lck); + /* Should not matter if srp->rq_state != SG_RQ_INACTIVE */ + if (sfp->mmap_called) { + result = -EBUSY; + free_n_srp = true; + goto unlock; + } + flf_srp = list_first_entry_or_null(&sfp->rq_free_list, + struct sg_request, free_entry); + if (flf_srp && flf_srp != srp && val <= flf_srp->data.dlen) { + spin_lock(&flf_srp->rq_entry_lck); + if (flf_srp->rq_state == SG_RQ_INACTIVE) { + free_n_srp = true; + sfp->reserve_srp = flf_srp; + } + spin_unlock(&flf_srp->rq_entry_lck); + } + if (!free_n_srp) { + sfp->reserve_srp = n_srp; + list_add(&n_srp->free_entry, &sfp->rq_free_list); + } + if (seip->valid_rd_mask & SG_SEIM_RESERVED_SIZE) { + srp = sfp->reserve_srp; + seip->reserved_sz = (u32)min_t(int, srp->data.dlen, + mx_sect_bytes); + } +unlock: + spin_unlock(&srp->rq_entry_lck); + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + if (free_n_srp) { + sg_remove_sgat(n_srp); + kfree(n_srp); /* no-one else has seen n_srp, so safe */ + } + return result; +} + +/* Returns 0 on success, else a negated errno value */ +static int +sg_set_reserved_sz(struct sg_fd *sfp, int val) +{ + struct sg_extended_info *seip; + struct sg_extended_info sei; + + seip = &sei; + memset(seip, 0, sizeof(*seip)); + seip->valid_wr_mask = SG_SEIM_RESERVED_SIZE; + seip->reserved_sz = (u32)val; + return sg_reserved_sz(sfp, seip); +} + +static bool +sg_any_persistent_orphans(struct sg_fd *sfp) +{ + bool res = false; + unsigned long iflags; + struct sg_request *srp; + + if (!sfp->keep_orphan) + return false; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + list_for_each_entry(srp, &sfp->rq_list, rq_entry) { + if (srp->orphan) { + res = true; + break; + } + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return res; +} + +static int +sg_set_get_extended(struct sg_fd *sfp, void __user *p) +{ + int result = 0; + u32 uv; + struct sg_device *sdp = sfp->parentdp; + struct sg_extended_info *seip; + struct sg_extended_info sei; + u32 or_masks; + + seip = &sei; + if (!access_ok(VERIFY_READ, p, SZ_SG_EXTENDED_INFO)) + return -EFAULT; + if (__copy_from_user(seip, p, SZ_SG_EXTENDED_INFO)) + return -EFAULT; + or_masks = seip->valid_wr_mask | seip->valid_rd_mask; + if (or_masks == 0) { + SG_LOG(2, sdp, "%s: both masks 0, do nothing\n", __func__); + return 0; + } + SG_LOG(3, sdp, "%s: wr_mask=0x%x rd_mask=0x%x\n", __func__, + seip->valid_wr_mask, seip->valid_rd_mask); + if (or_masks & SG_SEIM_RESERVED_SIZE) + result = sg_reserved_sz(sfp, seip); + if (or_masks & SG_SEIM_RQ_REM_THRESH) { + if (seip->valid_wr_mask & SG_SEIM_RQ_REM_THRESH) { + uv = seip->rq_rem_sgat_thresh; + if (uv < PAGE_SIZE) + uv = PAGE_SIZE; + sfp->rem_sgat_thresh = uv; + } + if (seip->valid_rd_mask & SG_SEIM_RQ_REM_THRESH) + seip->rq_rem_sgat_thresh = sfp->rem_sgat_thresh; + } + if (or_masks & SG_SEIM_TOT_FD_THRESH) { + if (seip->valid_wr_mask & SG_SEIM_TOT_FD_THRESH) { + uv = seip->tot_fd_thresh; + if (uv > 0 && uv < PAGE_SIZE) + uv = PAGE_SIZE; + sfp->tot_fd_thresh = uv; + } + if (seip->valid_rd_mask & SG_SEIM_TOT_FD_THRESH) + seip->tot_fd_thresh = sfp->tot_fd_thresh; + } + if (or_masks & SG_SEIM_CTL_FLAGS) { + /* don't care whether wr or rd mask set in or_mask */ + if (seip->ctl_flags_wr_mask & SG_CTL_FLAGM_TIME_IN_NS) + sfp->time_in_ns = + !!(seip->ctl_flags & SG_CTL_FLAGM_TIME_IN_NS); + if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_TIME_IN_NS) { + if (sfp->time_in_ns) + seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS; + else + seip->ctl_flags &= ~SG_CTL_FLAGM_TIME_IN_NS; + } + if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_ORPHANS) { + if (sg_any_persistent_orphans(sfp)) + seip->ctl_flags |= SG_CTL_FLAGM_ORPHANS; + else + seip->ctl_flags &= ~SG_CTL_FLAGM_ORPHANS; + } + if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_OTHER_OPENS) { + if (sdp->open_cnt > 1) + seip->ctl_flags |= SG_CTL_FLAGM_OTHER_OPENS; + else + seip->ctl_flags &= ~SG_CTL_FLAGM_OTHER_OPENS; + } + } + if (or_masks & SG_SEIM_MINOR_INDEX) { + if (seip->valid_wr_mask & SG_SEIM_MINOR_INDEX) + SG_LOG(2, sdp, "%s: writing to minor_index ignored\n", + __func__); + if (seip->valid_rd_mask & SG_SEIM_MINOR_INDEX) + seip->minor_index = sdp->index; + } + /* send object back to user space if any read mask set */ + if (seip->valid_rd_mask || seip->ctl_flags_rd_mask) { + if (access_ok(VERIFY_WRITE, p, SZ_SG_EXTENDED_INFO)) + result = __copy_to_user(p, seip, SZ_SG_EXTENDED_INFO); + else + result = -EFAULT; + } + return result; +} + static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { - bool leave; void __user *p = (void __user *)arg; int __user *ip = p; - int result, val, read_only; struct sg_device *sdp; struct sg_fd *sfp; struct sg_request *srp; - const char *cp; + struct scsi_device *sdev; + const char *mlp = ", pass to mid-level"; unsigned long iflags; + int k, len, val; + int result = 0; + bool leave; + bool check_detach = false; sfp = filp->private_data; - if (!sfp) - return -ENXIO; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } sdp = sfp->parentdp; - if (!sdp) - return -ENXIO; - - SG_LOG(3, sdp, "%s: cmd=0x%x\n", __func__, (int)cmd_in); - read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); + SG_LOG(4, sdp, "%s: cmd=0x%x\n", __func__, cmd_in); + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; + sdev = sdp->device; switch (cmd_in) { case SG_IO: - if (atomic_read(&sdp->detaching)) - return -ENODEV; - if (!scsi_block_when_processing_errors(sdp->device)) - return -ENXIO; - if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) - return -EFAULT; - result = sg_v3_write(sfp, filp, p, SZ_SG_IO_HDR, read_only, - true, &srp); - if (result < 0) - return result; - result = wait_event_interruptible(sfp->read_wait, - srp_state_or_detaching(sdp, srp)); - - spin_lock_irqsave(&srp->rq_entry_lck, iflags); - if (unlikely(result)) { /* -ERESTARTSYS because signal hit */ - srp->orphan = true; - srp->rq_state = SG_RQ_INFLIGHT; - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - SG_LOG(1, sdp, "%s: wait_event_interruptible-->%d\n", - __func__, result); - return result; - } - if (unlikely(atomic_read(&sdp->detaching))) { - srp->rq_state = SG_RQ_INACTIVE; - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - return -ENODEV; - } else if (likely(srp->rq_state == SG_RQ_AWAIT_READ)) { - srp->rq_state = SG_RQ_DONE_READ; - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); - return (result < 0) ? result : 0; - } - cp = sg_rq_state_str(srp->rq_state, true); - SG_LOG(1, sdp, "%s: unexpected srp=0x%p state: %s\n", __func__, - srp, cp); - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - return -EPROTO; /* Logic error */ + return sg_sg_io(filp, sdp, sfp, p); + case SG_IOSUBMIT: + SG_LOG(3, sdp, "%s: SG_IOSUBMIT\n", __func__); + /* do nothing now, more to come */ + return 0; + case SG_IORECEIVE: + SG_LOG(3, sdp, "%s: SG_IORECEIVE\n", __func__); + /* more to come */ + return 0; + case SG_IOABORT: + SG_LOG(3, sdp, "%s: SG_IOABORT\n", __func__); + /* more to come */ + return 0; + case SG_SET_GET_EXTENDED: + SG_LOG(3, sdp, "%s: SG_SET_GET_EXTENDED\n", __func__); + mutex_lock(&sfp->f_mutex); + result = sg_set_get_extended(sfp, p); + mutex_unlock(&sfp->f_mutex); + return 0; case SG_SET_TIMEOUT: + SG_LOG(3, sdp, "%s: SG_SET_TIMEOUT\n", __func__); result = get_user(val, ip); if (result) return result; @@ -1103,6 +1335,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ /* strange ..., for backward compatibility */ + SG_LOG(3, sdp, "%s: SG_GET_TIMEOUT\n", __func__); return sfp->timeout_user; case SG_SET_FORCE_LOW_DMA: /* @@ -1110,10 +1343,13 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) * return an error value. So returning '0' to keep compability * with legacy applications. */ + SG_LOG(3, sdp, "%s: SG_SET_FORCE_LOW_DMA\n", __func__); return 0; case SG_GET_LOW_DMA: - return put_user((int)sdp->device->host->unchecked_isa_dma, ip); + SG_LOG(3, sdp, "%s: SG_GET_LOW_DMA\n", __func__); + return put_user((int)sdev->host->unchecked_isa_dma, ip); case SG_GET_SCSI_ID: + SG_LOG(3, sdp, "%s: SG_GET_SCSI_ID\n", __func__); if (!access_ok(VERIFY_WRITE, p, sizeof(struct sg_scsi_id))) return -EFAULT; else { @@ -1121,22 +1357,22 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (atomic_read(&sdp->detaching)) return -ENODEV; - __put_user((int)sdp->device->host->host_no, + __put_user((int)sdev->host->host_no, &sg_idp->host_no); - __put_user((int)sdp->device->channel, - &sg_idp->channel); - __put_user((int)sdp->device->id, &sg_idp->scsi_id); - __put_user((int)sdp->device->lun, &sg_idp->lun); - __put_user((int)sdp->device->type, &sg_idp->scsi_type); - __put_user((short)sdp->device->host->cmd_per_lun, + __put_user((int)sdev->channel, &sg_idp->channel); + __put_user((int)sdev->id, &sg_idp->scsi_id); + __put_user((int)sdev->lun, &sg_idp->lun); + __put_user((int)sdev->type, &sg_idp->scsi_type); + __put_user((short)sdev->host->cmd_per_lun, &sg_idp->h_cmd_per_lun); - __put_user((short)sdp->device->queue_depth, + __put_user((short)sdev->queue_depth, &sg_idp->d_queue_depth); __put_user(0, &sg_idp->unused[0]); __put_user(0, &sg_idp->unused[1]); return 0; } case SG_SET_FORCE_PACK_ID: + SG_LOG(3, sdp, "%s: SG_SET_FORCE_PACK_ID\n", __func__); result = get_user(val, ip); if (result) return result; @@ -1178,60 +1414,54 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) val); return put_user(val, ip); case SG_GET_SG_TABLESIZE: - return put_user(sdp->sg_tablesize, ip); + SG_LOG(3, sdp, "%s: SG_GET_SG_TABLESIZE\n", __func__); + result = put_user(sdp->sg_tablesize, ip); case SG_SET_RESERVED_SIZE: + mutex_lock(&sfp->f_mutex); 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)); - srp = sfp->reserve_srp; - spin_lock_irqsave(&srp->rq_entry_lck, iflags); - if (srp->rq_state != SG_RQ_INACTIVE) { - result = -EBUSY; - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - return result; - } else if (val != srp->data.dlen) { - if (sfp->mmap_called) { - result = -EBUSY; - spin_unlock_irqrestore(&srp->rq_entry_lck, - iflags); - return result; - } - srp->rq_state = SG_RQ_BUSY; - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); - sg_remove_sgat(srp); - if (val > 0) - result = sg_mk_sgat_dlen(srp, sfp, val); - spin_lock_irqsave(&srp->rq_entry_lck, iflags); - srp->rq_state = SG_RQ_INACTIVE; - } else - result = 0; /* nothing needs to change */ - spin_unlock_irqrestore(&srp->rq_entry_lck, iflags); + ; + else if (val >= 0 && val <= (1000 * 1000 * 1000)) { + SG_LOG(3, sdp, "%s: ioctl(SG_SET_RESERVED_SIZE, %d)\n", + __func__, val); + result = sg_set_reserved_sz(sfp, val); + } else { + SG_LOG(3, sdp, "%s: invalid size\n", __func__); + result = -EINVAL; + } + mutex_unlock(&sfp->f_mutex); return result; case SG_GET_RESERVED_SIZE: + mutex_lock(&sfp->f_mutex); val = min_t(int, sfp->reserve_srp->data.dlen, - max_sectors_bytes(sdp->device->request_queue)); - return put_user(val, ip); + max_sectors_bytes(sdev->request_queue)); + SG_LOG(3, sdp, "%s: SG_GET_RESERVED_SIZE=%d\n", + __func__, val); + result = put_user(val, ip); + mutex_unlock(&sfp->f_mutex); + return result; case SG_SET_COMMAND_Q: + SG_LOG(3, sdp, "%s: SG_SET_COMMAND_Q\n", __func__); result = get_user(val, ip); if (result) return result; sfp->cmd_q = !!val; return 0; case SG_GET_COMMAND_Q: + SG_LOG(3, sdp, "%s: SG_GET_COMMAND_Q\n", __func__); return put_user((int)sfp->cmd_q, ip); case SG_SET_KEEP_ORPHAN: + SG_LOG(3, sdp, "%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, sdp, "%s: SG_GET_KEEP_ORPHAN\n", __func__); return put_user((int)sfp->keep_orphan, ip); case SG_NEXT_CMD_LEN: + SG_LOG(3, sdp, "%s: SG_NEXT_CMD_LEN\n", __func__); result = get_user(val, ip); if (result) return result; @@ -1240,77 +1470,126 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) sfp->next_cmd_len = (val > 0) ? val : 0; return 0; case SG_GET_VERSION_NUM: + SG_LOG(3, sdp, "%s: SG_GET_VERSION_NUM\n", __func__); return put_user(sg_version_num, ip); case SG_GET_ACCESS_COUNT: + SG_LOG(3, sdp, "%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: - if (!access_ok(VERIFY_WRITE, p, SZ_SG_REQ_INFO * SG_MAX_QUEUE)) - return -EFAULT; - else { + SG_LOG(3, sdp, "%s: SG_GET_REQUEST_TABLE\n", __func__); + /* + * For backward compatibility, output SG_MAX_QUEUE sg_req_info + * objects. Some may be empty which is indicated by + * sg_req_info::req_state being equal to SG_RQ_INACTIVE, any + * requests beyond SG_MAX_QUEUE are ignored. + */ + k = SG_MAX_QUEUE; + len = SZ_SG_REQ_INFO * k; + if (access_ok(VERIFY_WRITE, p, len)) { + int rinfos_len = len; struct sg_req_info *rinfo; - rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, - GFP_KERNEL); + rinfo = kcalloc(k, SZ_SG_REQ_INFO, GFP_KERNEL); if (!rinfo) return -ENOMEM; + val = 0; read_lock_irqsave(&sfp->rq_list_lock, iflags); - sg_fill_request_table(sfp, rinfo, SG_MAX_QUEUE); + list_for_each_entry(srp, &sfp->rq_list, rq_entry) + ++val; + if (val > 0) + sg_fill_request_table(sfp, rinfo, + (k < val ? k : val)); read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - result = __copy_to_user(p, rinfo, - SZ_SG_REQ_INFO * SG_MAX_QUEUE); + result = __copy_to_user(p, rinfo, rinfos_len); result = result ? -EFAULT : 0; kfree(rinfo); - return result; - } + } else + return -EFAULT; + return result; case SG_EMULATED_HOST: + SG_LOG(3, sdp, "%s: SG_EMULATED_HOST\n", __func__); if (atomic_read(&sdp->detaching)) return -ENODEV; - return put_user(sdp->device->host->hostt->emulated, ip); + return put_user(sdev->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: + SG_LOG(3, sdp, "%s: SCSI_IOCTL_SEND_COMMAND\n", __func__); if (atomic_read(&sdp->detaching)) return -ENODEV; - return sg_scsi_ioctl(sdp->device->request_queue, NULL, filp->f_mode, p); + return sg_scsi_ioctl(sdev->request_queue, NULL, + filp->f_mode, p); case SG_SET_DEBUG: + SG_LOG(3, sdp, "%s: SG_SET_DEBUG\n", __func__); result = get_user(val, ip); if (result) return result; sdp->sgdebug = (char) val; return 0; case BLKSECTGET: - return put_user(max_sectors_bytes(sdp->device->request_queue), + SG_LOG(3, sdp, "%s: BLKSECTGET\n", __func__); + return put_user(max_sectors_bytes(sdev->request_queue), ip); case BLKTRACESETUP: - return blk_trace_setup(sdp->device->request_queue, + SG_LOG(3, sdp, "%s: BLKTRACESETUP\n", __func__); + return blk_trace_setup(sdev->request_queue, sdp->disk->disk_name, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), NULL, p); case BLKTRACESTART: - return blk_trace_startstop(sdp->device->request_queue, 1); + SG_LOG(3, sdp, "%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, sdp, "%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, sdp, "%s: BLKTRACETEARDOWN\n", __func__); + return blk_trace_remove(sdev->request_queue); case SCSI_IOCTL_GET_IDLUN: + SG_LOG(3, sdp, "%s: SCSI_IOCTL_GET_IDLUN %s\n", __func__, + mlp); + check_detach = true; + break; case SCSI_IOCTL_GET_BUS_NUMBER: + SG_LOG(3, sdp, "%s: SCSI_IOCTL_GET_BUS_NUMBER%s\n", + __func__, mlp); + check_detach = true; + break; case SCSI_IOCTL_PROBE_HOST: + SG_LOG(3, sdp, "%s: SCSI_IOCTL_PROBE_HOST%s\n", __func__, + mlp); + check_detach = true; + break; case SG_GET_TRANSFORM: + SG_LOG(3, sdp, "%s: SG_GET_TRANSFORM%s\n", __func__, mlp); + check_detach = true; + break; + case SG_SET_TRANSFORM: + SG_LOG(3, sdp, "%s: SG_SET_TRANSFORM%s\n", __func__, mlp); + check_detach = true; + break; case SG_SCSI_RESET: - if (atomic_read(&sdp->detaching)) - return -ENODEV; + SG_LOG(3, sdp, "%s: SG_SCSI_RESET\n", __func__); + check_detach = true; break; default: - if (read_only) - return -EPERM; /* don't know so take safe approach */ + SG_LOG(3, sdp, "%s: unrecognized ioctl [0x%x]%s\n", + __func__, cmd_in, mlp); + if (O_RDWR != (filp->f_flags & O_ACCMODE)) + return -EPERM; /* don't know, so take safer approach */ break; } - result = scsi_ioctl_block_when_processing_errors(sdp->device, - cmd_in, filp->f_flags & O_NDELAY); + if (check_detach) { + if (atomic_read(&sdp->detaching)) + return -ENODEV; + } + result = scsi_ioctl_block_when_processing_errors(sdev, cmd_in, + filp->f_flags & O_NDELAY); if (result) return result; - return scsi_ioctl(sdp->device, cmd_in, p); + /* ioctl that reach here are forwarded to the mid-level */ + return scsi_ioctl(sdev, cmd_in, p); } #ifdef CONFIG_COMPAT @@ -1322,11 +1601,14 @@ sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) struct scsi_device *sdev; sfp = filp->private_data; - if (!sfp) - return -ENXIO; + if (IS_ERR_OR_NULL(sfp)) { + pr_warn("sg: %s: sfp is NULL or error\n", __func__); + return IS_ERR(sfp) ? PTR_ERR(sfp) : -ENXIO; + } sdp = sfp->parentdp; - if (!sdp) - return -ENXIO; + SG_LOG(3, sdp, "%s: cmd=0x%x\n", __func__, (int)cmd_in); + if (IS_ERR_OR_NULL(sdp)) + return IS_ERR(sdp) ? PTR_ERR(sdp) : -ENXIO; sdev = sdp->device; if (sdev->host->hostt->compat_ioctl) { @@ -3005,8 +3287,8 @@ 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, " sse_seen=%d mmap_called=%d sum_fd_dlens=%u\n", - (int)fp->sse_seen, (int)fp->mmap_called, + seq_printf(s, " mmap_called=%d sum_fd_dlens=%u\n", + (int)fp->mmap_called, atomic_read(&fp->sum_fd_dlens)); list_for_each_entry(srp, &fp->rq_list, rq_entry) { spin_lock(&srp->rq_entry_lck); @@ -3094,6 +3376,7 @@ sg_proc_seq_show_debug(struct seq_file *s, void *v) read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } + #endif /* CONFIG_SCSI_PROC_FS */ module_init(init_sg); From patchwork Fri Oct 26 11:48:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Douglas Gilbert X-Patchwork-Id: 10657295 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C129514BD for ; Fri, 26 Oct 2018 11:48:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B1B6E2BE97 for ; Fri, 26 Oct 2018 11:48:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A5A742C13E; Fri, 26 Oct 2018 11:48:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5B5572C13C for ; Fri, 26 Oct 2018 11:48:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727600AbeJZUZi (ORCPT ); Fri, 26 Oct 2018 16:25:38 -0400 Received: from smtp.infotech.no ([82.134.31.41]:56731 "EHLO smtp.infotech.no" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727545AbeJZUZi (ORCPT ); Fri, 26 Oct 2018 16:25:38 -0400 Received: from localhost (localhost [127.0.0.1]) by smtp.infotech.no (Postfix) with ESMTP id A0BE7204198; Fri, 26 Oct 2018 13:48: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 leRgjnURu93o; Fri, 26 Oct 2018 13:48:44 +0200 (CEST) Received: from xtwo70.bingwo.ca (65.194.6.51.dyn.plus.net [51.6.194.65]) by smtp.infotech.no (Postfix) with ESMTPA id 243B120419A; Fri, 26 Oct 2018 13:48:37 +0200 (CEST) From: Douglas Gilbert To: linux-scsi@vger.kernel.org Cc: martin.petersen@oracle.com, tonyb@cybernetics.com, hare@suse.de, bart.vanassche@wdc.com Subject: [PATCH v3 8/8] sg: user controls for q_at_head, read_value Date: Fri, 26 Oct 2018 12:48:30 +0100 Message-Id: <20181026114830.13506-9-dgilbert@interlog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181026114830.13506-1-dgilbert@interlog.com> References: <20181026114830.13506-1-dgilbert@interlog.com> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a SG_SET_GET_EXTENDED ioctl control for whether commands will be queued_at_head or queued_at_tail by the block layer (together with the scsi mid-level). It has file scope. Also add a read_value integer the can be used by write a value from the SG_SEIRV_* group then the corresponding value will be returned. Signed-off-by: Douglas Gilbert --- The user can still override the new file scope setting on a a per command basis with the SG_FLAG_Q_AT_HEAD and SG_FLAG_Q_AT_TAIL in the sg v3 and v4 structures. An example of read_value usage is to write the value SG_SEIRV_FL_RQS to the read_value field. Then after the SG_SET_GET_EXTENDED ioctl is run, the number of (inactive) requests currently on this file descriptor's request free list is placed in the read_value field. Added in v3 is SG_SEIRV_DEV_FL_RQS which is an expansion of SG_SEIRV_FL_RQS. SG_SEIRV_DEV_FL_RQS counts free list entries on all sg file descriptors currently open on the device that the file descriptor (given to ioctl()) is associated with. drivers/scsi/sg.c | 160 +++++++++++++++++++++++++++++++---------- include/scsi/sg.h | 32 ++------- include/uapi/scsi/sg.h | 49 ++++++------- 3 files changed, 150 insertions(+), 91 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 3e89bbd508de..4d6966d40949 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -59,7 +59,7 @@ static int sg_version_num = 30901; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include -static char *sg_version_date = "20181019"; +static char *sg_version_date = "20181024"; static int sg_proc_init(void); #endif @@ -95,6 +95,10 @@ enum sg_rq_state { #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_TAIL true +#define SG_FD_Q_AT_HEAD false +#define SG_DEFAULT_Q_AT SG_FD_Q_AT_HEAD /* for backward compatibility */ + 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 @@ -187,6 +191,7 @@ struct sg_fd { /* holds the state of a file descriptor */ bool keep_orphan;/* false -> drop (def), true -> keep for read() */ bool mmap_called; /* false -> mmap() never called on this fd */ bool time_in_ns; /* report times in nanoseconds */ + bool q_at_tail; /* queue at tail if true, head when false */ u8 next_cmd_len; /* 0: automatic, >0: use on next write() */ struct sg_request *reserve_srp; /* allocate on open(), starts on fl */ struct fasync_struct *async_qp; /* used by asynchronous notification */ @@ -238,7 +243,7 @@ static struct sg_request *sg_add_request(struct sg_fd *sfp, int dxfr_len, static void sg_remove_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); -static const char *sg_rq_state_str(u8 rq_state, bool long_str); +static const char *sg_rq_state_str(enum sg_rq_state rq_state, bool long_str); static struct sg_request *sg_mk_srp(struct sg_fd *sfp, bool first, rwlock_t *rwlp, unsigned long *iflagsp); @@ -855,7 +860,7 @@ sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p, if (h4p || !hi_p) return ERR_PTR(-EOPNOTSUPP); - srp = sg_add_request(sfp, hi_p->dxfer_len, false); + srp = sg_add_request(sfp, hi_p->dxfer_len, sync); if (IS_ERR(srp)) return srp; srp->header = *hi_p; /* structure assignment, could memcpy */ @@ -897,9 +902,13 @@ sg_common_write(struct sg_fd *sfp, const struct sg_io_hdr *hi_p, srp->start_ts = ktime_get_with_offset(TK_OFFS_BOOT); else hp->duration = jiffies_to_msecs(jiffies); - /* at tail if v3 or later interface and tail flag set */ - at_head = !(hp->interface_id != '\0' && - (SG_FLAG_Q_AT_TAIL & hp->flags)); + + if (hp->interface_id == '\0') /* v1 and v2 interface */ + at_head = true; /* backward compatibility */ + else if (sfp->q_at_tail) /* cmd flags can override sfd setting */ + at_head = (hp->flags & SG_FLAG_Q_AT_HEAD); + else /* this sfd is defaulting to head */ + at_head = !(hp->flags & SG_FLAG_Q_AT_TAIL); srp->rq->timeout = timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ @@ -1084,30 +1093,30 @@ sg_reserved_sz(struct sg_fd *sfp, struct sg_extended_info *seip) { bool free_n_srp = false; int result = 0; - int val, mx_sect_bytes; + int new_sz, mx_sect_bytes; unsigned long iflags; - struct sg_request *srp; /* prior reserve request */ + struct sg_request *o_srp; /* prior reserve request */ struct sg_request *n_srp; /* new sg_request, may be used */ - struct sg_request *flf_srp; /* free list first element */ + struct sg_request *t_srp; /* other fl entries */ struct sg_device *sdp = sfp->parentdp; mx_sect_bytes = max_sectors_bytes(sdp->device->request_queue); + o_srp = sfp->reserve_srp; if (!(seip->valid_wr_mask & SG_SEIM_RESERVED_SIZE)) { /* read only */ - srp = sfp->reserve_srp; - seip->reserved_sz = (u32)min_t(int, srp->data.dlen, + seip->reserved_sz = (u32)min_t(int, o_srp->data.dlen, mx_sect_bytes); SG_LOG(3, sdp, "%s: rd val=%u\n", __func__, seip->reserved_sz); return 0; } - val = min_t(int, (int)seip->reserved_sz, mx_sect_bytes); - SG_LOG(3, sdp, "%s: val=%u modify to %d\n", __func__, - seip->reserved_sz, val); + new_sz = min_t(int, (int)seip->reserved_sz, mx_sect_bytes); + SG_LOG(3, sdp, "%s: was %u, modify to %d\n", __func__, + o_srp->data.dlen, new_sz); /* Should sizes less than PAGE_SIZE be permitted? Round up? */ - n_srp = sg_mk_srp(sfp, true, NULL, NULL); + n_srp = sg_mk_srp(sfp, true /* can take time */, NULL, NULL); if (IS_ERR(n_srp)) return PTR_ERR(n_srp); - if (val > 0) { - result = sg_mk_sgat_dlen(n_srp, sfp, val); + if (new_sz > 0) { + result = sg_mk_sgat_dlen(n_srp, sfp, new_sz); if (result) { kfree(n_srp); return result; @@ -1115,35 +1124,39 @@ sg_reserved_sz(struct sg_fd *sfp, struct sg_extended_info *seip) } /* new sg_request object, sized correctly is now available */ write_lock_irqsave(&sfp->rq_list_lock, iflags); - srp = sfp->reserve_srp; - spin_lock(&srp->rq_entry_lck); - /* Should not matter if srp->rq_state != SG_RQ_INACTIVE */ + o_srp = sfp->reserve_srp; + spin_lock(&o_srp->rq_entry_lck); if (sfp->mmap_called) { result = -EBUSY; free_n_srp = true; goto unlock; } - flf_srp = list_first_entry_or_null(&sfp->rq_free_list, - struct sg_request, free_entry); - if (flf_srp && flf_srp != srp && val <= flf_srp->data.dlen) { - spin_lock(&flf_srp->rq_entry_lck); - if (flf_srp->rq_state == SG_RQ_INACTIVE) { - free_n_srp = true; - sfp->reserve_srp = flf_srp; + list_for_each_entry(t_srp, &sfp->rq_free_list, free_entry) { + if (t_srp != o_srp && new_sz <= t_srp->data.dlen) { + spin_lock(&t_srp->rq_entry_lck); + if (t_srp->rq_state == SG_RQ_INACTIVE) { + free_n_srp = true; + sfp->reserve_srp = t_srp; + } + spin_unlock(&t_srp->rq_entry_lck); } - spin_unlock(&flf_srp->rq_entry_lck); + if (free_n_srp) + break; } if (!free_n_srp) { - sfp->reserve_srp = n_srp; list_add(&n_srp->free_entry, &sfp->rq_free_list); + sfp->reserve_srp = n_srp; } if (seip->valid_rd_mask & SG_SEIM_RESERVED_SIZE) { - srp = sfp->reserve_srp; + struct sg_request *srp = sfp->reserve_srp; + seip->reserved_sz = (u32)min_t(int, srp->data.dlen, mx_sect_bytes); } unlock: - spin_unlock(&srp->rq_entry_lck); + if (new_sz > sfp->rem_sgat_thresh) + sfp->rem_sgat_thresh = new_sz; + spin_unlock(&o_srp->rq_entry_lck); write_unlock_irqrestore(&sfp->rq_list_lock, iflags); if (free_n_srp) { sg_remove_sgat(n_srp); @@ -1190,11 +1203,13 @@ static int sg_set_get_extended(struct sg_fd *sfp, void __user *p) { int result = 0; - u32 uv; + unsigned long iflags; + u32 uv, or_masks; struct sg_device *sdp = sfp->parentdp; + struct sg_fd *a_sfp; struct sg_extended_info *seip; + struct sg_request *srp; struct sg_extended_info sei; - u32 or_masks; seip = &sei; if (!access_ok(VERIFY_READ, p, SZ_SG_EXTENDED_INFO)) @@ -1208,8 +1223,10 @@ sg_set_get_extended(struct sg_fd *sfp, void __user *p) } SG_LOG(3, sdp, "%s: wr_mask=0x%x rd_mask=0x%x\n", __func__, seip->valid_wr_mask, seip->valid_rd_mask); + /* reserved_sz (u32), read-write */ if (or_masks & SG_SEIM_RESERVED_SIZE) result = sg_reserved_sz(sfp, seip); + /* rq_rem_sgat_threshold (u32), read-write [impacts re-use only] */ if (or_masks & SG_SEIM_RQ_REM_THRESH) { if (seip->valid_wr_mask & SG_SEIM_RQ_REM_THRESH) { uv = seip->rq_rem_sgat_thresh; @@ -1220,6 +1237,7 @@ sg_set_get_extended(struct sg_fd *sfp, void __user *p) if (seip->valid_rd_mask & SG_SEIM_RQ_REM_THRESH) seip->rq_rem_sgat_thresh = sfp->rem_sgat_thresh; } + /* tot_fd_thresh (u32), read-write [sum of active cmd dlen_s] */ if (or_masks & SG_SEIM_TOT_FD_THRESH) { if (seip->valid_wr_mask & SG_SEIM_TOT_FD_THRESH) { uv = seip->tot_fd_thresh; @@ -1230,8 +1248,9 @@ sg_set_get_extended(struct sg_fd *sfp, void __user *p) if (seip->valid_rd_mask & SG_SEIM_TOT_FD_THRESH) seip->tot_fd_thresh = sfp->tot_fd_thresh; } + /* check all boolean flags if either wr or rd mask set in or_mask */ if (or_masks & SG_SEIM_CTL_FLAGS) { - /* don't care whether wr or rd mask set in or_mask */ + /* TIME_IN_NS boolean, read-write */ if (seip->ctl_flags_wr_mask & SG_CTL_FLAGM_TIME_IN_NS) sfp->time_in_ns = !!(seip->ctl_flags & SG_CTL_FLAGM_TIME_IN_NS); @@ -1241,19 +1260,32 @@ sg_set_get_extended(struct sg_fd *sfp, void __user *p) else seip->ctl_flags &= ~SG_CTL_FLAGM_TIME_IN_NS; } + /* ORPHANS boolean, read-only */ if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_ORPHANS) { if (sg_any_persistent_orphans(sfp)) seip->ctl_flags |= SG_CTL_FLAGM_ORPHANS; else seip->ctl_flags &= ~SG_CTL_FLAGM_ORPHANS; } + /* OTHER_OPENS boolean, read-only */ if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_OTHER_OPENS) { if (sdp->open_cnt > 1) seip->ctl_flags |= SG_CTL_FLAGM_OTHER_OPENS; else seip->ctl_flags &= ~SG_CTL_FLAGM_OTHER_OPENS; } + /* Q_TAIL boolean, read-write */ + if (seip->ctl_flags_wr_mask & SG_CTL_FLAGM_Q_TAIL) + sfp->q_at_tail = + !!(seip->ctl_flags & SG_CTL_FLAGM_Q_TAIL); + if (seip->ctl_flags_rd_mask & SG_CTL_FLAGM_Q_TAIL) { + if (sfp->q_at_tail) + seip->ctl_flags |= SG_CTL_FLAGM_Q_TAIL; + else + seip->ctl_flags &= ~SG_CTL_FLAGM_Q_TAIL; + } } + /* minor_index u32, read-only */ if (or_masks & SG_SEIM_MINOR_INDEX) { if (seip->valid_wr_mask & SG_SEIM_MINOR_INDEX) SG_LOG(2, sdp, "%s: writing to minor_index ignored\n", @@ -1261,7 +1293,50 @@ sg_set_get_extended(struct sg_fd *sfp, void __user *p) if (seip->valid_rd_mask & SG_SEIM_MINOR_INDEX) seip->minor_index = sdp->index; } - /* send object back to user space if any read mask set */ + if ((seip->valid_rd_mask & SG_SEIM_READ_VAL) && + (seip->valid_wr_mask & SG_SEIM_READ_VAL)) { + switch (seip->read_value) { + case SG_SEIRV_INT_MASK: + seip->read_value = SG_SEIM_ALL_BITS; + break; + case SG_SEIRV_BOOL_MASK: + seip->read_value = SG_CTL_FLAGM_ALL_BITS; + break; + case SG_SEIRV_VERS_NUM: + seip->read_value = sg_version_num; + break; + case SG_SEIRV_FL_RQS: + uv = 0; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + list_for_each_entry(srp, &sfp->rq_free_list, + free_entry) + ++uv; + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + seip->read_value = uv; + break; + case SG_SEIRV_DEV_FL_RQS: + uv = 0; + read_lock_irqsave(&sdp->sfd_lock, iflags); + list_for_each_entry(a_sfp, &sdp->sfds, + sfd_entry) { + read_lock(&a_sfp->rq_list_lock); + list_for_each_entry(srp, &a_sfp->rq_free_list, + free_entry) + ++uv; + read_unlock(&a_sfp->rq_list_lock); + } + read_unlock_irqrestore(&sdp->sfd_lock, iflags); + seip->read_value = uv; + break; + default: + SG_LOG(6, sdp, "%s: can't decode %d --> read_value\n", + __func__, seip->read_value); + seip->read_value = 0; + break; + } + } + + /* finally send object back to user space if any read mask set */ if (seip->valid_rd_mask || seip->ctl_flags_rd_mask) { if (access_ok(VERIFY_WRITE, p, SZ_SG_EXTENDED_INFO)) result = __copy_to_user(p, seip, SZ_SG_EXTENDED_INFO); @@ -1318,7 +1393,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) mutex_lock(&sfp->f_mutex); result = sg_set_get_extended(sfp, p); mutex_unlock(&sfp->f_mutex); - return 0; + return result; case SG_SET_TIMEOUT: SG_LOG(3, sdp, "%s: SG_SET_TIMEOUT\n", __func__); result = get_user(val, ip); @@ -2197,6 +2272,9 @@ 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); sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); if ( IS_ERR(sg_sysfs_class) ) { rc = PTR_ERR(sg_sysfs_class); @@ -2592,7 +2670,12 @@ sg_get_rq_pack_id(struct sg_fd *sfp, int pack_id) return NULL; } -/* If rwlp and iflagsp non-NULL then release and re-take write lock */ +/* + * If 'first' is set then use GFP_KERNEL which may take time but has + * improved chance of success, otherwise use GFP_ATOMIC. Only in + * 'first' case, if both rwlp and iflagsp are non-NULL then release and + * re-take write rwlp. + */ static struct sg_request * sg_mk_srp(struct sg_fd *sfp, bool first, rwlock_t *rwlp, unsigned long *iflagsp) @@ -2851,6 +2934,7 @@ sg_add_sfp(struct sg_device *sdp) dlen : SG_TOT_FD_THRESHOLD; atomic_set(&sfp->sum_fd_dlens, 0); sfp->time_in_ns = !!SG_DEF_TIME_UNIT; + sfp->q_at_tail = SG_DEFAULT_Q_AT; sfp->parentdp = sdp; if (atomic_read(&sdp->detaching)) { kfree(sfp); @@ -3239,7 +3323,7 @@ sg_proc_seq_show_devstrs(struct seq_file *s, void *v) } static const char * -sg_rq_state_str(u8 rq_state, bool long_str) +sg_rq_state_str(enum sg_rq_state rq_state, bool long_str) { switch (rq_state) { case SG_RQ_INACTIVE: diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 596f68746f66..46fc7cbffd78 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -4,41 +4,21 @@ #include -/* - * 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 - 2018 Douglas Gilbert - * - * Version: 3.9.01 (20181016) - * This version is for 2.6, 3 and 4 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.txt - * - * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html - */ - #ifdef __KERNEL__ extern int sg_big_buff; /* for sysctl */ #endif +/* + * In version 3.9.01 of the sg driver, this file was spilt in two, with the + * bulk of the user space interface being placed in the file being included + * in the following line. + */ #include #ifdef __KERNEL__ #define SG_DEFAULT_TIMEOUT_USER (60*USER_HZ) /* HZ == 'jiffies in 1 second' */ #endif -#undef SG_DEFAULT_TIMEOUT /* cause define in sg.c */ +#undef SG_DEFAULT_TIMEOUT /* because of conflicting define in sg.c */ #endif /* end of ifndef _SCSI_GENERIC_H guard */ diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h index 37588e1c4e3a..b64d155541ea 100644 --- a/include/uapi/scsi/sg.h +++ b/include/uapi/scsi/sg.h @@ -2,27 +2,6 @@ #ifndef _UAPI_SCSI_SG_H #define _UAPI_SCSI_SG_H -/* - * Want to block the original sg.h header from also being included. That - * causes lots of multiple definition errors. This will only work if this - * header is included _before_ the original sg.h header . - */ -#define _SCSI_GENERIC_H /* original kernel header guard */ -#define _SCSI_SG_H /* glibc header guard */ - -/* - * Other UAPI headers include linux/compiler.h to resolve "__" types but that - * doesn't always work, perhaps in the future. Fall back to linux/types.h . - */ -/* #include */ -#include -#include - -/* Still problems with __user so define to nothing */ -#define __user - -#include - /* * History: * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user @@ -48,6 +27,11 @@ * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html */ +#include +#include + +/* bsg.h contains the sg v4 use space interface structure. */ +#include /* * Same structure as used by readv() call. It defines one scatter-gather @@ -109,7 +93,7 @@ typedef struct sg_io_hdr { #define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */ /* no transfer of kernel buffers to/from user space; to debug indirect IO */ #define SG_FLAG_NO_DXFER 0x10000 -/* defaults: for sg driver: Q_AT_HEAD; for block layer: Q_AT_TAIL */ +/* defaults: for sg driver (v3): Q_AT_HEAD; for block layer: Q_AT_TAIL */ #define SG_FLAG_Q_AT_TAIL 0x10 #define SG_FLAG_Q_AT_HEAD 0x20 @@ -186,12 +170,22 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ #define SG_SEIM_TOT_FD_THRESH 0x4 /* tot_fd_thresh field valid */ #define SG_SEIM_CTL_FLAGS 0x8 /* ctl_flags_mask bits in ctl_flags */ #define SG_SEIM_MINOR_INDEX 0x10 /* sg device minor index number */ -#define SG_SEIM_ALL_BITS 0x1f /* should be OR of previous items */ +#define SG_SEIM_READ_VAL 0x20 /* write SG_SEIRV, read related */ +#define SG_SEIM_ALL_BITS 0x3f /* should be OR of previous items */ #define SG_CTL_FLAGM_TIME_IN_NS 0x1 /* time: nanosecs (def: millisecs) */ #define SG_CTL_FLAGM_TAG_FOR_PACK_ID 0x2 #define SG_CTL_FLAGM_OTHER_OPENS 0x4 /* rd: other sg fd_s on this dev */ #define SG_CTL_FLAGM_ORPHANS 0x8 /* rd: orphaned requests on this fd */ +#define SG_CTL_FLAGM_Q_TAIL 0x10 /* used for future cmds on this fd */ +#define SG_CTL_FLAGM_ALL_BITS 0x1f /* should be OR of previous items */ + +/* Write one of the following values to sg_extended_info::read_value, get... */ +#define SG_SEIRV_INT_MASK 0x0 /* get SG_SEIM_ALL_BITS */ +#define SG_SEIRV_BOOL_MASK 0x1 /* get SG_CTL_FLAGM_ALL_BITS */ +#define SG_SEIRV_VERS_NUM 0x2 /* get driver version number as int */ +#define SG_SEIRV_FL_RQS 0x3 /* number of requests in free list */ +#define SG_SEIRV_DEV_FL_RQS 0x4 /* sum of rqs on all fds on this dev */ /* * A pointer to the following structure is passed as the third argument to @@ -203,11 +197,11 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ * back to the user space. If the same bit is set in both the *_wr_mask and * corresponding *_rd_mask fields, then the write action takes place before * the read action and no other operation will split the two. This structure - * is padded to 64 bytes to allow for new values to be added in the future. + * is padded to 96 bytes to allow for new values to be added in the future. */ struct sg_extended_info { - __u32 valid_wr_mask; /* OR-ed SG_SEIM_* values */ - __u32 valid_rd_mask; /* OR-ed SG_SEIM_* values */ + __u32 valid_wr_mask; /* OR-ed SG_SEIM_* user->driver values */ + __u32 valid_rd_mask; /* OR-ed SG_SEIM_* driver->user values */ __u32 reserved_sz; /* data/sgl size of pre-allocated request */ __u32 rq_rem_sgat_thresh;/* request re-use: clear data/sgat if > */ __u32 tot_fd_thresh; /* total data/sgat for this fd, 0: no limit */ @@ -215,7 +209,8 @@ struct sg_extended_info { __u32 ctl_flags_rd_mask; /* OR-ed SG_CTL_FLAGM_* values */ __u32 ctl_flags; /* bit values OR-ed, see SG_CTL_FLAGM_* */ __u32 minor_index; /* rd: kernel's sg device minor number */ - __u8 pad_to_64[28]; /* pad so struct is 64 bytes long */ + __u32 read_value; /* write known value, read back related */ + __u8 pad_to_96[56]; /* pad so struct is 96 bytes long */ }; /*