public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: Erik Andersen <andersen@codepoet.org>
To: James Bottomley <James.Bottomley@steeleye.com>,
	linux-scsi@vger.kernel.org
Subject: Re: [PATCH] SCSI hotplug support
Date: Fri, 18 Oct 2002 05:28:14 -0600	[thread overview]
Message-ID: <20021018112814.GA32693@codepoet.org> (raw)
In-Reply-To: <20021016024016.GB4690@redhat.com>

On Tue Oct 15, 2002 at 10:40:16PM -0400, Doug Ledford wrote:
> middle of an interrupt or not).  In this case though, it's pretty easy to 
> tell it isn't interrupt context.  That's fine.  However, you still haven't 
> tested the scenario I mentioned at the start of this particular part of 
> the debate.  If you plug in one drive, then plug in another, does the 
> second one get added?  Possible way to test this without having separate 
> drives: load the sbp2 module *before* hotplugging your disks and see what 
> happens.

I have several firewire drives so testing this is not a
problem...  The answer is yes it is added.  With my latest patch
applied to the kernel, I had 4 drives plugged into my 1394 card
(my 1394 RAID box) when I loaded the sbp2 module.  All 4
coldplugged drives showed up in /proc/scsi/scsi.  I then plugged
in another drive, at which point all 5 drives showed up under
/proc/scsi/scsi as expected.

I made an update of my patch.  I have not attempted to convert
the 1394 hotplug driver to echo stuff into /proc/scsi/scsi.
However, this patch attempts to close the longstanding race
conditions inherent in add-single-device and remove-single-device
by using the BKL, and adds some better function docs.  I didn't
see any other locks in the scsi code that looked sufficient to
handle the races, so please correct me if there is a better
way...  This also fixes a stupid bug in the sbp2 part of my
previous patch that would cause coldplugged sbp2 devices to be
also hotplugged, causing them to be double registered with the
SCSI subsystem.

 -Erik

--
Erik B. Andersen             http://codepoet-consulting.com/
--This message was written using 73% post-consumer electrons--


--- linux-2.4.19/drivers/scsi/scsi_syms.c	2002-08-02 19:39:44.000000000 -0500
+++ linux-2.4.20-pre10-erik/drivers/scsi/scsi_syms.c	2002-10-09 16:41:01.000000000 -0500
@@ -103,3 +103,9 @@
 extern int scsi_delete_timer(Scsi_Cmnd *);
 EXPORT_SYMBOL(scsi_add_timer);
 EXPORT_SYMBOL(scsi_delete_timer);
+
+/* Support for hot plugging and unplugging devices -- safe for 
+ * ieee1394 or USB devices, but probably not for normal SCSI... */
+EXPORT_SYMBOL(scsi_add_single_device);
+EXPORT_SYMBOL(scsi_remove_single_device);
+
--- linux-2.4.19/drivers/scsi/hosts.h	2002-10-18 05:16:17.000000000 -0500
+++ linux-2.4.20-pre10-erik/drivers/scsi/hosts.h	2002-10-09 17:00:13.000000000 -0500
@@ -532,6 +532,13 @@
 int scsi_register_device(struct Scsi_Device_Template * sdpnt);
 void scsi_deregister_device(struct Scsi_Device_Template * tpnt);
 
+/* Support for hot plugging and unplugging devices -- safe for 
+ * ieee1394 or USB devices, but probably not for normal SCSI... */
+extern int scsi_add_single_device(struct Scsi_Host *shpnt, 
+	int channel, int id, int lun);
+extern int scsi_remove_single_device(struct Scsi_Host *shpnt, 
+	int channel, int id, int lun);
+
 /* These are used by loadable modules */
 extern int scsi_register_module(int, void *);
 extern int scsi_unregister_module(int, void *);
--- linux-2.4.19/drivers/scsi/scsi.c	2002-10-18 05:16:17.000000000 -0500
+++ linux-2.4.20-pre10-erik/drivers/scsi/scsi.c	2002-10-17 21:12:48.000000000 -0500
@@ -1553,6 +1553,165 @@
     }
 }
 
+
+/*
+ * Function:    scsi_add_single_device()
+ *
+ * Purpose:     Support for hotplugging SCSI devices.  This function
+ *              implements the actual functionality for
+ *                  echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
+ *
+ * Arguments:   shpnt   - pointer to the SCSI host structure
+ *              channel - channel of the device to add
+ *              id      - id of the device to add
+ *              lun     - lun of the device to add
+ *
+ * Returns:     0 on success or an error code
+ *
+ * Lock status: None needed.  However we do grab the BKL to avoid racing with
+ *              scsi_register*(), scsi_unregister*(), scsi_remove_single_device()
+ *              and anything else that might be messing with with the Scsi_Host or
+ *              other fundamental data structures.
+ *
+ * Notes:       This feature is probably unsafe for standard SCSI devices, 
+ *              but is perfectly normal for things like ieee1394 or USB 
+ *              drives since these busses are designed for hotplugging.  
+ *              Use at your own risk....
+ */
+int scsi_add_single_device(struct Scsi_Host *shpnt, int channel, 
+	int id, int lun)
+{
+	Scsi_Device *scd;
+
+	/* Do a bit of sanity checking */
+	if (shpnt==NULL) {
+		return  -ENXIO;
+	}
+
+	/* get the big kernel lock to avoid racing with
+	 * scsi_register*(), scsi_unregister*(), scsi_remove_single_device() 
+	 * or anything else that might be messing with with the Scsi_Host or
+	 * other fundamental data structures.  */
+	lock_kernel();
+
+	/* Check if they asked us to add an already existing device.
+	 * If so, ignore their misguided efforts. */
+	for (scd = shpnt->host_queue; scd; scd = scd->next) {
+		if ((scd->channel == channel && scd->id == id && scd->lun == lun)) {
+			break;
+		}
+	}
+	if (scd) {
+		unlock_kernel();
+		return -ENOSYS;
+	}
+
+	/* We have the BKL, so we should be safe while we muck 
+	 * about with Scsi_Device internals. */
+	scan_scsis(shpnt, 1, channel, id, lun);
+	unlock_kernel();
+	return 0;
+}
+
+/* Support for plugging/unplugging SCSI devices.  This feature is
+ * probably unsafe for standard SCSI devices, but is perfectly
+ * normal for things like ieee1394 or USB drives since these
+ * busses are designed for hotplugging.  Use at your own risk.... */
+/*
+ * Function:    scsi_remove_single_device()
+ *
+ * Purpose:     Support for hot-unplugging SCSI devices.  This function
+ *              implements the actual functionality for
+ *                  echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
+ *
+ * Arguments:   shpnt   - pointer to the SCSI host structure
+ *              channel - channel of the device to add
+ *              id      - id of the device to add
+ *              lun     - lun of the device to add
+ *
+ * Returns:     0 on success or an error code
+ *
+ * Lock status: None needed.  However we do grab the BKL to avoid racing with
+ *              scsi_register*(), scsi_unregister*(), scsi_add_single_device()
+ *              and anything else that might be messing with with the Scsi_Host or
+ *              other fundamental data structures.
+ *
+ * Notes:       This feature is probably unsafe for standard SCSI devices, 
+ *              but is perfectly normal for things like ieee1394 or USB 
+ *              drives since these busses are designed for hotplugging.  
+ *              Use at your own risk....
+ */
+int scsi_remove_single_device(struct Scsi_Host *shpnt, int channel, 
+	int id, int lun)
+{
+	Scsi_Device *scd;
+	struct Scsi_Device_Template *SDTpnt;
+
+	/* Do a bit of sanity checking */
+	if (shpnt==NULL) {
+		return  -ENODEV;
+	}
+
+	/* get the big kernel lock to avoid racing with
+	 * scsi_register*(), scsi_unregister*(), scsi_add_single_device() 
+	 * or anything else that might be messing with with the Scsi_Host or
+	 * other fundamental data structures.  */
+	lock_kernel();
+
+	/* Make sure the specified device is in fact present */
+	for (scd = shpnt->host_queue; scd; scd = scd->next) {
+		if ((scd->channel == channel && scd->id == id && scd->lun == lun)) {
+			break;
+		}
+	}
+	if (scd==NULL) {
+		unlock_kernel();
+		return -ENODEV;
+	}
+
+	/* See if the specified device is busy.  We have the BKL, so we
+	 * should not race with sd_open(), sd_release() and similar that 
+	 * increment access_count */
+	if (scd->access_count) {
+		unlock_kernel();
+		return -EBUSY;
+	}
+
+	SDTpnt = scsi_devicelist;
+	while (SDTpnt != NULL) {
+		if (SDTpnt->detach)
+			(*SDTpnt->detach) (scd);
+		SDTpnt = SDTpnt->next;
+	}
+
+	/* We have the BKL, so we should be safe while we muck 
+	 * about with Scsi_Device internals. */
+	if (scd->attached == 0) {
+		/* Nobody is using this device, so we
+		 * can now free all command structures.  */
+		if (shpnt->hostt->revoke)
+			shpnt->hostt->revoke(scd);
+		devfs_unregister (scd->de);
+		scsi_release_commandblocks(scd);
+
+		/* Now we can remove the device structure */
+		if (scd->next != NULL)
+			scd->next->prev = scd->prev;
+
+		if (scd->prev != NULL)
+			scd->prev->next = scd->next;
+
+		if (shpnt->host_queue == scd) {
+			shpnt->host_queue = scd->next;
+		}
+		blk_cleanup_queue(&scd->request_queue);
+		kfree((char *) scd);
+	}
+
+	unlock_kernel();
+	return 0;
+}
+
 #ifdef CONFIG_PROC_FS
 static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
 {
@@ -1605,8 +1764,6 @@
 static int proc_scsi_gen_write(struct file * file, const char * buf,
                               unsigned long length, void *data)
 {
-	struct Scsi_Device_Template *SDTpnt;
-	Scsi_Device *scd;
 	struct Scsi_Host *HBA_ptr;
 	char *p;
 	int host, channel, id, lun;
@@ -1721,13 +1878,12 @@
 	/*
 	 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
-	 * Consider this feature BETA.
+	 *
+	 * Consider this feature pre-BETA.
+	 *
 	 *     CAUTION: This is not for hotplugging your peripherals. As
 	 *     SCSI was not designed for this you could damage your
-	 *     hardware !
-	 * However perhaps it is legal to switch on an
-	 * already connected device. It is perhaps not
-	 * guaranteed this device doesn't corrupt an ongoing data transfer.
+	 *     hardware and thoroughly confuse the SCSI subsystem.
 	 */
 	if (!strncmp("add-single-device", buffer + 5, 17)) {
 		p = buffer + 23;
@@ -1745,33 +1901,11 @@
 				break;
 			}
 		}
-		err = -ENXIO;
-		if (!HBA_ptr)
-			goto out;
-
-		for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
-			if ((scd->channel == channel
-			     && scd->id == id
-			     && scd->lun == lun)) {
-				break;
-			}
-		}
-
-		err = -ENOSYS;
-		if (scd)
-			goto out;	/* We do not yet support unplugging */
-
-		scan_scsis(HBA_ptr, 1, channel, id, lun);
-
-		/* FIXME (DB) This assumes that the queue_depth routines can be used
-		   in this context as well, while they were all designed to be
-		   called only once after the detect routine. (DB) */
-		/* queue_depth routine moved to inside scan_scsis(,1,,,) so
-		   it is called before build_commandblocks() */
-
-		err = length;
+		if ((err=scsi_add_single_device(HBA_ptr, channel, id, lun))==0)
+		    err = length;
 		goto out;
 	}
+
 	/*
 	 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
@@ -1781,7 +1915,6 @@
 	 *     CAUTION: This is not for hotplugging your peripherals. As
 	 *     SCSI was not designed for this you could damage your
 	 *     hardware and thoroughly confuse the SCSI subsystem.
-	 *
 	 */
 	else if (!strncmp("remove-single-device", buffer + 5, 20)) {
 		p = buffer + 26;
@@ -1797,58 +1930,8 @@
 				break;
 			}
 		}
-		err = -ENODEV;
-		if (!HBA_ptr)
-			goto out;
-
-		for (scd = HBA_ptr->host_queue; scd; scd = scd->next) {
-			if ((scd->channel == channel
-			     && scd->id == id
-			     && scd->lun == lun)) {
-				break;
-			}
-		}
-
-		if (scd == NULL)
-			goto out;	/* there is no such device attached */
-
-		err = -EBUSY;
-		if (scd->access_count)
-			goto out;
-
-		SDTpnt = scsi_devicelist;
-		while (SDTpnt != NULL) {
-			if (SDTpnt->detach)
-				(*SDTpnt->detach) (scd);
-			SDTpnt = SDTpnt->next;
-		}
-
-		if (scd->attached == 0) {
-			/*
-			 * Nobody is using this device any more.
-			 * Free all of the command structures.
-			 */
-                        if (HBA_ptr->hostt->revoke)
-                                HBA_ptr->hostt->revoke(scd);
-			devfs_unregister (scd->de);
-			scsi_release_commandblocks(scd);
-
-			/* Now we can remove the device structure */
-			if (scd->next != NULL)
-				scd->next->prev = scd->prev;
-
-			if (scd->prev != NULL)
-				scd->prev->next = scd->next;
-
-			if (HBA_ptr->host_queue == scd) {
-				HBA_ptr->host_queue = scd->next;
-			}
-			blk_cleanup_queue(&scd->request_queue);
-			kfree((char *) scd);
-		} else {
-			goto out;
-		}
-		err = 0;
+		err=scsi_remove_single_device(HBA_ptr, channel, id, lun);
+		goto out;
 	}
 out:
 	
--- linux-2.4.19/drivers/ieee1394/sbp2.c	2002-10-18 05:16:14.000000000 -0500
+++ linux-2.4.20-pre10-erik/drivers/ieee1394/sbp2.c	2002-10-18 05:29:20.000000000 -0500
@@ -562,6 +562,8 @@
 	.update = 	sbp2_update
 };
 
+static int sbp2_init_has_finished_initializing;
+
 /* List of device firmware's that require a forced 36 byte inquiry.  */
 static u32 sbp2_broken_inquiry_list[] = {
 	0x00002800,	/* Stefan Richter <richtest@bauwesen.tu-cottbus.de> */
@@ -1082,6 +1084,8 @@
 
 	hpsb_register_protocol(&sbp2_driver);
 
+	sbp2_init_has_finished_initializing++;
+
 	return 0;
 }
 
@@ -1681,6 +1685,23 @@
 
 	SBP2_INFO("Logged into SBP-2 device");
 
+	/* Try to hook ourselves into the SCSI subsystem, but only if
+	 * sbp2_init has finished doing its thing.  If we register
+	 * anything while sbp2_init is handling coldplugged devices, we
+	 * will will end up double registering things (a bad idea). */
+	if (sbp2_init_has_finished_initializing) {
+	    struct Scsi_Host *HBA_ptr;
+	    HBA_ptr=hi->scsi_host;
+	    if (HBA_ptr==NULL) {
+		return 0;
+	    }
+	    if (scsi_add_single_device(HBA_ptr, 0, scsi_id->id, 0)==0) {
+		SBP2_DEBUG("connecting SBP-2 device into the SCSI subsystem");
+	    } else {
+		SBP2_INFO("Unable to connect SBP-2 device into the SCSI subsystem");
+	    }
+	}
+
 	return(0);
 
 }
@@ -1739,8 +1760,23 @@
 
 	SBP2_INFO("Logged out of SBP-2 device");
 
-	return(0);
+	/* Now that we are logged out of the SBP-2 device, lets
+	 * try to un-hook ourselves from the SCSI subsystem */
+	{
+	    struct Scsi_Host *HBA_ptr;
+
+	    HBA_ptr=hi->scsi_host;
+	    if (HBA_ptr==NULL) {
+		return 0;
+	    }
+	    if (scsi_remove_single_device(HBA_ptr, 0, scsi_id->id, 0)==0) {
+		SBP2_DEBUG("disconnecting SBP-2 device from the SCSI subsystem");
+	    } else {
+		SBP2_INFO("Unable to disconnect SBP-2 device from the SCSI subsystem");
+	    }
+	}
 
+	return(0);
 }
 
 /*

  reply	other threads:[~2002-10-18 11:28 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-10-14  5:40 [PATCH] SCSI hotplug support Erik Andersen
2002-10-14  7:06 ` Matthew Dharm
2002-10-14  7:12   ` Erik Andersen
2002-10-14 15:47     ` Kurt Garloff
2002-10-14 16:19       ` Mike Anderson
2002-10-14 20:41         ` Erik Andersen
2002-10-14 22:10           ` Mike Anderson
2002-10-14 20:46       ` Erik Andersen
2002-10-14 15:57 ` James Bottomley
2002-10-14 17:22   ` Mike Anderson
2002-10-14 17:30   ` Matthew Dharm
2002-10-14 17:39     ` James Bottomley
2002-10-14 19:11       ` Oliver Xymoron
2002-10-15  0:42       ` Kurt Garloff
2002-10-14 20:37   ` Erik Andersen
2002-10-14 21:07     ` James Bottomley
2002-10-14 21:54       ` Erik Andersen
2002-10-14 22:25         ` Doug Ledford
2002-10-15  5:25           ` Erik Andersen
2002-10-15 15:33             ` Doug Ledford
2002-10-15 18:18               ` Erik Andersen
2002-10-15 18:22             ` Doug Ledford
2002-10-15 18:45               ` Erik Andersen
2002-10-15 19:13                 ` Doug Ledford
2002-10-15 19:32                   ` Erik Andersen
2002-10-15 19:45                     ` James Bottomley
2002-10-15 19:50                     ` Scott Merritt
2002-10-15 19:55                     ` Doug Ledford
2002-10-15 22:07                       ` Erik Andersen
2002-10-16  2:40                         ` Doug Ledford
2002-10-18 11:28                           ` Erik Andersen [this message]
2002-10-15 21:43                 ` Oliver Neukum
2002-10-15 22:07                   ` Erik Andersen
2002-10-14 22:19       ` Oliver Neukum
2002-10-15  0:22         ` Doug Ledford
2002-10-15  7:53           ` Oliver Neukum
2002-10-15 14:35             ` Doug Ledford
2002-10-15 15:19               ` Oliver Neukum
2002-10-15 15:40                 ` James Bottomley
2002-10-15 17:47               ` Erik Andersen
2002-10-15 18:34                 ` Doug Ledford
2002-10-15 18:22               ` Scott Merritt

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=20021018112814.GA32693@codepoet.org \
    --to=andersen@codepoet.org \
    --cc=James.Bottomley@steeleye.com \
    --cc=linux-scsi@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox