All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chris Wright <chrisw@sous-sol.org>
To: linux-kernel@vger.kernel.org, stable@kernel.org
Cc: Justin Forbes <jmforbes@linuxtx.org>,
	Zwane Mwaikambo <zwane@arm.linux.org.uk>,
	"Theodore Ts'o" <tytso@mit.edu>,
	Randy Dunlap <rdunlap@xenotime.net>,
	Dave Jones <davej@redhat.com>,
	Chuck Wolber <chuckw@quantumlinux.com>,
	Chris Wedgwood <reviews@ml.cw.f00f.org>,
	Michael Krufky <mkrufky@linuxtv.org>,
	Chuck Ebbert <cebbert@redhat.com>,
	Domenico Andreoli <cavokz@gmail.com>, Willy Tarreau <w@1wt.eu>,
	Rodrigo Rubira Branco <rbranco@la.checkpoint.com>,
	Jake Edge <jake@lwn.net>, Eugene Teo <eteo@redhat.com>,
	torvalds@linux-foundation.org, akpm@linux-foundation.org,
	alan@lxorguk.ukuu.org.uk, Tony Battersby <tonyb@cybernetics.com>,
	Douglas Gilbert <dgilbert@interlog.com>,
	James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: [patch 073/100] SCSI: sg: fix races during device removal
Date: Thu, 23 Apr 2009 00:21:33 -0700	[thread overview]
Message-ID: <20090423072845.227081925@sous-sol.org> (raw)
In-Reply-To: 20090423072020.428683652@sous-sol.org

[-- Attachment #1: scsi-sg-fix-races-during-device-removal.patch --]
[-- Type: text/plain, Size: 22407 bytes --]

-stable review patch.  If anyone has any objections, please let us know.
---------------------

From: Tony Battersby <tonyb@cybernetics.com>

upstream commit: c6517b7942fad663cc1cf3235cbe4207cf769332

sg has the following problems related to device removal:

* opening a sg fd races with removing a device
* closing a sg fd races with removing a device
* /proc/scsi/sg/* access races with removing a device
* command completion races with removing a device
* command completion races with closing a sg fd
* can rmmod sg with active commands

These problems can cause kernel oopses, memory-use-after-free, or
double-free errors.  This patch fixes these problems by using krefs
to manage the lifetime of sg_device and sg_fd.

Each command submitted to the midlevel holds a reference to sg_fd
until the completion callback.  This ensures that sg_fd doesn't go
away if the fd is closed with commands still outstanding.

sg_fd gets the reference of sg_device (with scsi_device) and also
makes sure that the sg module doesn't go away.

/proc/scsi/sg/* functions don't play nicely with krefs because they
give information about sg_fds which have been closed but not yet
freed due to still having outstanding commands and sg_devices which
have been removed but not yet freed due to still being referenced
by one or more sg_fds.  To deal with this safely without removing
functionality, /proc functions now access sg_device and sg_fd while
holding a lock instead of using kref_get()/kref_put().

Signed-off-by: Tony Battersby <tonyb@cybernetics.com>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
[chrisw: big for -stable, helps fix real bug, and made it through rc2 upstream]
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
---
 drivers/scsi/sg.c |  418 +++++++++++++++++++++++++-----------------------------
 1 file changed, 201 insertions(+), 217 deletions(-)

--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -101,6 +101,7 @@ static int scatter_elem_sz_prev = SG_SCA
 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
 
 static int sg_add(struct device *, struct class_interface *);
+static void sg_device_destroy(struct kref *kref);
 static void sg_remove(struct device *, struct class_interface *);
 
 static DEFINE_IDR(sg_index_idr);
@@ -158,6 +159,8 @@ typedef struct sg_fd {		/* holds the sta
 	char next_cmd_len;	/* 0 -> automatic (def), >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;
+	struct execute_work ew;
 } Sg_fd;
 
 typedef struct sg_device { /* holds the state of each scsi generic device */
@@ -171,6 +174,7 @@ typedef struct sg_device { /* holds the 
 	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>] */
+	struct kref d_ref;
 } Sg_device;
 
 static int sg_fasync(int fd, struct file *filp, int mode);
@@ -194,13 +198,14 @@ static void sg_build_reserve(Sg_fd * sfp
 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 int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
-static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
+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_lookup_dev(int dev);
 static Sg_device *sg_get_dev(int dev);
+static void sg_put_dev(Sg_device *sdp);
 #ifdef CONFIG_SCSI_PROC_FS
 static int sg_last_dev(void);
 #endif
@@ -237,22 +242,17 @@ sg_open(struct inode *inode, struct file
 	nonseekable_open(inode, filp);
 	SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
 	sdp = sg_get_dev(dev);
-	if ((!sdp) || (!sdp->device)) {
-		unlock_kernel();
-		return -ENXIO;
-	}
-	if (sdp->detached) {
-		unlock_kernel();
-		return -ENODEV;
+	if (IS_ERR(sdp)) {
+		retval = PTR_ERR(sdp);
+		sdp = NULL;
+		goto sg_put;
 	}
 
 	/* This driver's module count bumped by fops_get in <linux/fs.h> */
 	/* Prevent the device driver from vanishing while we sleep */
 	retval = scsi_device_get(sdp->device);
-	if (retval) {
-		unlock_kernel();
-		return retval;
-	}
+	if (retval)
+		goto sg_put;
 
 	if (!((flags & O_NONBLOCK) ||
 	      scsi_block_when_processing_errors(sdp->device))) {
@@ -303,16 +303,20 @@ sg_open(struct inode *inode, struct file
 	if ((sfp = sg_add_sfp(sdp, dev)))
 		filp->private_data = sfp;
 	else {
-		if (flags & O_EXCL)
+		if (flags & O_EXCL) {
 			sdp->exclude = 0;	/* undo if error */
+			wake_up_interruptible(&sdp->o_excl_wait);
+		}
 		retval = -ENOMEM;
 		goto error_out;
 	}
-	unlock_kernel();
-	return 0;
-
-      error_out:
-	scsi_device_put(sdp->device);
+	retval = 0;
+error_out:
+	if (retval)
+		scsi_device_put(sdp->device);
+sg_put:
+	if (sdp)
+		sg_put_dev(sdp);
 	unlock_kernel();
 	return retval;
 }
@@ -327,13 +331,13 @@ sg_release(struct inode *inode, struct f
 	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));
-	if (0 == sg_remove_sfp(sdp, sfp)) {	/* Returns 1 when sdp gone */
-		if (!sdp->detached) {
-			scsi_device_put(sdp->device);
-		}
-		sdp->exclude = 0;
-		wake_up_interruptible(&sdp->o_excl_wait);
-	}
+
+	sfp->closed = 1;
+
+	sdp->exclude = 0;
+	wake_up_interruptible(&sdp->o_excl_wait);
+
+	kref_put(&sfp->f_ref, sg_remove_sfp);
 	return 0;
 }
 
@@ -755,6 +759,7 @@ sg_common_write(Sg_fd * sfp, Sg_request 
 	hp->duration = jiffies_to_msecs(jiffies);
 
 	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);
 	return 0;
@@ -1247,24 +1252,23 @@ sg_mmap(struct file *filp, struct vm_are
 static void sg_rq_end_io(struct request *rq, int uptodate)
 {
 	struct sg_request *srp = rq->end_io_data;
-	Sg_device *sdp = NULL;
+	Sg_device *sdp;
 	Sg_fd *sfp;
 	unsigned long iflags;
 	unsigned int ms;
 	char *sense;
-	int result, resid;
+	int result, resid, done = 1;
 
-	if (NULL == srp) {
-		printk(KERN_ERR "sg_cmd_done: NULL request\n");
+	if (WARN_ON(srp->done != 0))
 		return;
-	}
+
 	sfp = srp->parentfp;
-	if (sfp)
-		sdp = sfp->parentdp;
-	if ((NULL == sdp) || sdp->detached) {
-		printk(KERN_INFO "sg_cmd_done: device detached\n");
+	if (WARN_ON(sfp == NULL))
 		return;
-	}
+
+	sdp = sfp->parentdp;
+	if (unlikely(sdp->detached))
+		printk(KERN_INFO "sg_rq_end_io: device detached\n");
 
 	sense = rq->sense;
 	result = rq->errors;
@@ -1303,33 +1307,26 @@ static void sg_rq_end_io(struct request 
 	}
 	/* Rely on write phase to clean out srp status values, so no "else" */
 
-	if (sfp->closed) {	/* whoops this fd already released, cleanup */
-		SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, freeing ...\n"));
-		sg_finish_rem_req(srp);
-		srp = NULL;
-		if (NULL == sfp->headrp) {
-			SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, final cleanup\n"));
-			if (0 == sg_remove_sfp(sdp, sfp)) {	/* device still present */
-				scsi_device_put(sdp->device);
-			}
-			sfp = NULL;
-		}
-	} else if (srp && srp->orphan) {
+	write_lock_irqsave(&sfp->rq_list_lock, iflags);
+	if (unlikely(srp->orphan)) {
 		if (sfp->keep_orphan)
 			srp->sg_io_owned = 0;
-		else {
-			sg_finish_rem_req(srp);
-			srp = NULL;
-		}
+		else
+			done = 0;
 	}
-	if (sfp && srp) {
-		/* Now wake up any sg_read() that is waiting for this packet. */
-		kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
-		write_lock_irqsave(&sfp->rq_list_lock, iflags);
-		srp->done = 1;
+	srp->done = done;
+	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+
+	if (likely(done)) {
+		/* Now wake up any sg_read() that is waiting for this
+		 * packet.
+		 */
 		wake_up_interruptible(&sfp->read_wait);
-		write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-	}
+		kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
+	} else
+		sg_finish_rem_req(srp); /* call with srp->done == 0 */
+
+	kref_put(&sfp->f_ref, sg_remove_sfp);
 }
 
 static struct file_operations sg_fops = {
@@ -1364,17 +1361,18 @@ static Sg_device *sg_alloc(struct gendis
 		printk(KERN_WARNING "kmalloc Sg_device failure\n");
 		return ERR_PTR(-ENOMEM);
 	}
-	error = -ENOMEM;
+
 	if (!idr_pre_get(&sg_index_idr, GFP_KERNEL)) {
 		printk(KERN_WARNING "idr expansion Sg_device failure\n");
+		error = -ENOMEM;
 		goto out;
 	}
 
 	write_lock_irqsave(&sg_index_lock, iflags);
-	error = idr_get_new(&sg_index_idr, sdp, &k);
-	write_unlock_irqrestore(&sg_index_lock, iflags);
 
+	error = idr_get_new(&sg_index_idr, sdp, &k);
 	if (error) {
+		write_unlock_irqrestore(&sg_index_lock, iflags);
 		printk(KERN_WARNING "idr allocation Sg_device failure: %d\n",
 		       error);
 		goto out;
@@ -1391,6 +1389,9 @@ static Sg_device *sg_alloc(struct gendis
 	init_waitqueue_head(&sdp->o_excl_wait);
 	sdp->sg_tablesize = min(q->max_hw_segments, q->max_phys_segments);
 	sdp->index = k;
+	kref_init(&sdp->d_ref);
+
+	write_unlock_irqrestore(&sg_index_lock, iflags);
 
 	error = 0;
  out:
@@ -1401,6 +1402,8 @@ static Sg_device *sg_alloc(struct gendis
 	return sdp;
 
  overflow:
+	idr_remove(&sg_index_idr, k);
+	write_unlock_irqrestore(&sg_index_lock, iflags);
 	sdev_printk(KERN_WARNING, scsidp,
 		    "Unable to attach sg device type=%d, minor "
 		    "number exceeds %d\n", scsidp->type, SG_MAX_DEVS - 1);
@@ -1488,49 +1491,46 @@ out:
 	return error;
 }
 
-static void
-sg_remove(struct device *cl_dev, struct class_interface *cl_intf)
+static void sg_device_destroy(struct kref *kref)
+{
+	struct sg_device *sdp = container_of(kref, struct sg_device, d_ref);
+	unsigned long flags;
+
+	/* CAUTION!  Note that the device can still be found via idr_find()
+	 * even though the refcount is 0.  Therefore, do idr_remove() BEFORE
+	 * any other cleanup.
+	 */
+
+	write_lock_irqsave(&sg_index_lock, flags);
+	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));
+
+	put_disk(sdp->disk);
+	kfree(sdp);
+}
+
+static void sg_remove(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;
-	Sg_fd *tsfp;
-	Sg_request *srp;
-	Sg_request *tsrp;
-	int delay;
 
-	if (!sdp)
+	if (!sdp || sdp->detached)
 		return;
 
-	delay = 0;
+	SCSI_LOG_TIMEOUT(3, printk("sg_remove: %s\n", sdp->disk->disk_name));
+
+	/* Need a write lock to set sdp->detached. */
 	write_lock_irqsave(&sg_index_lock, iflags);
-	if (sdp->headfp) {
-		sdp->detached = 1;
-		for (sfp = sdp->headfp; sfp; sfp = tsfp) {
-			tsfp = sfp->nextfp;
-			for (srp = sfp->headrp; srp; srp = tsrp) {
-				tsrp = srp->nextrp;
-				if (sfp->closed || (0 == sg_srp_done(srp, sfp)))
-					sg_finish_rem_req(srp);
-			}
-			if (sfp->closed) {
-				scsi_device_put(sdp->device);
-				__sg_remove_sfp(sdp, sfp);
-			} else {
-				delay = 1;
-				wake_up_interruptible(&sfp->read_wait);
-				kill_fasync(&sfp->async_qp, SIGPOLL,
-					    POLL_HUP);
-			}
-		}
-		SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d, dirty\n", sdp->index));
-		if (NULL == sdp->headfp) {
-			idr_remove(&sg_index_idr, sdp->index);
-		}
-	} else {	/* nothing active, simple case */
-		SCSI_LOG_TIMEOUT(3, printk("sg_remove: dev=%d\n", sdp->index));
-		idr_remove(&sg_index_idr, sdp->index);
+	sdp->detached = 1;
+	for (sfp = sdp->headfp; sfp; sfp = sfp->nextfp) {
+		wake_up_interruptible(&sfp->read_wait);
+		kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
 	}
 	write_unlock_irqrestore(&sg_index_lock, iflags);
 
@@ -1538,13 +1538,8 @@ sg_remove(struct device *cl_dev, struct 
 	device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index));
 	cdev_del(sdp->cdev);
 	sdp->cdev = NULL;
-	put_disk(sdp->disk);
-	sdp->disk = NULL;
-	if (NULL == sdp->headfp)
-		kfree(sdp);
 
-	if (delay)
-		msleep(10);	/* dirty detach so delay device destruction */
+	sg_put_dev(sdp);
 }
 
 module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR);
@@ -1961,22 +1956,6 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id)
 	return resp;
 }
 
-#ifdef CONFIG_SCSI_PROC_FS
-static Sg_request *
-sg_get_nth_request(Sg_fd * sfp, int nth)
-{
-	Sg_request *resp;
-	unsigned long iflags;
-	int k;
-
-	read_lock_irqsave(&sfp->rq_list_lock, iflags);
-	for (k = 0, resp = sfp->headrp; resp && (k < nth);
-	     ++k, resp = resp->nextrp) ;
-	read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-	return resp;
-}
-#endif
-
 /* always adds to end of list */
 static Sg_request *
 sg_add_request(Sg_fd * sfp)
@@ -2052,22 +2031,6 @@ sg_remove_request(Sg_fd * sfp, Sg_reques
 	return res;
 }
 
-#ifdef CONFIG_SCSI_PROC_FS
-static Sg_fd *
-sg_get_nth_sfp(Sg_device * sdp, int nth)
-{
-	Sg_fd *resp;
-	unsigned long iflags;
-	int k;
-
-	read_lock_irqsave(&sg_index_lock, iflags);
-	for (k = 0, resp = sdp->headfp; resp && (k < nth);
-	     ++k, resp = resp->nextfp) ;
-	read_unlock_irqrestore(&sg_index_lock, iflags);
-	return resp;
-}
-#endif
-
 static Sg_fd *
 sg_add_sfp(Sg_device * sdp, int dev)
 {
@@ -2082,6 +2045,7 @@ sg_add_sfp(Sg_device * sdp, int dev)
 	init_waitqueue_head(&sfp->read_wait);
 	rwlock_init(&sfp->rq_list_lock);
 
+	kref_init(&sfp->f_ref);
 	sfp->timeout = SG_DEFAULT_TIMEOUT;
 	sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
 	sfp->force_packid = SG_DEF_FORCE_PACK_ID;
@@ -2109,15 +2073,54 @@ sg_add_sfp(Sg_device * sdp, int dev)
 	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));
+
+	kref_get(&sdp->d_ref);
+	__module_get(THIS_MODULE);
 	return sfp;
 }
 
-static void
-__sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
+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;
+
+	/* Cleanup any responses which were never read(). */
+	while (sfp->headrp)
+		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_remove_scat(&sfp->reserve);
+	}
+
+	SCSI_LOG_TIMEOUT(6,
+		printk("sg_remove_sfp: %s, sfp=0x%p\n",
+			sdp->disk->disk_name,
+			sfp));
+	kfree(sfp);
+
+	scsi_device_put(sdp->device);
+	sg_put_dev(sdp);
+	module_put(THIS_MODULE);
+}
+
+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;
 	Sg_fd *fp;
 	Sg_fd *prev_fp;
+	unsigned long iflags;
+
+	/* CAUTION!  Note that sfp can still be found by walking sdp->headfp
+	 * even though the refcount is now 0.  Therefore, unlink sfp from
+	 * sdp->headfp BEFORE doing any other cleanup.
+	 */
 
+	write_lock_irqsave(&sg_index_lock, iflags);
 	prev_fp = sdp->headfp;
 	if (sfp == prev_fp)
 		sdp->headfp = prev_fp->nextfp;
@@ -2130,54 +2133,10 @@ __sg_remove_sfp(Sg_device * sdp, Sg_fd *
 			prev_fp = fp;
 		}
 	}
-	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_remove_scat(&sfp->reserve);
-	}
-	sfp->parentdp = NULL;
-	SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp:    sfp=0x%p\n", sfp));
-	kfree(sfp);
-}
-
-/* Returns 0 in normal case, 1 when detached and sdp object removed */
-static int
-sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
-{
-	Sg_request *srp;
-	Sg_request *tsrp;
-	int dirty = 0;
-	int res = 0;
-
-	for (srp = sfp->headrp; srp; srp = tsrp) {
-		tsrp = srp->nextrp;
-		if (sg_srp_done(srp, sfp))
-			sg_finish_rem_req(srp);
-		else
-			++dirty;
-	}
-	if (0 == dirty) {
-		unsigned long iflags;
+	write_unlock_irqrestore(&sg_index_lock, iflags);
+	wake_up_interruptible(&sdp->o_excl_wait);
 
-		write_lock_irqsave(&sg_index_lock, iflags);
-		__sg_remove_sfp(sdp, sfp);
-		if (sdp->detached && (NULL == sdp->headfp)) {
-			idr_remove(&sg_index_idr, sdp->index);
-			kfree(sdp);
-			res = 1;
-		}
-		write_unlock_irqrestore(&sg_index_lock, iflags);
-	} else {
-		/* MOD_INC's to inhibit unloading sg and associated adapter driver */
-		/* only bump the access_count if we actually succeeded in
-		 * throwing another counter on the host module */
-		scsi_device_get(sdp->device);	/* XXX: retval ignored? */	
-		sfp->closed = 1;	/* flag dirty state on this fd */
-		SCSI_LOG_TIMEOUT(1, printk("sg_remove_sfp: worrisome, %d writes pending\n",
-				  dirty));
-	}
-	return res;
+	execute_in_process_context(sg_remove_sfp_usercontext, &sfp->ew);
 }
 
 static int
@@ -2219,19 +2178,38 @@ sg_last_dev(void)
 }
 #endif
 
-static Sg_device *
-sg_get_dev(int dev)
+/* must be called with sg_index_lock held */
+static Sg_device *sg_lookup_dev(int dev)
 {
-	Sg_device *sdp;
-	unsigned long iflags;
+	return idr_find(&sg_index_idr, dev);
+}
 
-	read_lock_irqsave(&sg_index_lock, iflags);
-	sdp = idr_find(&sg_index_idr, dev);
-	read_unlock_irqrestore(&sg_index_lock, iflags);
+static Sg_device *sg_get_dev(int dev)
+{
+	struct sg_device *sdp;
+	unsigned long flags;
+
+	read_lock_irqsave(&sg_index_lock, flags);
+	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
+		 * which case it would be a bug to do kref_get().
+		 */
+		sdp = ERR_PTR(-ENODEV);
+	} else
+		kref_get(&sdp->d_ref);
+	read_unlock_irqrestore(&sg_index_lock, flags);
 
 	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;
@@ -2488,8 +2466,10 @@ static int sg_proc_seq_show_dev(struct s
 	struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
 	Sg_device *sdp;
 	struct scsi_device *scsidp;
+	unsigned long iflags;
 
-	sdp = it ? sg_get_dev(it->index) : NULL;
+	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",
 			      scsidp->host->host_no, scsidp->channel,
@@ -2500,6 +2480,7 @@ static int sg_proc_seq_show_dev(struct s
 			      (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;
 }
 
@@ -2513,16 +2494,20 @@ static int sg_proc_seq_show_devstrs(stru
 	struct sg_proc_deviter * it = (struct sg_proc_deviter *) v;
 	Sg_device *sdp;
 	struct scsi_device *scsidp;
+	unsigned long iflags;
 
-	sdp = it ? sg_get_dev(it->index) : NULL;
+	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, "%8.8s\t%16.16s\t%4.4s\n",
 			   scsidp->vendor, scsidp->model, scsidp->rev);
 	else
 		seq_printf(s, "<no active device>\n");
+	read_unlock_irqrestore(&sg_index_lock, iflags);
 	return 0;
 }
 
+/* must be called while holding sg_index_lock */
 static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
 {
 	int k, m, new_interface, blen, usg;
@@ -2532,7 +2517,8 @@ static void sg_proc_debug_helper(struct 
 	const char * cp;
 	unsigned int ms;
 
-	for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) {
+	for (k = 0, fp = sdp->headfp; fp != NULL; ++k, fp = fp->nextfp) {
+		read_lock(&fp->rq_list_lock); /* irqs already disabled */
 		seq_printf(s, "   FD(%d): timeout=%dms bufflen=%d "
 			   "(res)sgat=%d low_dma=%d\n", k + 1,
 			   jiffies_to_msecs(fp->timeout),
@@ -2542,7 +2528,9 @@ static void sg_proc_debug_helper(struct 
 		seq_printf(s, "   cmd_q=%d f_packid=%d k_orphan=%d closed=%d\n",
 			   (int) fp->cmd_q, (int) fp->force_packid,
 			   (int) fp->keep_orphan, (int) fp->closed);
-		for (m = 0; (srp = sg_get_nth_request(fp, m)); ++m) {
+		for (m = 0, srp = fp->headrp;
+				srp != NULL;
+				++m, srp = srp->nextrp) {
 			hp = &srp->header;
 			new_interface = (hp->interface_id == '\0') ? 0 : 1;
 			if (srp->res_used) {
@@ -2579,6 +2567,7 @@ static void sg_proc_debug_helper(struct 
 		}
 		if (0 == m)
 			seq_printf(s, "     No requests active\n");
+		read_unlock(&fp->rq_list_lock);
 	}
 }
 
@@ -2591,39 +2580,34 @@ static int sg_proc_seq_show_debug(struct
 {
 	struct sg_proc_deviter * it = (struct sg_proc_deviter *) 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);
 	}
-	sdp = it ? sg_get_dev(it->index) : NULL;
-	if (sdp) {
-		struct scsi_device *scsidp = sdp->device;
 
-		if (NULL == scsidp) {
-			seq_printf(s, "device %d detached ??\n", 
-				   (int)it->index);
-			return 0;
-		}
+	read_lock_irqsave(&sg_index_lock, iflags);
+	sdp = it ? sg_lookup_dev(it->index) : NULL;
+	if (sdp && sdp->headfp) {
+		struct scsi_device *scsidp = sdp->device;
 
-		if (sg_get_nth_sfp(sdp, 0)) {
-			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, sdp->exclude);
-		}
+		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, sdp->exclude);
 		sg_proc_debug_helper(s, sdp);
 	}
+	read_unlock_irqrestore(&sg_index_lock, iflags);
 	return 0;
 }
 


  parent reply	other threads:[~2009-04-23  7:54 UTC|newest]

Thread overview: 120+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-04-23  7:20 [patch 000/100] 2.6.29.2 -stable review Chris Wright
2009-04-23  7:20 ` [patch 001/100] security/smack: fix oops when setting a size 0 SMACK64 xattr Chris Wright
2009-04-23  7:20 ` [patch 002/100] fbmem: fix fb_info->lock and mm->mmap_sem circular locking dependency Chris Wright
2009-04-23  7:20 ` [patch 003/100] fbdev: fix info->lock deadlock in fbcon_event_notify() Chris Wright
2009-04-23  7:20 ` [patch 004/100] ide: Fix code dealing with sleeping devices in do_ide_request() Chris Wright
2009-04-23  7:20 ` [patch 005/100] PCI/x86: detect host bridge config space size w/o using quirks Chris Wright
2009-04-23  7:20 ` [patch 006/100] MIPS: Compat: Zero upper 32-bit of offset_high and offset_low Chris Wright
2009-04-23  7:20 ` [patch 007/100] ext4: fix typo which causes a memory leak on error path Chris Wright
2009-04-23  7:20 ` [patch 008/100] ext4: fix locking typo in mballoc which could cause soft lockup hangs Chris Wright
2009-04-23  7:20 ` [patch 009/100] rt2x00: Fix SLAB corruption during rmmod Chris Wright
2009-04-23  7:20   ` Chris Wright
2009-04-23  7:20 ` [patch 010/100] tracing/core: fix early free of cpumasks Chris Wright
2009-04-23  7:20 ` [patch 011/100] x86, setup: mark %esi as clobbered in E820 BIOS call Chris Wright
2009-04-23  7:20 ` [patch 012/100] acpi: fix of pmtimer overflow that make Cx states time incorrect Chris Wright
2009-04-23  9:24   ` Shi, Alex
2009-04-23  9:24     ` Shi, Alex
2009-04-23  9:40   ` Shi, Alex
2009-04-23  9:40     ` Shi, Alex
2009-04-23  7:20 ` [patch 013/100] ACPI: cap off P-state transition latency from buggy BIOSes Chris Wright
2009-04-25 15:59   ` Martin Steigerwald
2009-04-23  7:20 ` [patch 014/100] dock: fix dereference after kfree() Chris Wright
2009-04-23  7:20 ` [patch 015/100] drm/i915: Change DCC tiling detection case to cover only mobile parts Chris Wright
2009-04-23  7:20 ` [patch 016/100] drm/i915: Read the right SDVO register when detecting SVDO/HDMI Chris Wright
2009-04-23  7:20 ` [patch 017/100] drm/i915: Sync crt hotplug detection with intel video driver Chris Wright
2009-04-23  7:20 ` [patch 018/100] drm/i915: Check for dev->primary->master before dereference Chris Wright
2009-04-23  7:20 ` [patch 019/100] drm/i915: check for -EINVAL from vm_insert_pfn Chris Wright
2009-04-23  7:20 ` [patch 020/100] drm: Use pgprot_writecombine in GEM GTT mapping to get the right bits for !PAT Chris Wright
2009-04-23  7:20 ` [patch 021/100] drm/i915: only set TV mode when any property changed Chris Wright
2009-04-23  7:20 ` [patch 022/100] drm/i915: fix TV mode setting in property change Chris Wright
2009-04-23  7:20 ` [patch 023/100] SCSI: sg: fix iovec bugs introduced by the block layer conversion Chris Wright
2009-04-23  7:20 ` [patch 024/100] md/raid1 - dont assume newly allocated bvecs are initialised Chris Wright
2009-04-23  7:20 ` [patch 025/100] r8169: Reset IntrStatus after chip reset Chris Wright
2009-04-23  7:20 ` [patch 026/100] V4L/DVB (10943): cx88: Prevent general protection fault on rmmod Chris Wright
2009-04-23  7:20 ` [patch 027/100] ide: drivers/ide/ide-atapi.c needs <linux/scatterlist.h> Chris Wright
2009-04-23  7:20 ` [patch 028/100] ide-atapi: start DMA after issuing a packet command Chris Wright
2009-04-23  7:20 ` [patch 029/100] cpumask: fix slab corruption caused by alloc_cpumask_var_node() Chris Wright
2009-04-23  7:20 ` [patch 030/100] sysctl: fix suid_dumpable and lease-break-time sysctls Chris Wright
2009-04-23  7:20 ` [patch 031/100] mm: define a UNIQUE value for AS_UNEVICTABLE flag Chris Wright
2009-04-23  7:20 ` [patch 032/100] mm: do_xip_mapping_read: fix length calculation Chris Wright
2009-04-23  7:20 ` [patch 033/100] ixgbe: Fix potential memory leak/driver panic issue while setting up Tx & Rx ring parameters Chris Wright
2009-04-23  7:20 ` [patch 034/100] dm: preserve bi_io_vec when resubmitting bios Chris Wright
2009-04-23  7:20 ` [patch 035/100] vfs: skip I_CLEAR state inodes Chris Wright
2009-04-23  7:20 ` [patch 036/100] dm raid1: switch read_record from kmalloc to slab to save memory Chris Wright
2009-04-23  7:20 ` [patch 037/100] dm io: make sync_io uninterruptible Chris Wright
2009-04-23  7:20 ` [patch 038/100] dm snapshot: refactor __find_pending_exception Chris Wright
2009-04-23  7:20 ` [patch 039/100] dm snapshot: avoid dropping lock in __find_pending_exception Chris Wright
2009-04-23  7:21 ` [patch 040/100] dm snapshot: avoid having two exceptions for the same chunk Chris Wright
2009-04-23  7:21 ` [patch 041/100] dm target: use module refcount directly Chris Wright
2009-04-23  7:21 ` [patch 042/100] dm: path selector " Chris Wright
2009-04-23  7:21 ` [patch 043/100] dm table: fix upgrade mode race Chris Wright
2009-04-23  7:21 ` [patch 044/100] af_rose/x25: Sanity check the maximum user frame size Chris Wright
2009-04-23  7:21 ` [patch 045/100] net/netrom: Fix socket locking Chris Wright
2009-04-23  7:21 ` [patch 046/100] crypto: shash - Fix unaligned calculation with short length Chris Wright
2009-04-23  7:21 ` [patch 047/100] acer-wmi: Blacklist Acer Aspire One Chris Wright
2009-04-23  7:21 ` [patch 048/100] kprobes: Fix locking imbalance in kretprobes Chris Wright
2009-04-23  7:21 ` [patch 049/100] netfilter: {ip, ip6, arp}_tables: fix incorrect loop detection Chris Wright
2009-04-23  7:21 ` [patch 050/100] splice: fix deadlock in splicing to file Chris Wright
2009-04-23  7:21 ` [patch 051/100] ALSA: hda - add missing comma in ad1884_slave_vols Chris Wright
2009-04-23  7:21 ` [patch 052/100] sparc64: Fix bug in ("sparc64: Flush TLB before releasing pages.") Chris Wright
2009-04-23  7:21 ` [patch 053/100] SCSI: libiscsi: fix iscsi pool error path Chris Wright
2009-04-23  7:21 ` [patch 054/100] SCSI: libiscsi: fix iscsi pool error path (fixlet) Chris Wright
2009-04-23  7:21 ` [patch 055/100] cap_prctl: dont set error to 0 at no_change Chris Wright
2009-04-23  7:21 ` [patch 056/100] posixtimers, sched: Fix posix clock monotonicity Chris Wright
2009-04-23  7:21 ` [patch 057/100] posix-timers: fix RLIMIT_CPU && fork() Chris Wright
2009-04-23 20:59   ` Chuck Ebbert
2009-04-23 21:02     ` Oleg Nesterov
2009-04-23 21:11     ` Chris Wright
2009-04-23  7:21 ` [patch 058/100] posix-timers: fix RLIMIT_CPU && setitimer(CPUCLOCK_PROF) Chris Wright
2009-04-23  7:21 ` [patch 059/100] dm kcopyd: prepare for callback race fix Chris Wright
2009-04-23  7:21 ` [patch 060/100] dm kcopyd: fix callback race Chris Wright
2009-04-23  7:21 ` [patch 061/100] sched: do not count frozen tasks toward load Chris Wright
2009-04-23  7:21 ` Chris Wright
2009-04-23  7:21 ` Chris Wright
2009-04-23  7:21 ` [patch 062/100] x86: fix broken irq migration logic while cleaning up multiple vectors Chris Wright
2009-04-23  7:21 ` [patch 063/100] hrtimer: fix rq->lock inversion (again) Chris Wright
2009-04-23  7:21 ` [patch 064/100] add some long-missing capabilities to fs_mask Chris Wright
2009-04-23  7:21 ` [patch 065/100] spi: spi_write_then_read() bugfixes Chris Wright
2009-04-23  7:21 ` [patch 066/100] tty: Fix leak in ti-usb Chris Wright
2009-04-23  7:21 ` [patch 067/100] sfc: Match calls to netif_napi_add() and netif_napi_del() Chris Wright
2009-04-23  7:21 ` [patch 068/100] ALSA: hda - Fix the cmd cache keys for amp verbs Chris Wright
2009-04-23  7:21 ` [patch 069/100] powerpc: Fix data-corrupting bug in __futex_atomic_op Chris Wright
2009-04-23  7:21 ` [patch 070/100] hpt366: fix HPT370 DMA timeouts Chris Wright
2009-04-23  7:21 ` [patch 071/100] pata_hpt37x: " Chris Wright
2009-04-23  7:21 ` [patch 072/100] mm: pass correct mm when growing stack Chris Wright
2009-04-23  7:21 ` Chris Wright [this message]
2009-04-23  7:21 ` [patch 074/100] SCSI: sg: fix races with ioctl(SG_IO) Chris Wright
2009-04-23  7:21 ` [patch 075/100] SCSI: sg: avoid blk_put_request/blk_rq_unmap_user in interrupt Chris Wright
2009-04-23  7:21 ` [patch 076/100] SCSI: sg: fix q->queue_lock on scsi_error_handler path Chris Wright
2009-04-23  7:21 ` [patch 077/100] x86: disable X86_PTRACE_BTS for now Chris Wright
2009-04-23  7:21 ` [patch 078/100] usb gadget: fix ethernet link reports to ethtool Chris Wright
2009-04-23  7:21 ` [patch 079/100] USB: ftdi_sio: add vendor/project id for JETI specbos 1201 spectrometer Chris Wright
2009-04-23  7:21 ` [patch 080/100] USB: fix oops in cdc-wdm in case of malformed descriptors Chris Wright
2009-04-23  7:21 ` [patch 081/100] USB: usb-storage: augment unusual_devs entry for Simple Tech/Datafab Chris Wright
2009-04-23  7:21 ` [patch 082/100] KVM: Fix missing smp tlb flush in invlpg Chris Wright
2009-04-23  7:21 ` [patch 083/100] KVM: Add CONFIG_HAVE_KVM_IRQCHIP Chris Wright
2009-04-23  7:21 ` [patch 084/100] KVM: Interrupt mask notifiers for ioapic Chris Wright
2009-04-23  7:21 ` [patch 085/100] KVM: Reset PIT irq injection logic when the PIT IRQ is unmasked Chris Wright
2009-04-23  7:21 ` [patch 086/100] KVM: MMU: handle compound pages in kvm_is_mmio_pfn Chris Wright
2009-04-23  7:21 ` [patch 087/100] KVM: fix kvm_vm_ioctl_deassign_device Chris Wright
2009-04-23  7:21 ` [patch 088/100] KVM: VMX: Update necessary state when guest enters long mode Chris Wright
2009-04-23  7:21 ` [patch 089/100] KVM: is_long_mode() should check for EFER.LMA Chris Wright
2009-04-23  7:21 ` [patch 090/100] x86, PAT: Remove page granularity tracking for vm_insert_pfn maps Chris Wright
2009-04-23  7:21 ` [patch 091/100] Input: gameport - fix attach driver code Chris Wright
2009-04-23  7:21 ` [patch 092/100] Revert "console ASCII glyph 1:1 mapping" Chris Wright
2009-04-23  7:21 ` [patch 093/100] virtio: fix suspend when using virtio_balloon Chris Wright
2009-04-23  7:21 ` [patch 094/100] agp: zero pages before sending to userspace Chris Wright
2009-04-23  7:21 ` [patch 095/100] gso: Fix support for linear packets Chris Wright
2009-04-23  7:21 ` [patch 096/100] NFS: Fix the XDR iovec calculation in nfs3_xdr_setaclargs Chris Wright
2009-04-23  7:21 ` [patch 097/100] hugetlbfs: return negative error code for bad mount option Chris Wright
2009-04-23  7:21 ` [patch 098/100] scsi: mpt: suppress debugobjects warning Chris Wright
2009-04-23  7:21 ` [patch 099/100] skge: fix occasional BUG during MTU change Chris Wright
2009-04-23  7:22 ` [patch 100/100] Bonding: fix zero address hole bug in arp_ip_target list Chris Wright
2009-04-23 13:47 ` [patch 000/100] 2.6.29.2 -stable review Tvrtko Ursulin
2009-04-23 14:49   ` [stable] " Greg KH
2009-04-23 14:56     ` Tvrtko Ursulin
2009-04-23 15:02       ` Greg KH
2009-04-23 15:08         ` Tvrtko Ursulin
2009-04-23 15:45           ` Greg KH
2009-04-23 16:17             ` Tvrtko Ursulin
2009-04-23 16:25               ` Chris Wright

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090423072845.227081925@sous-sol.org \
    --to=chrisw@sous-sol.org \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=akpm@linux-foundation.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=cavokz@gmail.com \
    --cc=cebbert@redhat.com \
    --cc=chuckw@quantumlinux.com \
    --cc=davej@redhat.com \
    --cc=dgilbert@interlog.com \
    --cc=eteo@redhat.com \
    --cc=jake@lwn.net \
    --cc=jmforbes@linuxtx.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mkrufky@linuxtv.org \
    --cc=rbranco@la.checkpoint.com \
    --cc=rdunlap@xenotime.net \
    --cc=reviews@ml.cw.f00f.org \
    --cc=stable@kernel.org \
    --cc=tonyb@cybernetics.com \
    --cc=torvalds@linux-foundation.org \
    --cc=tytso@mit.edu \
    --cc=w@1wt.eu \
    --cc=zwane@arm.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.