diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index df5e961..843c66e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -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 - 2013 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,8 +16,8 @@ * */ -static int sg_version_num = 30534; /* 2 digits for each component */ -#define SG_VERSION_STR "3.5.34" +static int sg_version_num = 30535; /* 2 digits for each component */ +#define SG_VERSION_STR "3.5.35" /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: @@ -51,6 +49,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #include #include #include +#include #include #include "scsi.h" @@ -64,7 +63,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include -static char *sg_version_date = "20061027"; +static char *sg_version_date = "20131111"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -87,6 +86,8 @@ static void sg_proc_cleanup(void); #define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) +#define SG_LOG(LEVEL, CMD) SCSI_LOG_TIMEOUT(LEVEL, CMD) + 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 @@ -102,18 +103,16 @@ static int scatter_elem_sz_prev = SG_SCATTER_SZ; #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 +145,7 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ } 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 */ @@ -170,14 +168,15 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ 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 */ + atomic_t exclude; /* 1->open(O_EXCL) succeeded and is active */ + atomic_t 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] */ @@ -208,7 +207,7 @@ 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) @@ -225,38 +224,19 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) 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) -{ - unsigned long flags; - - spin_lock_irqsave(&sg_open_exclusive_lock, flags); - sdp->exclude = val; - spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); - return val; -} - -static int sfds_list_empty(Sg_device *sdp) +static int +open_wait_event(Sg_device *sdp, int excl_case) { - unsigned long flags; - int ret; + int retval; - read_lock_irqsave(&sg_index_lock, flags); - ret = list_empty(&sdp->sfds); - read_unlock_irqrestore(&sg_index_lock, flags); - return ret; + retval = wait_event_interruptible(sdp->open_wait, + (atomic_read(&sdp->detaching)) || + (excl_case ? !atomic_read(&sdp->exclude) + : (atomic_read(&sdp->open_cnt) < 1))); + return atomic_read(&sdp->detaching) ? -ENODEV : retval; } +/* Returns 0 on success, else a negated errno value */ static int sg_open(struct inode *inode, struct file *filp) { @@ -265,18 +245,19 @@ sg_open(struct inode *inode, struct file *filp) 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)); + SG_LOG(3, pr_info("%s: dev=%d, flags=0x%x\n", __func__, 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; } - /* This driver's module count bumped by fops_get in */ /* Prevent the device driver from vanishing while we sleep */ retval = scsi_device_get(sdp->device); @@ -287,6 +268,9 @@ sg_open(struct inode *inode, struct file *filp) 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,80 +278,101 @@ sg_open(struct inode *inode, struct file *filp) 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; + mutex_lock(&sdp->open_rel_lock); + if (flags & O_NONBLOCK) { + if (flags & O_EXCL) { + if (atomic_read(&sdp->open_cnt) > 0) { + retval = -EBUSY; + goto error_mutex_locked; + } + } else { + if (atomic_read(&sdp->exclude)) { + retval = -EBUSY; + goto error_mutex_locked; + } } - res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp)); - if (res) { - retval = res; /* -ERESTARTSYS because signal hit process */ - goto error_out; + } else { + if (flags & O_EXCL) { + while (atomic_read(&sdp->open_cnt) > 0) { + mutex_unlock(&sdp->open_rel_lock); + retval = open_wait_event(sdp, 0); + if (retval) /* -ERESTARTSYS or -ENODEV */ + goto error_out; + mutex_lock(&sdp->open_rel_lock); + } + } else { + while (atomic_read(&sdp->exclude)) { + mutex_unlock(&sdp->open_rel_lock); + retval = open_wait_event(sdp, 1); + if (retval) /* -ERESTARTSYS or -ENODEV */ + goto error_out; + mutex_lock(&sdp->open_rel_lock); + } } } - 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) + atomic_set(&sdp->exclude, 1); + + if (atomic_read(&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))) + sfp = sg_add_sfp(sdp, dev); + if (!IS_ERR(sfp)) { filp->private_data = sfp; - else { + retval = 0; + atomic_inc(&sdp->open_cnt); + mutex_unlock(&sdp->open_rel_lock); + } else { + retval = PTR_ERR(sfp); if (flags & O_EXCL) { - set_exclude(sdp, 0); /* undo if error */ - wake_up_interruptible(&sdp->o_excl_wait); + atomic_set(&sdp->exclude, 0); /* undo if error */ + wake_up_interruptible(&sdp->open_wait); } - retval = -ENOMEM; - goto error_out; - } - retval = 0; +error_mutex_locked: + mutex_unlock(&sdp->open_rel_lock); 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; } -/* 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) { + int rem_open_cnt; Sg_device *sdp; Sg_fd *sfp; 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); + SG_LOG(3, pr_info("%s: %s\n", __func__, sdp->disk->disk_name)); + mutex_lock(&sdp->open_rel_lock); scsi_autopm_put_device(sdp->device); kref_put(&sfp->f_ref, sg_remove_sfp); + rem_open_cnt = atomic_dec_return(&sdp->open_cnt); + if (unlikely(rem_open_cnt < 0)) { + atomic_set(&sdp->open_cnt, 0); + rem_open_cnt = 0; + pr_err("%s: open_cnt went negative, fix\n", __func__); + } + /* possibly many open()s waiting on exlude clearing , start many; + * only open(O_EXCL)s wait on 0==rem_open_cnt so only start one */ + if (atomic_add_unless(&sdp->exclude, -1, 0)) + wake_up_interruptible_all(&sdp->open_wait); + else if (0 == rem_open_cnt) + wake_up_interruptible(&sdp->open_wait); + mutex_unlock(&sdp->open_rel_lock); return 0; } @@ -384,8 +389,8 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) 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)); + SG_LOG(3, pr_info("%s: %s, count=%d\n", __func__, sdp->disk->disk_name, + (int) count)); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -419,7 +424,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) } 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 +433,9 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) 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; } @@ -570,9 +575,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 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) + SG_LOG(3, pr_info("%s: %s, count=%d\n", __func__, sdp->disk->disk_name, + (int) count)); + if (atomic_read(&sdp->detaching)) return -ENODEV; if (!((filp->f_flags & O_NONBLOCK) || scsi_block_when_processing_errors(sdp->device))) @@ -592,14 +597,15 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 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")); + SG_LOG(1, pr_info("%s: queue full\n", __func__)); 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")); + SG_LOG(1, pr_info("%s: command length too long\n", + __func__)); sfp->next_cmd_len = 0; sg_remove_request(sfp, srp); return -EIO; @@ -611,8 +617,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) if ((opcode >= 0xc0) && old_hdr.twelve_byte) cmd_size = 12; } - SCSI_LOG_TIMEOUT(4, printk( - "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size)); + SG_LOG(4, pr_info("%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (int) opcode, cmd_size)); /* Determine buffer size. */ input_size = count - cmd_size; mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; @@ -686,7 +692,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ if (!(srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); + SG_LOG(1, pr_info("%s: queue full\n", __func__)); return -EDOM; } srp->sg_io_owned = sg_io_owned; @@ -755,16 +761,16 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, 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)); + SG_LOG(4, pr_info("%s: scsi opcode=0x%02x, cmd_size=%d\n", __func__, + (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)); + SG_LOG(1, pr_info("%s: start_req err=%d\n", __func__, k)); sg_finish_rem_req(srp); return k; /* probably out of space --> ENOMEM */ } - if (sdp->detached) { + if (atomic_read(&sdp->detaching)) { if (srp->bio) blk_end_request_all(srp->rq, -EIO); sg_finish_rem_req(srp); @@ -820,13 +826,13 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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)); + SG_LOG(3, pr_info("%s: %s, cmd=0x%x\n", __func__, sdp->disk->disk_name, + (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; @@ -837,8 +843,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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) { @@ -877,7 +883,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) sg_build_reserve(sfp, val); } } else { - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; sfp->low_dma = sdp->device->host->unchecked_isa_dma; } @@ -890,7 +896,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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); @@ -993,7 +999,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) unsigned int ms; rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE, - GFP_KERNEL); + GFP_KERNEL); if (!rinfo) return -ENOMEM; read_lock_irqsave(&sfp->rq_list_lock, iflags); @@ -1032,11 +1038,11 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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) + if (atomic_read(&sdp->detaching)) return -ENODEV; if (filp->f_flags & O_NONBLOCK) { if (scsi_host_in_recovery(sdp->device->host)) @@ -1069,7 +1075,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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; @@ -1091,7 +1097,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) case SCSI_IOCTL_GET_BUS_NUMBER: case SCSI_IOCTL_PROBE_HOST: case SG_GET_TRANSFORM: - if (sdp->detached) + if (atomic_read(&sdp->detaching)) return -ENODEV; return scsi_ioctl(sdp->device, cmd_in, p); case BLKSECTGET: @@ -1165,15 +1171,15 @@ sg_poll(struct file *filp, poll_table * wait) } 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)); + SG_LOG(3, pr_info("%s: %s, res=0x%x\n", __func__, sdp->disk->disk_name, + (int) res)); return res; } @@ -1185,8 +1191,8 @@ sg_fasync(int fd, struct file *filp, int mode) 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)); + SG_LOG(3, pr_info("%s: %s, mode=%d\n", __func__, sdp->disk->disk_name, + mode)); return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1205,8 +1211,8 @@ sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 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)); + SG_LOG(3, pr_info("%s: offset=%lu, scatg=%d\n", __func__, 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++) { @@ -1241,8 +1247,8 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) 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)); + SG_LOG(3, pr_info("%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; @@ -1295,15 +1301,15 @@ static void sg_rq_end_io(struct request *rq, int uptodate) return; sdp = sfp->parentdp; - if (unlikely(sdp->detached)) - printk(KERN_INFO "sg_rq_end_io: device detached\n"); + if (unlikely(atomic_read(&sdp->detaching))) + SG_LOG(4, 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)); + SG_LOG(4, pr_info("%s: %s, pack_id=%d, res=0x%x\n", __func__, + sdp->disk->disk_name, srp->header.pack_id, result)); srp->header.resid = resid; ms = jiffies_to_msecs(jiffies); srp->header.duration = (ms > srp->header.duration) ? @@ -1319,7 +1325,7 @@ static void sg_rq_end_io(struct request *rq, int uptodate) 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("sg_rq_end_io", sense, SCSI_SENSE_BUFFERSIZE); /* Following if statement is a patch supplied by Eric Youngdale */ @@ -1388,7 +1394,7 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL); if (!sdp) { - printk(KERN_WARNING "kmalloc Sg_device failure\n"); + pr_warn("%s: kmalloc Sg_device failure\n", __func__); return ERR_PTR(-ENOMEM); } @@ -1403,20 +1409,25 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) scsidp->type, SG_MAX_DEVS - 1); error = -ENODEV; } else { - printk(KERN_WARNING - "idr allocation Sg_device failure: %d\n", error); + pr_warn("%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)); + SG_LOG(3, pr_info("%s: dev=%d\n", __func__, 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); + atomic_set(&sdp->exclude, 0); + atomic_set(&sdp->open_cnt, 0); + rwlock_init(&sdp->sfd_lock); sdp->sg_tablesize = queue_max_segments(q); sdp->index = k; kref_init(&sdp->d_ref); @@ -1434,7 +1445,7 @@ out_unlock: } 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; @@ -1445,7 +1456,7 @@ sg_add(struct device *cl_dev, struct class_interface *cl_intf) 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; @@ -1453,7 +1464,7 @@ sg_add(struct device *cl_dev, struct class_interface *cl_intf) 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; @@ -1461,7 +1472,7 @@ sg_add(struct device *cl_dev, struct class_interface *cl_intf) 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; } @@ -1479,22 +1490,20 @@ sg_add(struct device *cl_dev, struct class_interface *cl_intf) 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: symlink 'generic' to sg%d failed\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); @@ -1527,41 +1536,44 @@ static void sg_device_destroy(struct kref *kref) idr_remove(&sg_index_idr, sdp->index); write_unlock_irqrestore(&sg_index_lock, flags); - SCSI_LOG_TIMEOUT(3, - printk("sg_device_destroy: %s\n", - sdp->disk->disk_name)); + SG_LOG(3, pr_info("%s: %s\n", __func__, sdp->disk->disk_name)); 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)); + SG_LOG(3, pr_info("%s: %s\n", __func__, sdp->disk->disk_name)); - /* 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); @@ -1646,8 +1658,7 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) struct rq_map_data *md, map_data; int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; - SCSI_LOG_TIMEOUT(4, printk(KERN_INFO "sg_start_req: dxfer_len=%d\n", - dxfer_len)); + SG_LOG(4, pr_info("%s: dxfer_len=%d\n", __func__, dxfer_len)); rq = blk_get_request(q, rw, GFP_ATOMIC); if (!rq) @@ -1734,7 +1745,7 @@ static int sg_finish_rem_req(Sg_request * srp) 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)); + SG_LOG(4, pr_info("%s: res_used=%d\n", __func__, (int) srp->res_used)); if (srp->rq) { if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1779,8 +1790,8 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) ++blk_size; /* don't know why */ /* round request up to next highest SG_SECTOR_SZ byte boundary */ blk_size = ALIGN(blk_size, SG_SECTOR_SZ); - SCSI_LOG_TIMEOUT(4, printk("sg_build_indirect: buff_size=%d, blk_size=%d\n", - buff_size, blk_size)); + SG_LOG(4, pr_info("%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); @@ -1823,14 +1834,13 @@ retry: } } - SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k=%d, num=%d, " - "ret_sz=%d\n", k, num, ret_sz)); + SG_LOG(5, pr_info("%s: k=%d, num=%d, ret_sz=%d\n", __func__, + k, num, ret_sz)); } /* end of for loop */ schp->page_order = order; schp->k_use_sg = k; - SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, " - "rem_sz=%d\n", k, rem_sz)); + SG_LOG(5, pr_info("%s: k_use_sg=%d, rem_sz=%d\n", __func__, k, rem_sz)); schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ @@ -1849,15 +1859,14 @@ out: static void sg_remove_scat(Sg_scatter_hold * schp) { - SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); + SG_LOG(4, pr_info("%s: k_use_sg=%d\n", __func__, schp->k_use_sg)); if (schp->pages && schp->sglist_len > 0) { if (!schp->dio_in_use) { int k; for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { - SCSI_LOG_TIMEOUT(5, printk( - "sg_remove_scat: k=%d, pg=0x%p\n", - k, schp->pages[k])); + SG_LOG(5, pr_info("%s: k=%d, pg=0x%p\n", + __func__, k, schp->pages[k])); __free_pages(schp->pages[k], schp->page_order); } @@ -1873,8 +1882,7 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) 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)); + SG_LOG(4, pr_info("%s: num_read_xfer=%d\n", __func__, num_read_xfer)); if ((!outp) || (num_read_xfer <= 0)) return 0; @@ -1904,7 +1912,7 @@ sg_build_reserve(Sg_fd * sfp, int req_size) { Sg_scatter_hold *schp = &sfp->reserve; - SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); + SG_LOG(4, pr_info("%s: req_size=%d\n", __func__, req_size)); do { if (req_size < PAGE_SIZE) req_size = PAGE_SIZE; @@ -1924,7 +1932,7 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) int k, num, rem; srp->res_used = 1; - SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); + SG_LOG(4, pr_info("%s: size=%d\n", __func__, size)); rem = size; num = 1 << (PAGE_SHIFT + rsv_schp->page_order); @@ -1942,7 +1950,7 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) } if (k >= rsv_schp->k_use_sg) - SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); + SG_LOG(1, pr_info("%s: BAD size\n", __func__)); } static void @@ -1950,8 +1958,8 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) { 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)); + SG_LOG(4, pr_info("%s: req->k_use_sg=%d\n", __func__, + (int) req_schp->k_use_sg)); req_schp->k_use_sg = 0; req_schp->bufflen = 0; req_schp->pages = NULL; @@ -2064,7 +2072,7 @@ sg_add_sfp(Sg_device * sdp, int dev) 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); @@ -2078,18 +2086,22 @@ sg_add_sfp(Sg_device * sdp, int dev) 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); + SG_LOG(3, pr_info("%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, queue_max_sectors(sdp->device->request_queue) * 512); 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)); + SG_LOG(3, pr_info("%s: bufflen=%d, k_use_sg=%d\n", __func__, + sfp->reserve.bufflen, sfp->reserve.k_use_sg)); kref_get(&sdp->d_ref); __module_get(THIS_MODULE); @@ -2106,21 +2118,18 @@ static void sg_remove_sfp_usercontext(struct work_struct *work) 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", - (int) sfp->reserve.bufflen, - (int) sfp->reserve.k_use_sg)); + SG_LOG(6, pr_info("%s: bufflen=%d, k_use_sg=%d\n", __func__, + (int) sfp->reserve.bufflen, + (int) sfp->reserve.k_use_sg)); sg_remove_scat(&sfp->reserve); } - SCSI_LOG_TIMEOUT(6, - printk("sg_remove_sfp: %s, sfp=0x%p\n", - sdp->disk->disk_name, - sfp)); + SG_LOG(6, pr_info("%s: %s, sfp=0x%p\n", __func__, sdp->disk->disk_name, + sfp)); kfree(sfp); scsi_device_put(sdp->device); - sg_put_dev(sdp); + kref_put(&sdp->d_ref, sg_device_destroy); module_put(THIS_MODULE); } @@ -2130,10 +2139,9 @@ static void sg_remove_sfp(struct kref *kref) 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); @@ -2193,8 +2201,8 @@ static Sg_device *sg_get_dev(int dev) 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); @@ -2205,11 +2213,6 @@ static Sg_device *sg_get_dev(int dev) 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; @@ -2426,8 +2429,7 @@ static int sg_proc_single_open_version(struct inode *inode, struct file *file) 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; } @@ -2483,7 +2485,11 @@ static int 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 (sdp && (scsidp = sdp->device) && (!sdp->detached)) + 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%d\t%d\t%d\t%d\t%d\t%d\n", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, (int) scsidp->type, @@ -2491,8 +2497,7 @@ static int sg_proc_seq_show_dev(struct seq_file *s, void *v) (int) scsidp->queue_depth, (int) 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; } @@ -2511,11 +2516,12 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) read_lock_irqsave(&sg_index_lock, iflags); sdp = it ? sg_lookup_dev(it->index) : NULL; - 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; } @@ -2560,7 +2566,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) 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 ? @@ -2581,7 +2587,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) (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); } } @@ -2597,31 +2603,35 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) 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:%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 open_cnt=%d\n", + sdp->sg_tablesize, atomic_read(&sdp->exclude), + atomic_read(&sdp->open_cnt)); sg_proc_debug_helper(s, sdp); } + read_unlock(&sdp->sfd_lock); +skip: read_unlock_irqrestore(&sg_index_lock, iflags); return 0; }