From: Brian King <brking@us.ibm.com>
To: Alan Stern <stern@rowland.harvard.edu>
Cc: James Bottomley <James.Bottomley@SteelEye.com>,
Mike Anderson <andmike@us.ibm.com>,
SCSI development list <linux-scsi@vger.kernel.org>
Subject: Re: Questions about scsi_target_reap and starget/sdev lifecyle
Date: Mon, 20 Jun 2005 10:52:34 -0500 [thread overview]
Message-ID: <42B6E642.3000703@us.ibm.com> (raw)
In-Reply-To: <Pine.LNX.4.44L0.0506181554270.3347-100000@netrider.rowland.org>
Alan Stern wrote:
> Don't acquire the scan_mutex in scsi_remove_device. It's
> not needed since there's nothing wrong with removing devices
> during scanning, and it will cause deadlock under certain
> error conditions. (I'm not certain about this either. Will
> removing a device as it's being scanned cause a problem?
> Perhaps there should be a separate version of the routine
> that does acquire the scan_mutex.)
I actually submitted the patch to acquire the scan_mutex in scsi_remove_device
in order to fix an oops I ran into where a device was being deleted while it
was still being scanned. The actual oops was a NULL dentry in sysfs_remove_link.
I would much rather see a __scsi_remove_device API that scsi core and scsi_remove_device
can call that does not acquire the scan_mutex in the paths that make sense to
change. That way scsi_remove_device can maintain its existing behavior.
Brian
>
> Make scsi_forget_host remove all devices, not all targets.
>
> Make the loops to remove devices in scsi_forget_host and
> __scsi_remove_target restart from the beginning every time
> a device is removed (rather than using list_for_each_entry_safe,
> which is very definitely _unsafe_), and skip over devices
> that are already in the SDEV_DEL state.
>
> This fixes several oopses I encountered during testing. Do you think this
> is ready to be applied?
>
> Alan Stern
>
>
>
> Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
>
> --- a/include/scsi/scsi_host.h Mon Jun 13 14:57:26 2005
> +++ b/include/scsi/scsi_host.h Fri Jun 17 22:19:09 2005
> @@ -411,6 +411,7 @@
> SHOST_DEL,
> SHOST_CANCEL,
> SHOST_RECOVERY,
> + SHOST_REMOVE,
> };
>
> struct Scsi_Host {
> --- a/drivers/scsi/hosts.c Mon Jun 13 14:57:14 2005
> +++ b/drivers/scsi/hosts.c Fri Jun 17 22:20:19 2005
> @@ -74,8 +74,13 @@
> **/
> void scsi_remove_host(struct Scsi_Host *shost)
> {
> - scsi_forget_host(shost);
> + set_bit(SHOST_REMOVE, &shost->shost_state);
> scsi_host_cancel(shost, 0);
> +
> + down(&shost->scan_mutex); /* Wait for scanning to stop */
> + up(&shost->scan_mutex);
> +
> + scsi_forget_host(shost);
> scsi_proc_host_rm(shost);
>
> set_bit(SHOST_DEL, &shost->shost_state);
> --- a/drivers/scsi/scsi_scan.c Mon Jun 13 14:58:06 2005
> +++ b/drivers/scsi/scsi_scan.c Fri Jun 17 22:59:52 2005
> @@ -843,8 +843,12 @@
> out_free_sdev:
> if (res == SCSI_SCAN_LUN_PRESENT) {
> if (sdevp) {
> - scsi_device_get(sdev);
> - *sdevp = sdev;
> + if (scsi_device_get(sdev) == 0) {
> + *sdevp = sdev;
> + } else {
> + scsi_remove_device(sdev);
> + res = SCSI_SCAN_NO_RESPONSE;
> + }
> }
> } else {
> if (sdev->host->hostt->slave_destroy)
> @@ -1186,22 +1190,28 @@
> uint id, uint lun, void *hostdata)
> {
> struct scsi_device *sdev;
> - struct device *parent = &shost->shost_gendev;
> int res;
> - struct scsi_target *starget = scsi_alloc_target(parent, channel, id);
> + struct scsi_target *starget;
>
> - if (!starget)
> - return ERR_PTR(-ENOMEM);
> + down(&shost->scan_mutex);
> + if (test_bit(SHOST_REMOVE, &shost->shost_state)) {
> + sdev = ERR_PTR(-ENODEV);
> + goto out;
> + }
> + starget = scsi_alloc_target(&shost->shost_gendev, channel, id);
> + if (!starget) {
> + sdev = ERR_PTR(-ENOMEM);
> + goto out;
> + }
>
> get_device(&starget->dev);
> - down(&shost->scan_mutex);
> res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
> if (res != SCSI_SCAN_LUN_PRESENT)
> sdev = ERR_PTR(-ENODEV);
> - up(&shost->scan_mutex);
> scsi_target_reap(starget);
> put_device(&starget->dev);
> -
> +out:
> + up(&shost->scan_mutex);
> return sdev;
> }
> EXPORT_SYMBOL(__scsi_add_device);
> @@ -1222,29 +1232,9 @@
> }
> EXPORT_SYMBOL(scsi_rescan_device);
>
> -/**
> - * scsi_scan_target - scan a target id, possibly including all LUNs on the
> - * target.
> - * @sdevsca: Scsi_Device handle for scanning
> - * @shost: host to scan
> - * @channel: channel to scan
> - * @id: target id to scan
> - *
> - * Description:
> - * Scan the target id on @shost, @channel, and @id. Scan at least LUN
> - * 0, and possibly all LUNs on the target id.
> - *
> - * Use the pre-allocated @sdevscan as a handle for the scanning. This
> - * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the
> - * scanning functions modify sdevscan->lun.
> - *
> - * First try a REPORT LUN scan, if that does not scan the target, do a
> - * sequential scan of LUNs on the target id.
> - **/
> -void scsi_scan_target(struct device *parent, unsigned int channel,
> - unsigned int id, unsigned int lun, int rescan)
> +static void __scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
> + unsigned int id, unsigned int lun, int rescan)
> {
> - struct Scsi_Host *shost = dev_to_shost(parent);
> int bflags = 0;
> int res;
> struct scsi_device *sdev = NULL;
> @@ -1257,7 +1247,7 @@
> return;
>
>
> - starget = scsi_alloc_target(parent, channel, id);
> + starget = scsi_alloc_target(&shost->shost_gendev, channel, id);
>
> if (!starget)
> return;
> @@ -1304,6 +1294,32 @@
>
> put_device(&starget->dev);
> }
> +
> +/**
> + * scsi_scan_target - scan a target id, possibly including all LUNs on the
> + * target.
> + * @parent: host to scan
> + * @channel: channel to scan
> + * @id: target id to scan
> + * @rescan: passed to LUN scanning routines
> + *
> + * Description:
> + * Scan the target id on @shost, @channel, and @id. Scan at least LUN
> + * 0, and possibly all LUNs on the target id.
> + *
> + * First try a REPORT LUN scan, if that does not scan the target, do a
> + * sequential scan of LUNs on the target id.
> + **/
> +void scsi_scan_target(struct device *parent, unsigned int channel,
> + unsigned int id, unsigned int lun, int rescan)
> +{
> + struct Scsi_Host *shost = dev_to_shost(parent);
> +
> + down(&shost->scan_mutex);
> + if (!test_bit(SHOST_REMOVE, &shost->shost_state))
> + __scsi_scan_target(shost, channel, id, lun, rescan);
> + up(&shost->scan_mutex);
> +}
> EXPORT_SYMBOL(scsi_scan_target);
>
> static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
> @@ -1329,10 +1345,10 @@
> order_id = shost->max_id - id - 1;
> else
> order_id = id;
> - scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan);
> + __scsi_scan_target(shost, channel, order_id, lun, rescan);
> }
> else
> - scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan);
> + __scsi_scan_target(shost, channel, id, lun, rescan);
> }
>
> int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
> @@ -1347,11 +1363,14 @@
> return -EINVAL;
>
> down(&shost->scan_mutex);
> + if (test_bit(SHOST_REMOVE, &shost->shost_state))
> + goto out;
> if (channel == SCAN_WILD_CARD)
> for (channel = 0; channel <= shost->max_channel; channel++)
> scsi_scan_channel(shost, channel, id, lun, rescan);
> else
> scsi_scan_channel(shost, channel, id, lun, rescan);
> +out:
> up(&shost->scan_mutex);
>
> return 0;
> @@ -1383,23 +1402,17 @@
>
> void scsi_forget_host(struct Scsi_Host *shost)
> {
> - struct scsi_target *starget, *tmp;
> + struct scsi_device *sdev;
> unsigned long flags;
>
> - /*
> - * Ok, this look a bit strange. We always look for the first device
> - * on the list as scsi_remove_device removes them from it - thus we
> - * also have to release the lock.
> - * We don't need to get another reference to the device before
> - * releasing the lock as we already own the reference from
> - * scsi_register_device that's release in scsi_remove_device. And
> - * after that we don't look at sdev anymore.
> - */
> +restart:
> spin_lock_irqsave(shost->host_lock, flags);
> - list_for_each_entry_safe(starget, tmp, &shost->__targets, siblings) {
> + list_for_each_entry(sdev, &shost->__devices, siblings) {
> + if (sdev->sdev_state == SDEV_DEL)
> + continue;
> spin_unlock_irqrestore(shost->host_lock, flags);
> - scsi_remove_target(&starget->dev);
> - spin_lock_irqsave(shost->host_lock, flags);
> + scsi_remove_device(sdev);
> + goto restart;
> }
> spin_unlock_irqrestore(shost->host_lock, flags);
> }
> @@ -1426,12 +1439,16 @@
> */
> struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
> {
> - struct scsi_device *sdev;
> + struct scsi_device *sdev = NULL;
> struct scsi_target *starget;
>
> + down(&shost->scan_mutex);
> + if (test_bit(SHOST_REMOVE, &shost->shost_state))
> + goto out;
> +
> starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id);
> if (!starget)
> - return NULL;
> + goto out;
>
> sdev = scsi_alloc_sdev(starget, 0, NULL);
> if (sdev) {
> @@ -1439,6 +1456,8 @@
> sdev->borken = 0;
> }
> put_device(&starget->dev);
> +out:
> + up(&shost->scan_mutex);
> return sdev;
> }
> EXPORT_SYMBOL(scsi_get_host_dev);
> --- a/drivers/scsi/scsi_sysfs.c Fri Jun 17 22:23:37 2005
> +++ b/drivers/scsi/scsi_sysfs.c Fri Jun 17 22:25:18 2005
> @@ -631,11 +631,8 @@
> **/
> void scsi_remove_device(struct scsi_device *sdev)
> {
> - struct Scsi_Host *shost = sdev->host;
> -
> - down(&shost->scan_mutex);
> if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
> - goto out;
> + return;
>
> class_device_unregister(&sdev->sdev_classdev);
> device_del(&sdev->sdev_gendev);
> @@ -644,8 +641,6 @@
> sdev->host->hostt->slave_destroy(sdev);
> transport_unregister_device(&sdev->sdev_gendev);
> put_device(&sdev->sdev_gendev);
> -out:
> - up(&shost->scan_mutex);
> }
> EXPORT_SYMBOL(scsi_remove_device);
>
> @@ -653,17 +648,20 @@
> {
> struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
> unsigned long flags;
> - struct scsi_device *sdev, *tmp;
> + struct scsi_device *sdev;
>
> spin_lock_irqsave(shost->host_lock, flags);
> starget->reap_ref++;
> - list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
> +restart:
> + list_for_each_entry(sdev, &shost->__devices, siblings) {
> if (sdev->channel != starget->channel ||
> - sdev->id != starget->id)
> + sdev->id != starget->id ||
> + sdev->sdev_state == SDEV_DEL)
> continue;
> spin_unlock_irqrestore(shost->host_lock, flags);
> scsi_remove_device(sdev);
> spin_lock_irqsave(shost->host_lock, flags);
> + goto restart;
> }
> spin_unlock_irqrestore(shost->host_lock, flags);
> scsi_target_reap(starget);
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Brian King
eServer Storage I/O
IBM Linux Technology Center
next prev parent reply other threads:[~2005-06-20 15:52 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-06-14 21:27 Questions about scsi_target_reap and starget/sdev lifecyle Alan Stern
2005-06-15 3:28 ` James Bottomley
2005-06-15 20:07 ` Alan Stern
2005-06-15 21:11 ` Alan Stern
2005-06-15 23:03 ` James Bottomley
2005-06-16 2:22 ` Alan Stern
2005-06-16 7:31 ` Mike Anderson
2005-06-16 13:57 ` James Bottomley
2005-06-17 2:01 ` Alan Stern
2005-06-18 20:14 ` Alan Stern
2005-06-20 15:52 ` Brian King [this message]
2005-06-20 16:35 ` Alan Stern
2005-06-20 17:31 ` Patrick Mansfield
2005-06-20 19:24 ` Alan Stern
2005-06-21 17:12 ` Mike Anderson
2005-06-21 17:43 ` Patrick Mansfield
2005-06-21 19:24 ` Mike Anderson
2005-06-21 20:04 ` Alan Stern
2005-06-21 20:10 ` Christoph Hellwig
2005-06-21 20:33 ` Alan Stern
2005-06-21 20:58 ` Mike Anderson
2005-06-21 21:22 ` Alan Stern
2005-06-22 13:44 ` Luben Tuikov
2005-06-22 13:36 ` Luben Tuikov
2005-06-22 15:12 ` Alan Stern
2005-06-22 15:46 ` Luben Tuikov
2005-06-22 16:16 ` Alan Stern
2005-06-22 16:53 ` Luben Tuikov
2005-06-21 21:08 ` Mike Anderson
2005-06-21 21:37 ` Alan Stern
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=42B6E642.3000703@us.ibm.com \
--to=brking@us.ibm.com \
--cc=James.Bottomley@SteelEye.com \
--cc=andmike@us.ibm.com \
--cc=linux-scsi@vger.kernel.org \
--cc=stern@rowland.harvard.edu \
/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