diff options
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r-- | drivers/scsi/sg.c | 657 |
1 files changed, 373 insertions, 284 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 53268aaba559..01cf88888797 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 <rgooch@atnf.csiro.au> 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). @@ -51,6 +49,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #include <linux/delay.h> #include <linux/blktrace_api.h> #include <linux/mutex.h> +#include <linux/atomic.h> #include <linux/ratelimit.h> #include "scsi.h" @@ -64,7 +63,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include <linux/proc_fs.h> -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 @@ static void sg_proc_cleanup(void); #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 @@ 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 +149,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 */ @@ -161,7 +163,7 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ 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_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 */ + 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<n>] */ @@ -197,24 +200,28 @@ static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, 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_printk(prefix, (sdp)->device, "[%s] " fmt, \ + (sdp)->disk->disk_name, ##a) + static int sg_allow_access(struct file *filp, unsigned char *cmd) { struct sg_fd *sfp = filp->private_data; @@ -225,38 +232,43 @@ 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) +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 @@ 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)); + 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 <linux/fs.h> */ /* Prevent the device driver from vanishing while we sleep */ @@ -287,6 +299,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,65 +309,65 @@ 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; - } - 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 @@ sg_release(struct inode *inode, struct file *filp) 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 @@ 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)); + 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 @@ 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 +452,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; } @@ -566,13 +590,13 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 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 ((!(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))) @@ -592,18 +616,13 @@ 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")); + 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 { @@ -611,7 +630,7 @@ 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( + 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; @@ -675,7 +694,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, 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; @@ -686,7 +705,8 @@ 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")); + 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; @@ -743,7 +763,7 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking) { - int k, data_dir; + int k, data_dir, at_head; Sg_device *sdp = sfp->parentdp; sg_io_hdr_t *hp = &srp->header; @@ -755,16 +775,18 @@ 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)); + 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) blk_end_request_all(srp->rq, -EIO); sg_finish_rem_req(srp); @@ -787,11 +809,16 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, 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; } @@ -806,6 +833,15 @@ static int srp_done(Sg_fd *sfp, Sg_request *srp) 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) { @@ -820,13 +856,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)); + 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; @@ -837,8 +873,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) { @@ -873,11 +909,11 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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); + sg_remove_scat(sfp, &sfp->reserve); 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 +926,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); @@ -945,17 +981,17 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 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); + sg_remove_scat(sfp, &sfp->reserve); sg_build_reserve(sfp, val); } 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); @@ -1032,11 +1068,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 +1105,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,11 +1127,11 @@ 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: - 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, @@ -1165,15 +1201,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)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_poll: res=0x%x\n", (int) res)); return res; } @@ -1185,8 +1221,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)); + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, + "sg_fasync: mode=%d\n", mode)); return fasync_helper(fd, filp, mode, &sfp->async_qp); } @@ -1205,8 +1241,9 @@ 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)); + 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++) { @@ -1241,8 +1278,9 @@ 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)); + 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; @@ -1264,7 +1302,8 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) 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; @@ -1277,7 +1316,8 @@ static void 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). */ -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; @@ -1295,15 +1335,16 @@ 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))) + 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) ? @@ -1319,7 +1360,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(__func__, sense, SCSI_SENSE_BUFFERSIZE); /* Following if statement is a patch supplied by Eric Youngdale */ @@ -1378,7 +1419,8 @@ static struct class *sg_sysfs_class; 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; @@ -1388,7 +1430,8 @@ 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"); + sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device " + "failure\n", __func__); return ERR_PTR(-ENOMEM); } @@ -1403,20 +1446,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); + 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); @@ -1434,7 +1482,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 +1493,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 +1501,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 +1509,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 +1527,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: 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); @@ -1513,7 +1559,8 @@ out: 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; @@ -1528,40 +1575,45 @@ static void sg_device_destroy(struct kref *kref) 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); @@ -1631,7 +1683,8 @@ exit_sg(void) 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; @@ -1645,15 +1698,28 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) 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) + if (!rq) { + kfree(long_cmdp); return -ENOMEM; + } 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; @@ -1726,25 +1792,30 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) 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)); + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, + "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); + 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); @@ -1778,8 +1849,9 @@ 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)); + 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); @@ -1822,14 +1894,16 @@ retry: } } - 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 */ @@ -1846,17 +1920,19 @@ out: } 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); } @@ -1872,8 +1948,9 @@ 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)); + 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; @@ -1903,14 +1980,15 @@ 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)); + 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)); } @@ -1923,7 +2001,8 @@ 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)); + 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); @@ -1941,7 +2020,8 @@ 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")); + SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, + "sg_link_reserve: BAD size\n")); } static void @@ -1949,8 +2029,9 @@ 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)); + 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; @@ -2055,7 +2136,7 @@ sg_remove_request(Sg_fd * sfp, Sg_request * srp) } static Sg_fd * -sg_add_sfp(Sg_device * sdp, int dev) +sg_add_sfp(Sg_device * sdp) { Sg_fd *sfp; unsigned long iflags; @@ -2063,7 +2144,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); @@ -2077,25 +2158,33 @@ 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); + 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; @@ -2105,34 +2194,32 @@ 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", + 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); @@ -2183,7 +2270,8 @@ static Sg_device *sg_lookup_dev(int dev) 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; @@ -2192,8 +2280,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); @@ -2204,11 +2292,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; @@ -2425,8 +2508,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; } @@ -2482,16 +2564,19 @@ 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)) - 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; } @@ -2510,11 +2595,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, "<no active device>\n"); + seq_puts(s, "<no active device>\n"); read_unlock_irqrestore(&sg_index_lock, iflags); return 0; } @@ -2559,12 +2645,12 @@ 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 ? - ((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) @@ -2580,7 +2666,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); } } @@ -2596,31 +2682,34 @@ 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:%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; } |