--- zzzz-none-000/linux-3.10.107/drivers/scsi/sg.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/scsi/sg.c 2021-02-04 17:41:59.000000000 +0000 @@ -7,9 +7,7 @@ * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2005 Douglas Gilbert - * - * Modified 19-JAN-1998 Richard Gooch Devfs support + * Copyright (C) 1998 - 2014 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 @@ -18,11 +16,11 @@ * */ -static int sg_version_num = 30534; /* 2 digits for each component */ -#define SG_VERSION_STR "3.5.34" +static int sg_version_num = 30536; /* 2 digits for each component */ +#define SG_VERSION_STR "3.5.36" /* - * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: + * 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). @@ -35,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -51,7 +48,9 @@ #include #include #include +#include #include +#include #include "scsi.h" #include @@ -64,7 +63,7 @@ #ifdef CONFIG_SCSI_PROC_FS #include -static char *sg_version_date = "20061027"; +static char *sg_version_date = "20140603"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -74,6 +73,12 @@ #define SG_MAX_DEVS 32768 +/* 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 + */ +#define SG_MAX_CDB_SIZE 252 + /* * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d) * Then when using 32 bit integers x * m may overflow during the calculation. @@ -102,18 +107,16 @@ #define SG_SECTOR_SZ 512 -static int sg_add(struct device *, struct class_interface *); -static void sg_remove(struct device *, struct class_interface *); - -static DEFINE_SPINLOCK(sg_open_exclusive_lock); +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 struct class_interface sg_interface = { - .add_dev = sg_add, - .remove_dev = sg_remove, + .add_dev = sg_add_device, + .remove_dev = sg_remove_device, }; typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ @@ -146,8 +149,7 @@ } Sg_request; typedef struct sg_fd { /* holds the state of a file descriptor */ - /* sfd_siblings is protected by sg_index_lock */ - struct list_head sfd_siblings; + 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 */ rwlock_t rq_list_lock; /* protect access to list in req_arr */ @@ -161,7 +163,7 @@ char low_dma; /* as in parent but possibly overridden to 1 */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ - char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ + 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 */ struct kref f_ref; @@ -170,14 +172,15 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ struct scsi_device *device; - wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ + wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ + struct mutex open_rel_lock; /* held when in open() or release() */ int sg_tablesize; /* adapter's max scatter-gather table size */ u32 index; /* device index number */ - /* sfds is protected by sg_index_lock */ struct list_head sfds; - volatile char detached; /* 0->attached, 1->detached pending removal */ - /* exclude protected by sg_open_exclusive_lock */ - char exclude; /* opened for exclusive access */ + rwlock_t sfd_lock; /* protect access to sfd list */ + atomic_t detaching; /* 0->device usable, 1->device detaching */ + bool exclude; /* 1->open(O_EXCL) succeeded and is active */ + int open_cnt; /* count of opens (perhaps < num(sfds) ) */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ struct gendisk *disk; struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg] */ @@ -197,24 +200,28 @@ 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_scatter_hold * schp); +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, int dev); +static Sg_fd *sg_add_sfp(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 int sg_res_in_use(Sg_fd * sfp); static Sg_device *sg_get_dev(int dev); -static void sg_put_dev(Sg_device *sdp); +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 sg_printk(prefix, sdp, fmt, a...) \ + sdev_prefix_printk(prefix, (sdp)->device, \ + (sdp)->disk->disk_name, fmt, ##a) + static int sg_allow_access(struct file *filp, unsigned char *cmd) { struct sg_fd *sfp = filp->private_data; @@ -225,38 +232,43 @@ return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); } -static int get_exclude(Sg_device *sdp) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&sg_open_exclusive_lock, flags); - ret = sdp->exclude; - spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); - return ret; -} - -static int set_exclude(Sg_device *sdp, char val) +static int +open_wait(Sg_device *sdp, int flags) { - unsigned long flags; - - spin_lock_irqsave(&sg_open_exclusive_lock, flags); - sdp->exclude = val; - spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); - return val; -} + int retval = 0; -static int sfds_list_empty(Sg_device *sdp) -{ - unsigned long flags; - int ret; + if (flags & O_EXCL) { + while (sdp->open_cnt > 0) { + mutex_unlock(&sdp->open_rel_lock); + retval = wait_event_interruptible(sdp->open_wait, + (atomic_read(&sdp->detaching) || + !sdp->open_cnt)); + mutex_lock(&sdp->open_rel_lock); + + if (retval) /* -ERESTARTSYS */ + return retval; + if (atomic_read(&sdp->detaching)) + return -ENODEV; + } + } else { + while (sdp->exclude) { + mutex_unlock(&sdp->open_rel_lock); + retval = wait_event_interruptible(sdp->open_wait, + (atomic_read(&sdp->detaching) || + !sdp->exclude)); + mutex_lock(&sdp->open_rel_lock); + + if (retval) /* -ERESTARTSYS */ + return retval; + if (atomic_read(&sdp->detaching)) + return -ENODEV; + } + } - read_lock_irqsave(&sg_index_lock, flags); - ret = list_empty(&sdp->sfds); - read_unlock_irqrestore(&sg_index_lock, flags); - return ret; + return retval; } +/* Returns 0 on success, else a negated errno value */ static int sg_open(struct inode *inode, struct file *filp) { @@ -265,17 +277,17 @@ struct request_queue *q; Sg_device *sdp; Sg_fd *sfp; - int res; int retval; nonseekable_open(inode, filp); - SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); + if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) + return -EPERM; /* Can't lock it with read only access */ sdp = sg_get_dev(dev); - if (IS_ERR(sdp)) { - retval = PTR_ERR(sdp); - sdp = NULL; - goto sg_put; - } + if (IS_ERR(sdp)) + return PTR_ERR(sdp); + + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_open: flags=0x%x\n", flags)); /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ @@ -287,6 +299,9 @@ if (retval) goto sdp_put; + /* scsi_block_when_processing_errors() may block so bypass + * check if O_NONBLOCK. Permits SCSI commands to be issued + * during error recovery. Tread carefully. */ if (!((flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) { retval = -ENXIO; @@ -294,65 +309,65 @@ goto error_out; } - if (flags & O_EXCL) { - if (O_RDONLY == (flags & O_ACCMODE)) { - retval = -EPERM; /* Can't lock it with read only access */ - goto error_out; - } - if (!sfds_list_empty(sdp) && (flags & O_NONBLOCK)) { - retval = -EBUSY; - goto error_out; - } - res = wait_event_interruptible(sdp->o_excl_wait, - ((!sfds_list_empty(sdp) || get_exclude(sdp)) ? 0 : set_exclude(sdp, 1))); - if (res) { - retval = res; /* -ERESTARTSYS because signal hit process */ - goto error_out; - } - } else if (get_exclude(sdp)) { /* some other fd has an exclusive lock on dev */ - if (flags & O_NONBLOCK) { - retval = -EBUSY; - goto error_out; - } - res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp)); - if (res) { - retval = res; /* -ERESTARTSYS because signal hit process */ - goto error_out; + mutex_lock(&sdp->open_rel_lock); + if (flags & O_NONBLOCK) { + if (flags & O_EXCL) { + if (sdp->open_cnt > 0) { + retval = -EBUSY; + goto error_mutex_locked; + } + } else { + if (sdp->exclude) { + retval = -EBUSY; + goto error_mutex_locked; + } } + } else { + retval = open_wait(sdp, flags); + if (retval) /* -ERESTARTSYS or -ENODEV */ + goto error_mutex_locked; } - if (sdp->detached) { - retval = -ENODEV; - goto error_out; - } - if (sfds_list_empty(sdp)) { /* no existing opens on this device */ + + /* N.B. at this point we are holding the open_rel_lock */ + if (flags & O_EXCL) + sdp->exclude = true; + + if (sdp->open_cnt < 1) { /* no existing opens */ sdp->sgdebug = 0; q = sdp->device->request_queue; sdp->sg_tablesize = queue_max_segments(q); } - if ((sfp = sg_add_sfp(sdp, dev))) - filp->private_data = sfp; - else { - if (flags & O_EXCL) { - set_exclude(sdp, 0); /* undo if error */ - wake_up_interruptible(&sdp->o_excl_wait); - } - retval = -ENOMEM; - goto error_out; + sfp = sg_add_sfp(sdp); + if (IS_ERR(sfp)) { + retval = PTR_ERR(sfp); + goto out_undo; } + + filp->private_data = sfp; + sdp->open_cnt++; + mutex_unlock(&sdp->open_rel_lock); + retval = 0; -error_out: - if (retval) { - scsi_autopm_put_device(sdp->device); -sdp_put: - scsi_device_put(sdp->device); - } sg_put: - if (sdp) - sg_put_dev(sdp); + kref_put(&sdp->d_ref, sg_device_destroy); return retval; + +out_undo: + if (flags & O_EXCL) { + sdp->exclude = false; /* undo if error */ + wake_up_interruptible(&sdp->open_wait); + } +error_mutex_locked: + mutex_unlock(&sdp->open_rel_lock); +error_out: + scsi_autopm_put_device(sdp->device); +sdp_put: + scsi_device_put(sdp->device); + goto sg_put; } -/* Following function was formerly called 'sg_close' */ +/* Release resources associated with a successful sg_open() + * Returns 0 on success, else a negated errno value */ static int sg_release(struct inode *inode, struct file *filp) { @@ -361,13 +376,22 @@ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); - - set_exclude(sdp, 0); - wake_up_interruptible(&sdp->o_excl_wait); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); + mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); kref_put(&sfp->f_ref, sg_remove_sfp); + sdp->open_cnt--; + + /* possibly many open()s waiting on exlude clearing, start many; + * only open(O_EXCL)s wait on 0==open_cnt so only start one */ + if (sdp->exclude) { + sdp->exclude = false; + wake_up_interruptible_all(&sdp->open_wait); + } else if (0 == sdp->open_cnt) { + wake_up_interruptible(&sdp->open_wait); + } + mutex_unlock(&sdp->open_rel_lock); return 0; } @@ -384,8 +408,8 @@ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_read: %s, count=%d\n", - sdp->disk->disk_name, (int) count)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_read: count=%d\n", (int) count)); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -419,7 +443,7 @@ } srp = sg_get_rq_mark(sfp, req_pack_id); if (!srp) { /* now wait on packet to arrive */ - if (sdp->detached) { + if (atomic_read(&sdp->detaching)) { retval = -ENODEV; goto free_old_hdr; } @@ -428,9 +452,9 @@ goto free_old_hdr; } retval = wait_event_interruptible(sfp->read_wait, - (sdp->detached || + (atomic_read(&sdp->detaching) || (srp = sg_get_rq_mark(sfp, req_pack_id)))); - if (sdp->detached) { + if (atomic_read(&sdp->detaching)) { retval = -ENODEV; goto free_old_hdr; } @@ -486,7 +510,7 @@ old_hdr->result = EIO; break; case DID_ERROR: - old_hdr->result = (srp->sense_b[0] == 0 && + old_hdr->result = (srp->sense_b[0] == 0 && hp->masked_status == GOOD) ? 0 : EIO; break; default: @@ -566,16 +590,16 @@ Sg_request *srp; struct sg_header old_hdr; sg_io_hdr_t *hp; - unsigned char cmnd[MAX_COMMAND_SIZE]; + unsigned char cmnd[SG_MAX_CDB_SIZE]; if (unlikely(segment_eq(get_fs(), KERNEL_DS))) return -EINVAL; if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_write: %s, count=%d\n", - sdp->disk->disk_name, (int) count)); - if (sdp->detached) + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_write: count=%d\n", (int) count)); + if (atomic_read(&sdp->detaching)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) @@ -595,18 +619,13 @@ return -EIO; /* The minimum scsi command length is 6 bytes. */ if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); + SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp, + "sg_write: queue full\n")); return -EDOM; } buf += SZ_SG_HEADER; __get_user(opcode, buf); if (sfp->next_cmd_len > 0) { - if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); - sfp->next_cmd_len = 0; - sg_remove_request(sfp, srp); - return -EIO; - } cmd_size = sfp->next_cmd_len; sfp->next_cmd_len = 0; /* reset so only this write() effected */ } else { @@ -614,7 +633,7 @@ if ((opcode >= 0xc0) && old_hdr.twelve_byte) cmd_size = 12; } - SCSI_LOG_TIMEOUT(4, printk( + 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. */ input_size = count - cmd_size; @@ -679,7 +698,7 @@ int k; Sg_request *srp; sg_io_hdr_t *hp; - unsigned char cmnd[MAX_COMMAND_SIZE]; + unsigned char cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -690,7 +709,8 @@ sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); + SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, + "sg_new_write: queue full\n")); return -EDOM; } srp->sg_io_owned = sg_io_owned; @@ -747,7 +767,7 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking) { - int k, data_dir; + int k, at_head; Sg_device *sdp = sfp->parentdp; sg_io_hdr_t *hp = &srp->header; @@ -759,16 +779,18 @@ hp->host_status = 0; hp->driver_status = 0; hp->resid = 0; - SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", - (int) cmnd[0], (int) hp->cmd_len)); + 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)); k = sg_start_req(srp, cmnd); if (k) { - SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k)); + SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, + "sg_common_write: start_req err=%d\n", k)); sg_finish_rem_req(srp); return k; /* probably out of space --> ENOMEM */ } - if (sdp->detached) { + if (atomic_read(&sdp->detaching)) { if (srp->bio) { if (srp->rq->cmd != srp->rq->__cmd) kfree(srp->rq->cmd); @@ -781,27 +803,17 @@ return -ENODEV; } - switch (hp->dxfer_direction) { - case SG_DXFER_TO_FROM_DEV: - case SG_DXFER_FROM_DEV: - data_dir = DMA_FROM_DEVICE; - break; - case SG_DXFER_TO_DEV: - data_dir = DMA_TO_DEVICE; - break; - case SG_DXFER_UNKNOWN: - data_dir = DMA_BIDIRECTIONAL; - break; - default: - data_dir = DMA_NONE; - break; - } 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; 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, 1, sg_rq_end_io); + srp->rq, at_head, sg_rq_end_io); return 0; } @@ -816,6 +828,15 @@ return ret; } +static int max_sectors_bytes(struct request_queue *q) +{ + unsigned int max_sectors = queue_max_sectors(q); + + max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9); + + return max_sectors << 9; +} + static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -830,25 +851,27 @@ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: %s, cmd=0x%x\n", - sdp->disk->disk_name, (int) cmd_in)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_ioctl: cmd=0x%x\n", (int) cmd_in)); read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); switch (cmd_in) { case SG_IO: - if (sdp->detached) + 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; + mutex_lock(&sfp->parentdp->open_rel_lock); result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 1, read_only, 1, &srp); + mutex_unlock(&sfp->parentdp->open_rel_lock); if (result < 0) return result; result = wait_event_interruptible(sfp->read_wait, - (srp_done(sfp, srp) || sdp->detached)); - if (sdp->detached) + (srp_done(sfp, srp) || atomic_read(&sdp->detaching))); + if (atomic_read(&sdp->detaching)) return -ENODEV; write_lock_irq(&sfp->rq_list_lock); if (srp->done) { @@ -883,11 +906,13 @@ sfp->low_dma = 1; if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) { val = (int) sfp->reserve.bufflen; - sg_remove_scat(&sfp->reserve); + mutex_lock(&sfp->parentdp->open_rel_lock); + sg_remove_scat(sfp, &sfp->reserve); sg_build_reserve(sfp, val); + mutex_unlock(&sfp->parentdp->open_rel_lock); } } else { - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; sfp->low_dma = sdp->device->host->unchecked_isa_dma; } @@ -900,7 +925,7 @@ else { sg_scsi_id_t __user *sg_idp = p; - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; __put_user((int) sdp->device->host->host_no, &sg_idp->host_no); @@ -952,20 +977,22 @@ result = get_user(val, ip); if (result) return result; - if (val < 0) - return -EINVAL; + if (val < 0) + return -EINVAL; val = min_t(int, val, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); if (val != sfp->reserve.bufflen) { if (sg_res_in_use(sfp) || sfp->mmap_called) return -EBUSY; - sg_remove_scat(&sfp->reserve); + mutex_lock(&sfp->parentdp->open_rel_lock); + sg_remove_scat(sfp, &sfp->reserve); sg_build_reserve(sfp, val); + mutex_unlock(&sfp->parentdp->open_rel_lock); } return 0; case SG_GET_RESERVED_SIZE: val = min_t(int, sfp->reserve.bufflen, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q: result = get_user(val, ip); @@ -987,6 +1014,8 @@ result = get_user(val, ip); if (result) return result; + if (val > SG_MAX_CDB_SIZE) + return -ENOMEM; sfp->next_cmd_len = (val > 0) ? val : 0; return 0; case SG_GET_VERSION_NUM: @@ -1013,8 +1042,8 @@ if (srp) { rinfo[val].req_state = srp->done + 1; rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & + srp->header.masked_status & + srp->header.host_status & srp->header.driver_status; if (srp->done) rinfo[val].duration = @@ -1035,51 +1064,18 @@ } } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - result = __copy_to_user(p, rinfo, + result = __copy_to_user(p, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE); result = result ? -EFAULT : 0; kfree(rinfo); return result; } case SG_EMULATED_HOST: - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; return put_user(sdp->device->host->hostt->emulated, ip); - case SG_SCSI_RESET: - if (sdp->detached) - return -ENODEV; - if (filp->f_flags & O_NONBLOCK) { - if (scsi_host_in_recovery(sdp->device->host)) - return -EBUSY; - } else if (!scsi_block_when_processing_errors(sdp->device)) - return -EBUSY; - result = get_user(val, ip); - if (result) - return result; - if (SG_SCSI_RESET_NOTHING == val) - return 0; - switch (val) { - case SG_SCSI_RESET_DEVICE: - val = SCSI_TRY_RESET_DEVICE; - break; - case SG_SCSI_RESET_TARGET: - val = SCSI_TRY_RESET_TARGET; - break; - case SG_SCSI_RESET_BUS: - val = SCSI_TRY_RESET_BUS; - break; - case SG_SCSI_RESET_HOST: - val = SCSI_TRY_RESET_HOST; - break; - default: - return -EINVAL; - } - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return (scsi_reset_provider(sdp->device, val) == - SUCCESS) ? 0 : -EIO; case SCSI_IOCTL_SEND_COMMAND: - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; if (read_only) { unsigned char opcode = WRITE_6; @@ -1097,15 +1093,8 @@ return result; sdp->sgdebug = (char) val; return 0; - case SCSI_IOCTL_GET_IDLUN: - case SCSI_IOCTL_GET_BUS_NUMBER: - case SCSI_IOCTL_PROBE_HOST: - case SG_GET_TRANSFORM: - if (sdp->detached) - return -ENODEV; - return scsi_ioctl(sdp->device, cmd_in, p); case BLKSECTGET: - return put_user(queue_max_sectors(sdp->device->request_queue) * 512, + return put_user(max_sectors_bytes(sdp->device->request_queue), ip); case BLKTRACESETUP: return blk_trace_setup(sdp->device->request_queue, @@ -1119,11 +1108,25 @@ return blk_trace_startstop(sdp->device->request_queue, 0); case BLKTRACETEARDOWN: return blk_trace_remove(sdp->device->request_queue); + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_TRANSFORM: + case SG_SCSI_RESET: + if (atomic_read(&sdp->detaching)) + return -ENODEV; + break; default: if (read_only) return -EPERM; /* don't know so take safe approach */ - return scsi_ioctl(sdp->device, cmd_in, p); + break; } + + result = scsi_ioctl_block_when_processing_errors(sdp->device, + cmd_in, filp->f_flags & O_NDELAY); + if (result) + return result; + return scsi_ioctl(sdp->device, cmd_in, p); } #ifdef CONFIG_COMPAT @@ -1137,14 +1140,14 @@ return -ENXIO; sdev = sdp->device; - if (sdev->host->hostt->compat_ioctl) { + if (sdev->host->hostt->compat_ioctl) { int ret; ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); return ret; } - + return -ENOIOCTLCMD; } #endif @@ -1175,15 +1178,15 @@ } read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - if (sdp->detached) + if (atomic_read(&sdp->detaching)) res |= POLLHUP; else if (!sfp->cmd_q) { if (0 == count) res |= POLLOUT | POLLWRNORM; } else if (count < SG_MAX_QUEUE) res |= POLLOUT | POLLWRNORM; - SCSI_LOG_TIMEOUT(3, printk("sg_poll: %s, res=0x%x\n", - sdp->disk->disk_name, (int) res)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_poll: res=0x%x\n", (int) res)); return res; } @@ -1195,8 +1198,8 @@ if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_fasync: %s, mode=%d\n", - sdp->disk->disk_name, mode)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_fasync: mode=%d\n", mode)); return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1215,8 +1218,9 @@ offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rsv_schp->bufflen) return VM_FAULT_SIGBUS; - SCSI_LOG_TIMEOUT(3, printk("sg_vma_fault: offset=%lu, scatg=%d\n", - offset, rsv_schp->k_use_sg)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, + "sg_vma_fault: offset=%lu, scatg=%d\n", + offset, rsv_schp->k_use_sg)); sa = vma->vm_start; length = 1 << (PAGE_SHIFT + rsv_schp->page_order); for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { @@ -1251,8 +1255,9 @@ if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) return -ENXIO; req_sz = vma->vm_end - vma->vm_start; - SCSI_LOG_TIMEOUT(3, printk("sg_mmap starting, vm_start=%p, len=%d\n", - (void *) vma->vm_start, (int) req_sz)); + 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)); if (vma->vm_pgoff) return -EINVAL; /* want no offset */ rsv_schp = &sfp->reserve; @@ -1274,7 +1279,8 @@ return 0; } -static void sg_rq_end_io_usercontext(struct work_struct *work) +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; @@ -1287,7 +1293,8 @@ * This function is a "bottom half" handler that is called by the mid * level when a command is completed (or has failed). */ -static void sg_rq_end_io(struct request *rq, int uptodate) +static void +sg_rq_end_io(struct request *rq, int uptodate) { struct sg_request *srp = rq->end_io_data; Sg_device *sdp; @@ -1305,15 +1312,16 @@ return; sdp = sfp->parentdp; - if (unlikely(sdp->detached)) - printk(KERN_INFO "sg_rq_end_io: device detached\n"); + if (unlikely(atomic_read(&sdp->detaching))) + pr_info("%s: device detaching\n", __func__); sense = rq->sense; result = rq->errors; resid = rq->resid_len; - SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n", - sdp->disk->disk_name, srp->header.pack_id, result)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp, + "sg_cmd_done: pack_id=%d, res=0x%x\n", + srp->header.pack_id, result)); srp->header.resid = resid; ms = jiffies_to_msecs(jiffies); srp->header.duration = (ms > srp->header.duration) ? @@ -1329,7 +1337,7 @@ if ((sdp->sgdebug > 0) && ((CHECK_CONDITION == srp->header.masked_status) || (COMMAND_TERMINATED == srp->header.masked_status))) - __scsi_print_sense("sg_cmd_done", sense, + __scsi_print_sense(sdp->device, __func__, sense, SCSI_SENSE_BUFFERSIZE); /* Following if statement is a patch supplied by Eric Youngdale */ @@ -1345,6 +1353,17 @@ } /* Rely on write phase to clean out srp status values, so no "else" */ + /* + * Free the request as soon as it is complete so that its resources + * can be reused without waiting for userspace to read() the + * result. But keep the associated bio (if any) around until + * blk_rq_unmap_user() can be called from user context. + */ + srp->rq = NULL; + if (rq->cmd != rq->__cmd) + kfree(rq->cmd); + __blk_put_request(rq->q, rq); + write_lock_irqsave(&sfp->rq_list_lock, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan) @@ -1388,7 +1407,8 @@ static int sg_sysfs_valid = 0; -static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) +static Sg_device * +sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) { struct request_queue *q = scsidp->request_queue; Sg_device *sdp; @@ -1398,7 +1418,8 @@ sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL); if (!sdp) { - printk(KERN_WARNING "kmalloc Sg_device failure\n"); + sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device " + "failure\n", __func__); return ERR_PTR(-ENOMEM); } @@ -1413,20 +1434,25 @@ scsidp->type, SG_MAX_DEVS - 1); error = -ENODEV; } else { - printk(KERN_WARNING - "idr allocation Sg_device failure: %d\n", error); + sdev_printk(KERN_WARNING, scsidp, "%s: idr " + "allocation Sg_device failure: %d\n", + __func__, error); } goto out_unlock; } k = error; - SCSI_LOG_TIMEOUT(3, printk("sg_alloc: dev=%d \n", k)); + SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, + "sg_alloc: dev=%d \n", k)); sprintf(disk->disk_name, "sg%d", k); disk->first_minor = k; sdp->disk = disk; sdp->device = scsidp; + mutex_init(&sdp->open_rel_lock); INIT_LIST_HEAD(&sdp->sfds); - init_waitqueue_head(&sdp->o_excl_wait); + init_waitqueue_head(&sdp->open_wait); + atomic_set(&sdp->detaching, 0); + rwlock_init(&sdp->sfd_lock); sdp->sg_tablesize = queue_max_segments(q); sdp->index = k; kref_init(&sdp->d_ref); @@ -1444,7 +1470,7 @@ } static int -sg_add(struct device *cl_dev, struct class_interface *cl_intf) +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; @@ -1455,7 +1481,7 @@ disk = alloc_disk(1); if (!disk) { - printk(KERN_WARNING "alloc_disk failed\n"); + pr_warn("%s: alloc_disk failed\n", __func__); return -ENOMEM; } disk->major = SCSI_GENERIC_MAJOR; @@ -1463,7 +1489,7 @@ error = -ENOMEM; cdev = cdev_alloc(); if (!cdev) { - printk(KERN_WARNING "cdev_alloc failed\n"); + pr_warn("%s: cdev_alloc failed\n", __func__); goto out; } cdev->owner = THIS_MODULE; @@ -1471,7 +1497,7 @@ sdp = sg_alloc(disk, scsidp); if (IS_ERR(sdp)) { - printk(KERN_WARNING "sg_alloc failed\n"); + pr_warn("%s: sg_alloc failed\n", __func__); error = PTR_ERR(sdp); goto out; } @@ -1489,22 +1515,20 @@ sdp->index), sdp, "%s", disk->disk_name); if (IS_ERR(sg_class_member)) { - printk(KERN_ERR "sg_add: " - "device_create failed\n"); + pr_err("%s: device_create failed\n", __func__); error = PTR_ERR(sg_class_member); goto cdev_add_err; } error = sysfs_create_link(&scsidp->sdev_gendev.kobj, &sg_class_member->kobj, "generic"); if (error) - printk(KERN_ERR "sg_add: unable to make symlink " - "'generic' back to sg%d\n", sdp->index); + pr_err("%s: unable to make symlink 'generic' back " + "to sg%d\n", __func__, sdp->index); } else - printk(KERN_WARNING "sg_add: sg_sys Invalid\n"); + pr_warn("%s: sg_sys Invalid\n", __func__); - sdev_printk(KERN_NOTICE, scsidp, - "Attached scsi generic sg%d type %d\n", sdp->index, - scsidp->type); + sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d " + "type %d\n", sdp->index, scsidp->type); dev_set_drvdata(cl_dev, sdp); @@ -1523,7 +1547,8 @@ return error; } -static void sg_device_destroy(struct kref *kref) +static void +sg_device_destroy(struct kref *kref) { struct sg_device *sdp = container_of(kref, struct sg_device, d_ref); unsigned long flags; @@ -1538,40 +1563,45 @@ write_unlock_irqrestore(&sg_index_lock, flags); SCSI_LOG_TIMEOUT(3, - printk("sg_device_destroy: %s\n", - sdp->disk->disk_name)); + sg_printk(KERN_INFO, sdp, "sg_device_destroy\n")); put_disk(sdp->disk); kfree(sdp); } -static void sg_remove(struct device *cl_dev, struct class_interface *cl_intf) +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); unsigned long iflags; Sg_fd *sfp; + int val; - if (!sdp || sdp->detached) + if (!sdp) return; + /* want sdp->detaching non-zero as soon as possible */ + val = atomic_inc_return(&sdp->detaching); + if (val > 1) + return; /* only want to do following once per device */ - SCSI_LOG_TIMEOUT(3, printk("sg_remove: %s\n", sdp->disk->disk_name)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "%s\n", __func__)); - /* Need a write lock to set sdp->detached. */ - write_lock_irqsave(&sg_index_lock, iflags); - sdp->detached = 1; + read_lock_irqsave(&sdp->sfd_lock, iflags); list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { - wake_up_interruptible(&sfp->read_wait); + wake_up_interruptible_all(&sfp->read_wait); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); } - write_unlock_irqrestore(&sg_index_lock, iflags); + wake_up_interruptible_all(&sdp->open_wait); + read_unlock_irqrestore(&sdp->sfd_lock, iflags); sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index)); cdev_del(sdp->cdev); sdp->cdev = NULL; - sg_put_dev(sdp); + kref_put(&sdp->d_ref, sg_device_destroy); } module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); @@ -1604,7 +1634,7 @@ else def_reserved_size = sg_big_buff; - rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), + rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); if (rc) return rc; @@ -1641,7 +1671,8 @@ idr_destroy(&sg_index_idr); } -static int sg_start_req(Sg_request *srp, unsigned char *cmd) +static int +sg_start_req(Sg_request *srp, unsigned char *cmd) { int res; struct request *rq; @@ -1655,18 +1686,45 @@ 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; - SCSI_LOG_TIMEOUT(4, printk(KERN_INFO "sg_start_req: dxfer_len=%d\n", - dxfer_len)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, + "sg_start_req: dxfer_len=%d\n", + dxfer_len)); + + if (hp->cmd_len > BLK_MAX_CDB) { + long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL); + if (!long_cmdp) + return -ENOMEM; + } - rq = blk_get_request(q, rw, GFP_ATOMIC); - if (!rq) - return -ENOMEM; + /* + * NOTE + * + * With scsi-mq enabled, there are a fixed number of preallocated + * requests equal in number to shost->can_queue. If all of the + * preallocated requests are already in use, then using GFP_ATOMIC with + * blk_get_request() will return -EWOULDBLOCK, whereas using GFP_KERNEL + * will cause blk_get_request() to sleep until an active command + * completes, freeing up a request. Neither option is ideal, but + * GFP_KERNEL is the better choice to prevent userspace from getting an + * unexpected EWOULDBLOCK. + * + * With scsi-mq disabled, blk_get_request() with GFP_KERNEL usually + * does not sleep except under memory pressure. + */ + rq = blk_get_request(q, rw, GFP_KERNEL); + if (IS_ERR(rq)) { + kfree(long_cmdp); + return PTR_ERR(rq); + } - memcpy(rq->cmd, cmd, hp->cmd_len); + blk_rq_set_block_pc(rq); + if (hp->cmd_len > BLK_MAX_CDB) + rq->cmd = long_cmdp; + memcpy(rq->cmd, cmd, hp->cmd_len); rq->cmd_len = hp->cmd_len; - rq->cmd_type = REQ_TYPE_BLOCK_PC; srp->rq = rq; rq->end_io_data = srp; @@ -1704,26 +1762,21 @@ md->from_user = 0; } - if (unlikely(iov_count > UIO_MAXIOV)) - return -EINVAL; - if (iov_count) { - int len, size = sizeof(struct sg_iovec) * iov_count; - struct iovec *iov; + struct iovec *iov = NULL; + struct iov_iter i; - iov = memdup_user(hp->dxferp, size); - if (IS_ERR(iov)) - return PTR_ERR(iov); - - len = iov_length(iov, iov_count); - if (hp->dxfer_len < len) { - iov_count = iov_shorten(iov, iov_count, hp->dxfer_len); - len = hp->dxfer_len; - } - - res = blk_rq_map_user_iov(q, rq, md, (struct sg_iovec *)iov, - iov_count, - len, GFP_ATOMIC); + res = import_iovec(rw, hp->dxferp, iov_count, 0, &iov, &i); + if (res < 0) + return res; + + iov_iter_truncate(&i, hp->dxfer_len); + if (!iov_iter_count(&i)) { + kfree(iov); + return -EINVAL; + } + + res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC); kfree(iov); } else res = blk_rq_map_user(q, rq, md, hp->dxferp, @@ -1740,25 +1793,30 @@ return res; } -static int sg_finish_rem_req(Sg_request * srp) +static int +sg_finish_rem_req(Sg_request *srp) { int ret = 0; Sg_fd *sfp = srp->parentfp; Sg_scatter_hold *req_schp = &srp->data; - SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); - if (srp->rq) { - if (srp->bio) - ret = blk_rq_unmap_user(srp->bio); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, + "sg_finish_rem_req: res_used=%d\n", + (int) srp->res_used)); + if (srp->bio) + ret = blk_rq_unmap_user(srp->bio); + if (srp->rq) { + if (srp->rq->cmd != srp->rq->__cmd) + kfree(srp->rq->cmd); blk_put_request(srp->rq); } if (srp->res_used) sg_unlink_reserve(sfp, srp); else - sg_remove_scat(req_schp); + sg_remove_scat(sfp, req_schp); sg_remove_request(sfp, srp); @@ -1792,8 +1850,9 @@ ++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, printk("sg_build_indirect: buff_size=%d, blk_size=%d\n", - buff_size, blk_size)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, + "sg_build_indirect: buff_size=%d, blk_size=%d\n", + buff_size, blk_size)); /* N.B. ret_sz carried into this block ... */ mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); @@ -1836,14 +1895,16 @@ } } - SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k=%d, num=%d, " - "ret_sz=%d\n", k, num, ret_sz)); + SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, + "sg_build_indirect: k=%d, num=%d, ret_sz=%d\n", + k, num, ret_sz)); } /* end of for loop */ schp->page_order = order; schp->k_use_sg = k; - SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, " - "rem_sz=%d\n", k, rem_sz)); + SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, + "sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", + k, rem_sz)); schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ @@ -1860,17 +1921,19 @@ } static void -sg_remove_scat(Sg_scatter_hold * schp) +sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp) { - SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); + 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) { if (!schp->dio_in_use) { int k; for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { - SCSI_LOG_TIMEOUT(5, printk( - "sg_remove_scat: k=%d, pg=0x%p\n", - k, schp->pages[k])); + SCSI_LOG_TIMEOUT(5, + sg_printk(KERN_INFO, sfp->parentdp, + "sg_remove_scat: k=%d, pg=0x%p\n", + k, schp->pages[k])); __free_pages(schp->pages[k], schp->page_order); } @@ -1886,8 +1949,9 @@ Sg_scatter_hold *schp = &srp->data; int k, num; - SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n", - num_read_xfer)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, + "sg_read_oxfer: num_read_xfer=%d\n", + num_read_xfer)); if ((!outp) || (num_read_xfer <= 0)) return 0; @@ -1917,14 +1981,15 @@ { Sg_scatter_hold *schp = &sfp->reserve; - SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, + "sg_build_reserve: req_size=%d\n", 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(schp); + sg_remove_scat(sfp, schp); req_size >>= 1; /* divide by 2 */ } while (req_size > (PAGE_SIZE / 2)); } @@ -1937,7 +2002,8 @@ int k, num, rem; srp->res_used = 1; - SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); + 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); @@ -1955,7 +2021,8 @@ } if (k >= rsv_schp->k_use_sg) - SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); + SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, + "sg_link_reserve: BAD size\n")); } static void @@ -1963,8 +2030,9 @@ { Sg_scatter_hold *req_schp = &srp->data; - SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n", - (int) req_schp->k_use_sg)); + 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; req_schp->pages = NULL; @@ -2069,7 +2137,7 @@ } static Sg_fd * -sg_add_sfp(Sg_device * sdp, int dev) +sg_add_sfp(Sg_device * sdp) { Sg_fd *sfp; unsigned long iflags; @@ -2077,7 +2145,7 @@ sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); if (!sfp) - return NULL; + return ERR_PTR(-ENOMEM); init_waitqueue_head(&sfp->read_wait); rwlock_init(&sfp->rq_list_lock); @@ -2091,25 +2159,33 @@ sfp->cmd_q = SG_DEF_COMMAND_Q; sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; sfp->parentdp = sdp; - write_lock_irqsave(&sg_index_lock, iflags); + write_lock_irqsave(&sdp->sfd_lock, iflags); + if (atomic_read(&sdp->detaching)) { + write_unlock_irqrestore(&sdp->sfd_lock, iflags); + return ERR_PTR(-ENODEV); + } list_add_tail(&sfp->sfd_siblings, &sdp->sfds); - write_unlock_irqrestore(&sg_index_lock, iflags); - SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); + write_unlock_irqrestore(&sdp->sfd_lock, iflags); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_add_sfp: sfp=0x%p\n", sfp)); if (unlikely(sg_big_buff != def_reserved_size)) sg_big_buff = def_reserved_size; bufflen = min_t(int, sg_big_buff, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); sg_build_reserve(sfp, bufflen); - SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", - sfp->reserve.bufflen, sfp->reserve.k_use_sg)); + 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)); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); return sfp; } -static void sg_remove_sfp_usercontext(struct work_struct *work) +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; @@ -2119,34 +2195,32 @@ sg_finish_rem_req(sfp->headrp); if (sfp->reserve.bufflen > 0) { - SCSI_LOG_TIMEOUT(6, - printk("sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", + 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_scat(&sfp->reserve); + sg_remove_scat(sfp, &sfp->reserve); } - SCSI_LOG_TIMEOUT(6, - printk("sg_remove_sfp: %s, sfp=0x%p\n", - sdp->disk->disk_name, - sfp)); + SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, + "sg_remove_sfp: sfp=0x%p\n", sfp)); kfree(sfp); scsi_device_put(sdp->device); - sg_put_dev(sdp); + kref_put(&sdp->d_ref, sg_device_destroy); module_put(THIS_MODULE); } -static void sg_remove_sfp(struct kref *kref) +static void +sg_remove_sfp(struct kref *kref) { struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref); struct sg_device *sdp = sfp->parentdp; unsigned long iflags; - write_lock_irqsave(&sg_index_lock, iflags); + write_lock_irqsave(&sdp->sfd_lock, iflags); list_del(&sfp->sfd_siblings); - write_unlock_irqrestore(&sg_index_lock, iflags); - wake_up_interruptible(&sdp->o_excl_wait); + write_unlock_irqrestore(&sdp->sfd_lock, iflags); INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); schedule_work(&sfp->ew.work); @@ -2197,7 +2271,8 @@ return idr_find(&sg_index_idr, dev); } -static Sg_device *sg_get_dev(int dev) +static Sg_device * +sg_get_dev(int dev) { struct sg_device *sdp; unsigned long flags; @@ -2206,8 +2281,8 @@ sdp = sg_lookup_dev(dev); if (!sdp) sdp = ERR_PTR(-ENXIO); - else if (sdp->detached) { - /* If sdp->detached, then the refcount may already be 0, in + else if (atomic_read(&sdp->detaching)) { + /* If sdp->detaching, then the refcount may already be 0, in * which case it would be a bug to do kref_get(). */ sdp = ERR_PTR(-ENODEV); @@ -2218,11 +2293,6 @@ return sdp; } -static void sg_put_dev(struct sg_device *sdp) -{ - kref_put(&sdp->d_ref, sg_device_destroy); -} - #ifdef CONFIG_SCSI_PROC_FS static struct proc_dir_entry *sg_proc_sgp = NULL; @@ -2244,7 +2314,7 @@ }; static int sg_proc_single_open_dressz(struct inode *inode, struct file *file); -static ssize_t sg_proc_write_dressz(struct file *filp, +static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off); static const struct file_operations dressz_fops = { .owner = THIS_MODULE, @@ -2384,7 +2454,7 @@ return single_open(file, sg_proc_seq_show_int, &sg_allow_dio); } -static ssize_t +static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -2405,7 +2475,7 @@ return single_open(file, sg_proc_seq_show_int, &sg_big_buff); } -static ssize_t +static ssize_t sg_proc_write_dressz(struct file *filp, const char __user *buffer, size_t count, loff_t *off) { @@ -2439,8 +2509,7 @@ static int sg_proc_seq_show_devhdr(struct seq_file *s, void *v) { - seq_printf(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\t" - "online\n"); + seq_puts(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); return 0; } @@ -2496,16 +2565,19 @@ read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - if (sdp && (scsidp = sdp->device) && (!sdp->detached)) - seq_printf(s, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + if ((NULL == sdp) || (NULL == 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; + 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, 1, (int) scsidp->queue_depth, - (int) scsidp->device_busy, + (int) atomic_read(&scsidp->device_busy), (int) scsi_device_online(scsidp)); - else - seq_printf(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); + } read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } @@ -2524,11 +2596,12 @@ read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - if (sdp && (scsidp = sdp->device) && (!sdp->detached)) + scsidp = sdp ? sdp->device : NULL; + if (sdp && scsidp && (!atomic_read(&sdp->detaching))) seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n", scsidp->vendor, scsidp->model, scsidp->rev); else - seq_printf(s, "\n"); + seq_puts(s, "\n"); read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } @@ -2562,7 +2635,7 @@ hp = &srp->header; new_interface = (hp->interface_id == '\0') ? 0 : 1; if (srp->res_used) { - if (new_interface && + if (new_interface && (SG_FLAG_MMAP_IO & hp->flags)) cp = " mmap>> "; else @@ -2573,12 +2646,12 @@ else cp = " "; } - seq_printf(s, cp); + seq_puts(s, cp); blen = srp->data.bufflen; usg = srp->data.k_use_sg; - seq_printf(s, srp->done ? - ((1 == srp->done) ? "rcv:" : "fin:") - : "act:"); + 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) @@ -2594,7 +2667,7 @@ (int) srp->data.cmd_opcode); } if (0 == m) - seq_printf(s, " No requests active\n"); + seq_puts(s, " No requests active\n"); read_unlock(&fp->rq_list_lock); } } @@ -2610,31 +2683,34 @@ Sg_device *sdp; unsigned long iflags; - if (it && (0 == it->index)) { - seq_printf(s, "max_active_device=%d(origin 1)\n", - (int)it->max); - seq_printf(s, " def_reserved_size=%d\n", sg_big_buff); - } + if (it && (0 == it->index)) + seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", + (int)it->max, sg_big_buff); read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - if (sdp && !list_empty(&sdp->sfds)) { - struct scsi_device *scsidp = sdp->device; - + if (NULL == sdp) + goto skip; + read_lock(&sdp->sfd_lock); + if (!list_empty(&sdp->sfds)) { seq_printf(s, " >>> device=%s ", sdp->disk->disk_name); - if (sdp->detached) - seq_printf(s, "detached pending close "); - else - seq_printf - (s, "scsi%d chan=%d id=%d lun=%d em=%d", - scsidp->host->host_no, - scsidp->channel, scsidp->id, - scsidp->lun, - scsidp->host->hostt->emulated); - seq_printf(s, " sg_tablesize=%d excl=%d\n", - sdp->sg_tablesize, get_exclude(sdp)); + if (atomic_read(&sdp->detaching)) + seq_puts(s, "detaching pending close "); + else if (sdp->device) { + struct scsi_device *scsidp = sdp->device; + + seq_printf(s, "%d:%d:%d:%llu em=%d", + scsidp->host->host_no, + scsidp->channel, scsidp->id, + scsidp->lun, + scsidp->host->hostt->emulated); + } + seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n", + sdp->sg_tablesize, sdp->exclude, sdp->open_cnt); sg_proc_debug_helper(s, sdp); } + read_unlock(&sdp->sfd_lock); +skip: read_unlock_irqrestore(&sg_index_lock, iflags); return 0; }