public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* Questions about scsi_target_reap and starget/sdev lifecyle
@ 2005-06-14 21:27 Alan Stern
  2005-06-15  3:28 ` James Bottomley
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-14 21:27 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI development list

James:

Can you or anyone else please explain what scsi_target_reap() and
especially what scsi_target.reap_ref are for?  What's the point of this
home-brewed reference counting scheme when there's already a perfectly
useful embedded struct device inside scsi_target?  Why isn't
scsi_target_reap() simply the release routine for the embedded device?

In addition, can someone please explain how long a scsi_target structure 
and a scsi_device structure are supposed to remain linked into the host's 
__targets and __devices lists once they have been removed?  The 
scsi_device link isn't severed until the release routine runs, which can 
be quite a long time after the device has been removed.  The scsi_target 
link is severed in scsi_target_reap(), which should be thought of as a
kind of release routine as well.

Won't this cause trouble in a hotplug environment?  A target or a LUN can 
be removed, and while it's still on the host's list a new target or LUN 
could be discovered with the same ID.  Having multiple entries on a list 
with the same ID sounds like a bad idea.  Is there some reason why these 
things aren't taken off their list as soon as they are removed?

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  0 siblings, 2 replies; 30+ messages in thread
From: James Bottomley @ 2005-06-15  3:28 UTC (permalink / raw)
  To: Alan Stern; +Cc: SCSI development list

On Tue, 2005-06-14 at 17:27 -0400, Alan Stern wrote:
> Can you or anyone else please explain what scsi_target_reap() and
> especially what scsi_target.reap_ref are for?  What's the point of this
> home-brewed reference counting scheme when there's already a perfectly
> useful embedded struct device inside scsi_target?  Why isn't
> scsi_target_reap() simply the release routine for the embedded device?

Look at how scsi_target_reap works: it's the decider for visibility
removal, not actual release, that's why we can't use the device
reference, which is the decider for release.  I could have used a kref,
but, given the special locking conventions and the tying to the __target
list, the call would have had to be wrappered anyway, so there didn't
seem to to be much point.

A target is basically a self destroying entity which is why the
complexity.

> In addition, can someone please explain how long a scsi_target structure 
> and a scsi_device structure are supposed to remain linked into the host's 
> __targets and __devices lists once they have been removed?  The 
> scsi_device link isn't severed until the release routine runs, which can 
> be quite a long time after the device has been removed.  The scsi_target 
> link is severed in scsi_target_reap(), which should be thought of as a
> kind of release routine as well.

__target entries are removed when the target goes invisible (and for
this to happen, all the devices have to be already gone).  __devices
when the device is released.

> Won't this cause trouble in a hotplug environment?  A target or a LUN can 
> be removed, and while it's still on the host's list a new target or LUN 
> could be discovered with the same ID.  Having multiple entries on a list 
> with the same ID sounds like a bad idea.  Is there some reason why these 
> things aren't taken off their list as soon as they are removed?

It shouldn't, that's what rescan is about.  If the device isn't gone,
you get the old device back again.

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-15  3:28 ` James Bottomley
@ 2005-06-15 20:07   ` Alan Stern
  2005-06-15 21:11   ` Alan Stern
  1 sibling, 0 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-15 20:07 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI development list

On Tue, 14 Jun 2005, James Bottomley wrote:

> > Won't this cause trouble in a hotplug environment?  A target or a LUN can 
> > be removed, and while it's still on the host's list a new target or LUN 
> > could be discovered with the same ID.  Having multiple entries on a list 
> > with the same ID sounds like a bad idea.  Is there some reason why these 
> > things aren't taken off their list as soon as they are removed?
> 
> It shouldn't, that's what rescan is about.  If the device isn't gone,
> you get the old device back again.

Is this wise?  With hotplugging, it's possible that the new device is
physically different from the one that was there before.  You don't want
to re-use an old structure with stale data in it when that happens.

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  1 sibling, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-15 21:11 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI development list

On Tue, 14 Jun 2005, James Bottomley wrote:

> On Tue, 2005-06-14 at 17:27 -0400, Alan Stern wrote:
> > Can you or anyone else please explain what scsi_target_reap() and
> > especially what scsi_target.reap_ref are for?  What's the point of this
> > home-brewed reference counting scheme when there's already a perfectly
> > useful embedded struct device inside scsi_target?  Why isn't
> > scsi_target_reap() simply the release routine for the embedded device?
> 
> Look at how scsi_target_reap works: it's the decider for visibility
> removal, not actual release, that's why we can't use the device
> reference, which is the decider for release.  I could have used a kref,
> but, given the special locking conventions and the tying to the __target
> list, the call would have had to be wrappered anyway, so there didn't
> seem to to be much point.
> 
> A target is basically a self destroying entity which is why the
> complexity.

> __target entries are removed when the target goes invisible (and for
> this to happen, all the devices have to be already gone).  __devices
> when the device is released.

This means that scsi_target_reap can be called and the __targets list 
changed essentially at any time (subject only to the host_lock).  Hence it 
is impossible for scsi_forget_host to iterate through the list of targets 
belonging to the host: While it is working to remove one target, the next 
target on the list (stored in the tmp variable) might be removed by 
another thread.

In fact there doesn't seem to be any safe way to remove all the targets
from a host.  And what's to prevent scsi_target_reap being called twice
for the same target?

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-15 21:11   ` Alan Stern
@ 2005-06-15 23:03     ` James Bottomley
  2005-06-16  2:22       ` Alan Stern
  0 siblings, 1 reply; 30+ messages in thread
From: James Bottomley @ 2005-06-15 23:03 UTC (permalink / raw)
  To: Alan Stern; +Cc: SCSI development list

On Wed, 2005-06-15 at 17:11 -0400, Alan Stern wrote:
> This means that scsi_target_reap can be called and the __targets list 
> changed essentially at any time (subject only to the host_lock).  Hence it 
> is impossible for scsi_forget_host to iterate through the list of targets 
> belonging to the host: While it is working to remove one target, the next 
> target on the list (stored in the tmp variable) might be removed by 
> another thread.

It's no better nor worse than we already have.  As has been said many
times before, we need a proper host state model.

> In fact there doesn't seem to be any safe way to remove all the targets
> from a host.  And what's to prevent scsi_target_reap being called twice
> for the same target?

The usage, if you look at the code ... it's alloc/reap or inc
reap_ref/reap

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-15 23:03     ` James Bottomley
@ 2005-06-16  2:22       ` Alan Stern
  2005-06-16  7:31         ` Mike Anderson
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-16  2:22 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI development list

On Wed, 15 Jun 2005, James Bottomley wrote:

> On Wed, 2005-06-15 at 17:11 -0400, Alan Stern wrote:
> > This means that scsi_target_reap can be called and the __targets list 
> > changed essentially at any time (subject only to the host_lock).  Hence it 
> > is impossible for scsi_forget_host to iterate through the list of targets 
> > belonging to the host: While it is working to remove one target, the next 
> > target on the list (stored in the tmp variable) might be removed by 
> > another thread.
> 
> It's no better nor worse than we already have.  As has been said many
> times before, we need a proper host state model.
> 
> > In fact there doesn't seem to be any safe way to remove all the targets
> > from a host.  And what's to prevent scsi_target_reap being called twice
> > for the same target?
> 
> The usage, if you look at the code ... it's alloc/reap or inc
> reap_ref/reap

Okay, I understand a little better now.

Would it hurt anything to change scsi_remove_host so that SHOST_DEL gets
set at the start, before scsi_forget_host is called?  If that were done,
then we could make every pathway that adds a device acquire the scan_mutex
and fail if SHOST_DEL is already set.  Thus no devices would ever be added
after forget_host had removed the existing ones, which can happen as
things stand.

Furthermore, forget_host could be changed to loop over the __devices list
instead of the __targets list.  The net effect would be the same: All the
devices on the host would be removed and the targets would automatically
get reaped by scsi_device_dev_release.  There wouldn't be any need to
iterate over the targets at all.

As a final change, the new loop-over-devices in forget_host and the one in
__scsi_remove_target should be made to skip over devices with SDEV_DEL
already set, and each time they call scsi_remove_device the loops should
restart from the beginning.  This will eliminate the problem of the tmp
pointer being pulled out from under the code (even though it has 
quadratic behavior).

Do these changes sound okay?

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-16  2:22       ` Alan Stern
@ 2005-06-16  7:31         ` Mike Anderson
  2005-06-16 13:57           ` James Bottomley
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Anderson @ 2005-06-16  7:31 UTC (permalink / raw)
  To: Alan Stern; +Cc: James Bottomley, SCSI development list

Alan Stern [stern@rowland.harvard.edu] wrote:
> On Wed, 15 Jun 2005, James Bottomley wrote:
> 
> > On Wed, 2005-06-15 at 17:11 -0400, Alan Stern wrote:
> > > This means that scsi_target_reap can be called and the __targets list 
> > > changed essentially at any time (subject only to the host_lock).  Hence it 
> > > is impossible for scsi_forget_host to iterate through the list of targets 
> > > belonging to the host: While it is working to remove one target, the next 
> > > target on the list (stored in the tmp variable) might be removed by 
> > > another thread.
> > 
> > It's no better nor worse than we already have.  As has been said many
> > times before, we need a proper host state model.
> > 
> > > In fact there doesn't seem to be any safe way to remove all the targets
> > > from a host.  And what's to prevent scsi_target_reap being called twice
> > > for the same target?
> > 
> > The usage, if you look at the code ... it's alloc/reap or inc
> > reap_ref/reap
> 
> Okay, I understand a little better now.
> 
> Would it hurt anything to change scsi_remove_host so that SHOST_DEL gets
> set at the start, before scsi_forget_host is called?  If that were done,
> then we could make every pathway that adds a device acquire the scan_mutex
> and fail if SHOST_DEL is already set.  Thus no devices would ever be added
> after forget_host had removed the existing ones, which can happen as
> things stand.
> 
> Furthermore, forget_host could be changed to loop over the __devices list
> instead of the __targets list.  The net effect would be the same: All the
> devices on the host would be removed and the targets would automatically
> get reaped by scsi_device_dev_release.  There wouldn't be any need to
> iterate over the targets at all.
> 
> As a final change, the new loop-over-devices in forget_host and the one in
> __scsi_remove_target should be made to skip over devices with SDEV_DEL
> already set, and each time they call scsi_remove_device the loops should
> restart from the beginning.  This will eliminate the problem of the tmp
> pointer being pulled out from under the code (even though it has 
> quadratic behavior).
> 
> Do these changes sound okay?
> 

I have something similar that I was testing since you mentioned the
problem the other day. Our build machine went down so I will need to wait
until tomorrow to get access to my patches for a post, if you have already
rolled a patch we can compare notes when I post.

What I did in the patch sequence was:
	1.) Recode the scsi_host state model to align with the device
	state model (i.e. added scsi_host_set_state function and
	associated changes).
	2.) Made shost cancel matched the scsi device state model and set
	this at the top of scsi_remove_host. Previous cancel code was not
	doing anything as the list was normally empty do to
	scsi_forget_host being called first. Also there where possible
	race conditions that you previously mentioned about canceling
	commands in this method.
	3.) Added choke point checks under the scan_mutex to determine if
	scanning is allowed (scsi_host_scan_allowed).
	4.) Added a target state model to match the scsi device state
	model.
	5.) I use the STGT_CANCEL and STGT_DEL states in scsi_forget_host
	to skip over entries in the list as I am no longer using the
	_safe version.
This looks like a good starting point with limited testing. I also need to
more states to the target state model as I only added a few that I needed
for the delete code.

Anyway sorry about the delay.

-andmike
--
Michael Anderson
andmike@us.ibm.com


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  0 siblings, 2 replies; 30+ messages in thread
From: James Bottomley @ 2005-06-16 13:57 UTC (permalink / raw)
  To: Mike Anderson; +Cc: Alan Stern, SCSI development list

On Thu, 2005-06-16 at 00:31 -0700, Mike Anderson wrote:

> I have something similar that I was testing since you mentioned the
> problem the other day. Our build machine went down so I will need to wait
> until tomorrow to get access to my patches for a post, if you have already
> rolled a patch we can compare notes when I post.
> 
> What I did in the patch sequence was:
> 	1.) Recode the scsi_host state model to align with the device
> 	state model (i.e. added scsi_host_set_state function and
> 	associated changes).
> 	2.) Made shost cancel matched the scsi device state model and set
> 	this at the top of scsi_remove_host. Previous cancel code was not
> 	doing anything as the list was normally empty do to
> 	scsi_forget_host being called first. Also there where possible
> 	race conditions that you previously mentioned about canceling
> 	commands in this method.
> 	3.) Added choke point checks under the scan_mutex to determine if
> 	scanning is allowed (scsi_host_scan_allowed).
> 	4.) Added a target state model to match the scsi device state
> 	model.
> 	5.) I use the STGT_CANCEL and STGT_DEL states in scsi_forget_host
> 	to skip over entries in the list as I am no longer using the
> 	_safe version.
> This looks like a good starting point with limited testing. I also need to
> more states to the target state model as I only added a few that I needed
> for the delete code.

That sounds something like what I was thinking.

There is some subtlety to this: the call to forget host with existing
targets must:

a) put the host into a state where it won't accept any additions or
removal.
b) loop over targets and devices removing them from visibility and doing
final puts for them
c) finally remove the host from visibility, set the deleted state and do
the final put (which may not remove the actual structure until the last
referrer relinquishes its reference).

Also, since the target is a pure container, I don't think it needs to be
a party to the state model.

Anyway, post the code and we can refine it.

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-16 13:57           ` James Bottomley
@ 2005-06-17  2:01             ` Alan Stern
  2005-06-18 20:14             ` Alan Stern
  1 sibling, 0 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-17  2:01 UTC (permalink / raw)
  To: James Bottomley; +Cc: Mike Anderson, SCSI development list

On Thu, 16 Jun 2005, James Bottomley wrote:

> On Thu, 2005-06-16 at 00:31 -0700, Mike Anderson wrote:
> 
> > I have something similar that I was testing since you mentioned the
> > problem the other day. Our build machine went down so I will need to wait
> > until tomorrow to get access to my patches for a post, if you have already
> > rolled a patch we can compare notes when I post.
> > 
> > What I did in the patch sequence was:
> > 	1.) Recode the scsi_host state model to align with the device
> > 	state model (i.e. added scsi_host_set_state function and
> > 	associated changes).
> > 	2.) Made shost cancel matched the scsi device state model and set
> > 	this at the top of scsi_remove_host. Previous cancel code was not
> > 	doing anything as the list was normally empty do to
> > 	scsi_forget_host being called first. Also there where possible
> > 	race conditions that you previously mentioned about canceling
> > 	commands in this method.
> > 	3.) Added choke point checks under the scan_mutex to determine if
> > 	scanning is allowed (scsi_host_scan_allowed).
> > 	4.) Added a target state model to match the scsi device state
> > 	model.
> > 	5.) I use the STGT_CANCEL and STGT_DEL states in scsi_forget_host
> > 	to skip over entries in the list as I am no longer using the
> > 	_safe version.
> > This looks like a good starting point with limited testing. I also need to
> > more states to the target state model as I only added a few that I needed
> > for the delete code.

That's more ambitious than what I was planning, although it seems to cover
much the same ground.  At the moment my patchwork is only partly finished.

> That sounds something like what I was thinking.
> 
> There is some subtlety to this: the call to forget host with existing
> targets must:
> 
> a) put the host into a state where it won't accept any additions or
> removal.

I don't see anything wrong with allowing removals, but certainly it 
shouldn't accept any additions.  Would it be best to add a new 
SHOST_REMOVED state bit and test for it every time the scan_mutex is 
acquired?

> b) loop over targets and devices removing them from visibility and doing
> final puts for them

There's no point in looping over targets.  As the devices get released
they will automatically take the targets along with them.  Visibility of 
targets won't matter because no new devices can be created.

> c) finally remove the host from visibility, set the deleted state and do
> the final put (which may not remove the actual structure until the last
> referrer relinquishes its reference).

Aside from the visibility part, this is already done by scsi_remove_host 
and the LLDs, right?

> Also, since the target is a pure container, I don't think it needs to be
> a party to the state model.

Agreed.

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  2005-06-21 17:12               ` Mike Anderson
  1 sibling, 2 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-18 20:14 UTC (permalink / raw)
  To: James Bottomley; +Cc: Mike Anderson, SCSI development list

James and Mike:

Here's my patch (as529), very lightly tested.  Among the changes:

	Introduce an SHOST_REMOVE state bit, set it at the start
	of scsi_remove_host, and check it every time scan_mutex is
	acquired.

	In scsi_remove_host, call scsi_host_cancel _before_ calling
	scsi_forget_host so that outstanding I/O really does get
	cancelled.  I don't think this will cause any problems,
	but you know the code better than I do.

	Fix an unchecked call to scsi_device_get in
	scsi_probe_and_add_lun.  The call can fail (if the LLD is
	being unloaded, for example) in which case the patch calls
	scsi_remove_device.  This worked, but I'm not certain it's
	exactly the right way to handle the failure.

	Rename scsi_scan_target to __scsi_scan_target, and introduce
	a new exported function named scsi_scan_target, which merely
	acquires the scan_mutex around a call to the old routine.
	Change existing calls so they refer to __scsi_scan_target.

	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.)

	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);


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-18 20:14             ` Alan Stern
@ 2005-06-20 15:52               ` Brian King
  2005-06-20 16:35                 ` Alan Stern
  2005-06-21 17:12               ` Mike Anderson
  1 sibling, 1 reply; 30+ messages in thread
From: Brian King @ 2005-06-20 15:52 UTC (permalink / raw)
  To: Alan Stern; +Cc: James Bottomley, Mike Anderson, SCSI development list

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

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-20 15:52               ` Brian King
@ 2005-06-20 16:35                 ` Alan Stern
  2005-06-20 17:31                   ` Patrick Mansfield
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-20 16:35 UTC (permalink / raw)
  To: Brian King; +Cc: James Bottomley, Mike Anderson, SCSI development list

On Mon, 20 Jun 2005, Brian King wrote:

> 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.

Okay, I can see you would run into problems if scsi_remove_device was 
called after the device structure had been initialized but before it was 
registered in sysfs.  (I'm not sure exactly how that could happen... but 
never mind.)

Here's a revised patch that does what Brian suggested.

Alan Stern


Index: usb-2.6/include/scsi/scsi_host.h
===================================================================
--- usb-2.6.orig/include/scsi/scsi_host.h
+++ usb-2.6/include/scsi/scsi_host.h
@@ -411,6 +411,7 @@ enum {
 	SHOST_DEL,
 	SHOST_CANCEL,
 	SHOST_RECOVERY,
+	SHOST_REMOVE,
 };
 
 struct Scsi_Host {
Index: usb-2.6/drivers/scsi/hosts.c
===================================================================
--- usb-2.6.orig/drivers/scsi/hosts.c
+++ usb-2.6/drivers/scsi/hosts.c
@@ -74,8 +74,13 @@ void scsi_host_cancel(struct Scsi_Host *
  **/
 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);
Index: usb-2.6/drivers/scsi/scsi_scan.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_scan.c
+++ usb-2.6/drivers/scsi/scsi_scan.c
@@ -843,8 +843,12 @@ static int scsi_probe_and_add_lun(struct
  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 @@ struct scsi_device *__scsi_add_device(st
 				      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 @@ void scsi_rescan_device(struct device *d
 }
 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 @@ void scsi_scan_target(struct device *par
 		return;
 
 
-	starget = scsi_alloc_target(parent, channel, id);
+	starget = scsi_alloc_target(&shost->shost_gendev, channel, id);
 
 	if (!starget)
 		return;
@@ -1304,6 +1294,32 @@ void scsi_scan_target(struct device *par
 
 	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 @@ static void scsi_scan_channel(struct Scs
 				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 @@ int scsi_scan_host_selected(struct Scsi_
 		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 @@ EXPORT_SYMBOL(scsi_scan_single_target);
 
 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 @@ void scsi_forget_host(struct Scsi_Host *
  */
 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 @@ struct scsi_device *scsi_get_host_dev(st
 		sdev->borken = 0;
 	}
 	put_device(&starget->dev);
+out:
+	up(&shost->scan_mutex);
 	return sdev;
 }
 EXPORT_SYMBOL(scsi_get_host_dev);
Index: usb-2.6/drivers/scsi/scsi_sysfs.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_sysfs.c
+++ usb-2.6/drivers/scsi/scsi_sysfs.c
@@ -591,7 +591,7 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 			error = attr_add(&sdev->sdev_gendev,
 					sdev->host->hostt->sdev_attrs[i]);
 			if (error) {
-				scsi_remove_device(sdev);
+				__scsi_remove_device(sdev);
 				goto out;
 			}
 		}
@@ -605,7 +605,7 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 							scsi_sysfs_sdev_attrs[i]);
 			error = device_create_file(&sdev->sdev_gendev, attr);
 			if (error) {
-				scsi_remove_device(sdev);
+				__scsi_remove_device(sdev);
 				goto out;
 			}
 		}
@@ -625,17 +625,10 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 	return error;
 }
 
-/**
- * scsi_remove_device - unregister a device from the scsi bus
- * @sdev:	scsi_device to unregister
- **/
-void scsi_remove_device(struct scsi_device *sdev)
+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 +637,17 @@ void scsi_remove_device(struct scsi_devi
 		sdev->host->hostt->slave_destroy(sdev);
 	transport_unregister_device(&sdev->sdev_gendev);
 	put_device(&sdev->sdev_gendev);
-out:
-	up(&shost->scan_mutex);
+}
+
+/**
+ * scsi_remove_device - unregister a device from the scsi bus
+ * @sdev:	scsi_device to unregister
+ **/
+void scsi_remove_device(struct scsi_device *sdev)
+{
+	down(&sdev->host->scan_mutex);
+	__scsi_remove_device(sdev);
+	up(&sdev->host->scan_mutex);
 }
 EXPORT_SYMBOL(scsi_remove_device);
 
@@ -653,17 +655,20 @@ void __scsi_remove_target(struct scsi_ta
 {
 	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);
Index: usb-2.6/drivers/scsi/scsi_priv.h
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_priv.h
+++ usb-2.6/drivers/scsi/scsi_priv.h
@@ -144,6 +144,7 @@ extern void scsi_sysfs_unregister(void);
 extern void scsi_sysfs_device_initialize(struct scsi_device *);
 extern int scsi_sysfs_target_initialize(struct scsi_device *);
 extern struct scsi_transport_template blank_transport_template;
+extern void __scsi_remove_device(struct scsi_device *);
 
 extern struct class sdev_class;
 extern struct bus_type scsi_bus_type;


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-20 16:35                 ` Alan Stern
@ 2005-06-20 17:31                   ` Patrick Mansfield
  2005-06-20 19:24                     ` Alan Stern
  0 siblings, 1 reply; 30+ messages in thread
From: Patrick Mansfield @ 2005-06-20 17:31 UTC (permalink / raw)
  To: Alan Stern
  Cc: Brian King, James Bottomley, Mike Anderson, SCSI development list

Hi Alan -

On Mon, Jun 20, 2005 at 12:35:02PM -0400, Alan Stern wrote:

> -/**
> - * 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 @@ void scsi_scan_target(struct device *par
>  		return;
>  
>  
> -	starget = scsi_alloc_target(parent, channel, id);
> +	starget = scsi_alloc_target(&shost->shost_gendev, channel, id);

For FC (and iSCSI), parent != &shost->shost_gendev. See user scanning
on FC thread / patches. 

[kernel scsi]$ grep scsi_scan_target scsi_transport_fc.c
	scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id,

And &rport->dev is not &shost->shost_gendev :-(

> +
> +/**
> + * 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);

And so parent still has to be passed down to __scsi_scan_target.

-- Patrick Mansfield

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-20 17:31                   ` Patrick Mansfield
@ 2005-06-20 19:24                     ` Alan Stern
  0 siblings, 0 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-20 19:24 UTC (permalink / raw)
  To: Patrick Mansfield
  Cc: Brian King, James Bottomley, Mike Anderson, SCSI development list

On Mon, 20 Jun 2005, Patrick Mansfield wrote:

> > -	starget = scsi_alloc_target(parent, channel, id);
> > +	starget = scsi_alloc_target(&shost->shost_gendev, channel, id);
> 
> For FC (and iSCSI), parent != &shost->shost_gendev. See user scanning
> on FC thread / patches. 
> 
> [kernel scsi]$ grep scsi_scan_target scsi_transport_fc.c
> 	scsi_scan_target(&rport->dev, rport->channel, rport->scsi_target_id,
> 
> And &rport->dev is not &shost->shost_gendev :-(

> And so parent still has to be passed down to __scsi_scan_target.

Okay.  It's a good thing you guys are able to find the bugs I've
inadvertently introduced.  Here's the next revision of the patch.  
Patrick, you may be able to suggest a better kerneldoc explanation for the
"parent" parameter in scsi_scan_target.  (The kerneldoc there now is way
out of date!)

Alan Stern



Index: usb-2.6/include/scsi/scsi_host.h
===================================================================
--- usb-2.6.orig/include/scsi/scsi_host.h
+++ usb-2.6/include/scsi/scsi_host.h
@@ -411,6 +411,7 @@ enum {
 	SHOST_DEL,
 	SHOST_CANCEL,
 	SHOST_RECOVERY,
+	SHOST_REMOVE,
 };
 
 struct Scsi_Host {
Index: usb-2.6/drivers/scsi/hosts.c
===================================================================
--- usb-2.6.orig/drivers/scsi/hosts.c
+++ usb-2.6/drivers/scsi/hosts.c
@@ -74,8 +74,13 @@ void scsi_host_cancel(struct Scsi_Host *
  **/
 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);
Index: usb-2.6/drivers/scsi/scsi_scan.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_scan.c
+++ usb-2.6/drivers/scsi/scsi_scan.c
@@ -843,8 +843,12 @@ static int scsi_probe_and_add_lun(struct
  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 @@ struct scsi_device *__scsi_add_device(st
 				      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,27 +1232,8 @@ void scsi_rescan_device(struct device *d
 }
 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 device *parent, unsigned int channel,
+		unsigned int id, unsigned int lun, int rescan)
 {
 	struct Scsi_Host *shost = dev_to_shost(parent);
 	int bflags = 0;
@@ -1256,9 +1247,7 @@ void scsi_scan_target(struct device *par
 		 */
 		return;
 
-
 	starget = scsi_alloc_target(parent, channel, id);
-
 	if (!starget)
 		return;
 
@@ -1304,6 +1293,32 @@ void scsi_scan_target(struct device *par
 
 	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(parent, 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 +1344,12 @@ static void scsi_scan_channel(struct Scs
 				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->shost_gendev, channel,
+					order_id, lun, rescan);
 		}
 	else
-		scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan);
+		__scsi_scan_target(&shost->shost_gendev, channel,
+				id, lun, rescan);
 }
 
 int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
@@ -1347,11 +1364,14 @@ int scsi_scan_host_selected(struct Scsi_
 		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 +1403,17 @@ EXPORT_SYMBOL(scsi_scan_single_target);
 
 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 +1440,16 @@ void scsi_forget_host(struct Scsi_Host *
  */
 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 +1457,8 @@ struct scsi_device *scsi_get_host_dev(st
 		sdev->borken = 0;
 	}
 	put_device(&starget->dev);
+out:
+	up(&shost->scan_mutex);
 	return sdev;
 }
 EXPORT_SYMBOL(scsi_get_host_dev);
Index: usb-2.6/drivers/scsi/scsi_sysfs.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_sysfs.c
+++ usb-2.6/drivers/scsi/scsi_sysfs.c
@@ -591,7 +591,7 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 			error = attr_add(&sdev->sdev_gendev,
 					sdev->host->hostt->sdev_attrs[i]);
 			if (error) {
-				scsi_remove_device(sdev);
+				__scsi_remove_device(sdev);
 				goto out;
 			}
 		}
@@ -605,7 +605,7 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 							scsi_sysfs_sdev_attrs[i]);
 			error = device_create_file(&sdev->sdev_gendev, attr);
 			if (error) {
-				scsi_remove_device(sdev);
+				__scsi_remove_device(sdev);
 				goto out;
 			}
 		}
@@ -625,17 +625,10 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 	return error;
 }
 
-/**
- * scsi_remove_device - unregister a device from the scsi bus
- * @sdev:	scsi_device to unregister
- **/
-void scsi_remove_device(struct scsi_device *sdev)
+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 +637,17 @@ void scsi_remove_device(struct scsi_devi
 		sdev->host->hostt->slave_destroy(sdev);
 	transport_unregister_device(&sdev->sdev_gendev);
 	put_device(&sdev->sdev_gendev);
-out:
-	up(&shost->scan_mutex);
+}
+
+/**
+ * scsi_remove_device - unregister a device from the scsi bus
+ * @sdev:	scsi_device to unregister
+ **/
+void scsi_remove_device(struct scsi_device *sdev)
+{
+	down(&sdev->host->scan_mutex);
+	__scsi_remove_device(sdev);
+	up(&sdev->host->scan_mutex);
 }
 EXPORT_SYMBOL(scsi_remove_device);
 
@@ -653,17 +655,20 @@ void __scsi_remove_target(struct scsi_ta
 {
 	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);
Index: usb-2.6/drivers/scsi/scsi_priv.h
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_priv.h
+++ usb-2.6/drivers/scsi/scsi_priv.h
@@ -144,6 +144,7 @@ extern void scsi_sysfs_unregister(void);
 extern void scsi_sysfs_device_initialize(struct scsi_device *);
 extern int scsi_sysfs_target_initialize(struct scsi_device *);
 extern struct scsi_transport_template blank_transport_template;
+extern void __scsi_remove_device(struct scsi_device *);
 
 extern struct class sdev_class;
 extern struct bus_type scsi_bus_type;



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-18 20:14             ` Alan Stern
  2005-06-20 15:52               ` Brian King
@ 2005-06-21 17:12               ` Mike Anderson
  2005-06-21 17:43                 ` Patrick Mansfield
  2005-06-21 20:04                 ` Alan Stern
  1 sibling, 2 replies; 30+ messages in thread
From: Mike Anderson @ 2005-06-21 17:12 UTC (permalink / raw)
  To: Alan Stern; +Cc: James Bottomley, SCSI development list

Alan Stern [stern@rowland.harvard.edu] wrote:
> James and Mike:
> 
> Here's my patch (as529), very lightly tested.  Among the changes:
> 

I ran the updated version of your patch on a scsi-misc-2.6 kernel with an
Emulex and Qlogic adapter. It does not find any devices of these adapters
during scan. I need to look into this.

> 
> 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,
>  };

Well it is a larger change we should consider migrating the shost state
model to one like the scsi device uses and also align on similar states
if possible.

>  
>  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);

Moving scsi_forget_host to after scsi_host_cancel will cause the sd cache
flush routines to fail. Also as previously mentioned I would like to
understand if we still need the cancel functionality. I believe there are
end case races with cancel and LLDD really should be able to return all
commands prior to calling scsi_remove_host or post (prior to the last
shost put).

The bit is set to SHOST_REMOVE then scsi_host_cancel is called which will
set the bit SHOST_CANCEL. Later on scan is stopped only if state is
SHOST_REMOVE. Is that what you wanted?

>  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;

It might be better to have a wrapper function so if we change the cases
where we would allow scanning we can change just one place. Also we might
cover more states if we reverse the logic on the check and look for the
case we allow scanning (see previous comment about cancel). This is what I
did in my previous patch.

> @@ -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);
>  }

Well it saves some some overhead we really should delete the parent
and let it handle cleanup of children. If we switch to using the driver
model lists the code would be easier to migrate.

-andmike
--
Michael Anderson
andmike@us.ibm.com


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  1 sibling, 1 reply; 30+ messages in thread
From: Patrick Mansfield @ 2005-06-21 17:43 UTC (permalink / raw)
  To: Alan Stern, James Bottomley, SCSI development list

On Tue, Jun 21, 2005 at 10:12:09AM -0700, Mike Anderson wrote:
> Alan Stern [stern@rowland.harvard.edu] wrote:
> > James and Mike:
> > 
> > Here's my patch (as529), very lightly tested.  Among the changes:
> > 
> 
> I ran the updated version of your patch on a scsi-misc-2.6 kernel with an
> Emulex and Qlogic adapter. It does not find any devices of these adapters
> during scan. I need to look into this.

Mike -

You probably need Alan's most recent patch that passes through
scsi_scan_target's parent argument.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 17:43                 ` Patrick Mansfield
@ 2005-06-21 19:24                   ` Mike Anderson
  0 siblings, 0 replies; 30+ messages in thread
From: Mike Anderson @ 2005-06-21 19:24 UTC (permalink / raw)
  To: Patrick Mansfield; +Cc: Alan Stern, James Bottomley, SCSI development list

Patrick Mansfield [patmans@us.ibm.com] wrote:
> Mike -
> 
> You probably need Alan's most recent patch that passes through
> scsi_scan_target's parent argument.

Yes, thanks I thought that I was running the third update, but I was
running the second one. Running with third patch update fixes my scan
issue.

-andmike
--
Michael Anderson
andmike@us.ibm.com


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 17:12               ` Mike Anderson
  2005-06-21 17:43                 ` Patrick Mansfield
@ 2005-06-21 20:04                 ` Alan Stern
  2005-06-21 20:10                   ` Christoph Hellwig
  2005-06-21 21:08                   ` Mike Anderson
  1 sibling, 2 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-21 20:04 UTC (permalink / raw)
  To: Mike Anderson; +Cc: James Bottomley, SCSI development list

On Tue, 21 Jun 2005, Mike Anderson wrote:

> > --- 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,
> >  };
> 
> Well it is a larger change we should consider migrating the shost state
> model to one like the scsi device uses and also align on similar states
> if possible.

For now I just wanted to make as simple a patch as possible.  I have no 
objection to making a larger change like the one you suggest.

> >  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);
> 
> Moving scsi_forget_host to after scsi_host_cancel will cause the sd cache
> flush routines to fail.

This objection runs up against an issue we discussed some time ago.  
Should the intended meaning of scsi_remove_host be simply that the kernel
needs to stop using the HBA reasonably soon?  In that case you are right.  
Or should the intended meaning be that the HBA is actually gone
(hot-unplugged) and all further attempts to use it will fail?  In that
case it doesn't matter.  The best ways to resolve this issue may be to
have a separate scsi_host_gone routine or to add an extra argument to
scsi_remove_host.

At any rate, we currently face the bigger problem that if scsi_forget_host
comes first then scsi_host_cancel doesn't work right.  Outstanding
commands do not get cancelled.  I didn't try to trace down the exact
reason for this; I just did the simplest thing to make it work.

>  Also as previously mentioned I would like to
> understand if we still need the cancel functionality. I believe there are
> end case races with cancel and LLDD really should be able to return all
> commands prior to calling scsi_remove_host or post (prior to the last
> shost put).

I rather agree in principle that the cancel functionality isn't really
needed.  Removing it will require some tricky changes to the LLDDs,
however.  And the changes will all have to be made at once.  If some LLDDs 
are changed and others aren't, then (depending on whether scsi_host_cancel 
has been removed) either the changed ones will oops as they try to cancel 
an already-cancelled command or the unchanged ones will oops as 
uncancelled commands time out.  I've seen both kinds of errors in working 
with usb-storage.

> The bit is set to SHOST_REMOVE then scsi_host_cancel is called which will
> set the bit SHOST_CANCEL. Later on scan is stopped only if state is
> SHOST_REMOVE. Is that what you wanted?

Remember, at the moment the state is a bit-vector.  It can have both
SHOST_CANCEL and SHOST_REMOVE set at the same time.  That is what I
wanted.  Changing to a host state model will of course require you to do
things differently.

> >  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;
> 
> It might be better to have a wrapper function so if we change the cases
> where we would allow scanning we can change just one place. Also we might
> cover more states if we reverse the logic on the check and look for the
> case we allow scanning (see previous comment about cancel). This is what I
> did in my previous patch.

That's okay with me.  So long as all the scanning pathways are covered and 
all scanning is stopped before scsi_forget_host runs, you can feel free to 
improve the implementation details.

> > @@ -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);
> >  }
> 
> Well it saves some some overhead we really should delete the parent
> and let it handle cleanup of children. If we switch to using the driver
> model lists the code would be easier to migrate.

I didn't do it that way because it can't be made to work correctly with
the current code -- there's no way to know whether a target has already
been removed.  Adding a target state model would make your approach
feasible, but James has said that targets don't merit a state model.

Driver model klists also have their disadvantages.  If you delete a node 
from a klist asynchronously then you cannot re-use it; it must be allowed 
to deallocate itself when the refcount goes to 0.  And it's not possible 
to remove nodes from a klist synchronously while traversing the klist.

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:04                 ` Alan Stern
@ 2005-06-21 20:10                   ` Christoph Hellwig
  2005-06-21 20:33                     ` Alan Stern
  2005-06-21 21:08                   ` Mike Anderson
  1 sibling, 1 reply; 30+ messages in thread
From: Christoph Hellwig @ 2005-06-21 20:10 UTC (permalink / raw)
  To: Alan Stern; +Cc: Mike Anderson, James Bottomley, SCSI development list

On Tue, Jun 21, 2005 at 04:04:06PM -0400, Alan Stern wrote:
> This objection runs up against an issue we discussed some time ago.  
> Should the intended meaning of scsi_remove_host be simply that the kernel
> needs to stop using the HBA reasonably soon?  In that case you are right.  
> Or should the intended meaning be that the HBA is actually gone
> (hot-unplugged) and all further attempts to use it will fail?  In that
> case it doesn't matter.  The best ways to resolve this issue may be to
> have a separate scsi_host_gone routine or to add an extra argument to
> scsi_remove_host.

It must mean both because we don't know whether a hot unplug happened or
not.  The ->remove callbacks don't tell us.


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:10                   ` Christoph Hellwig
@ 2005-06-21 20:33                     ` Alan Stern
  2005-06-21 20:58                       ` Mike Anderson
  2005-06-22 13:36                       ` Luben Tuikov
  0 siblings, 2 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-21 20:33 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Mike Anderson, James Bottomley, SCSI development list

On Tue, 21 Jun 2005, Christoph Hellwig wrote:

> On Tue, Jun 21, 2005 at 04:04:06PM -0400, Alan Stern wrote:
> > This objection runs up against an issue we discussed some time ago.  
> > Should the intended meaning of scsi_remove_host be simply that the kernel
> > needs to stop using the HBA reasonably soon?  In that case you are right.  
> > Or should the intended meaning be that the HBA is actually gone
> > (hot-unplugged) and all further attempts to use it will fail?  In that
> > case it doesn't matter.  The best ways to resolve this issue may be to
> > have a separate scsi_host_gone routine or to add an extra argument to
> > scsi_remove_host.
> 
> It must mean both because we don't know whether a hot unplug happened or
> not.  The ->remove callbacks don't tell us.

I would describe it differently: Since you don't know whether a hot-unplug 
occurred, you might as well assume it did not.  There's no harm in this, 
because if the HBA really was unplugged then it doesn't matter what you 
do; everything will fail.

But those sd flush-cache commands are a problem.  Presumably you want to 
send them _after_ all the outstanding commands have finished or been 
cancelled.  What's the right way to allow those commands while rejecting 
all others?

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  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
  1 sibling, 2 replies; 30+ messages in thread
From: Mike Anderson @ 2005-06-21 20:58 UTC (permalink / raw)
  To: Alan Stern; +Cc: Christoph Hellwig, James Bottomley, SCSI development list

Alan Stern [stern@rowland.harvard.edu] wrote:
> On Tue, 21 Jun 2005, Christoph Hellwig wrote:
> 
> > On Tue, Jun 21, 2005 at 04:04:06PM -0400, Alan Stern wrote:
> > > This objection runs up against an issue we discussed some time ago.  
> > > Should the intended meaning of scsi_remove_host be simply that the kernel
> > > needs to stop using the HBA reasonably soon?  In that case you are right.  
> > > Or should the intended meaning be that the HBA is actually gone
> > > (hot-unplugged) and all further attempts to use it will fail?  In that
> > > case it doesn't matter.  The best ways to resolve this issue may be to
> > > have a separate scsi_host_gone routine or to add an extra argument to
> > > scsi_remove_host.
> > 
> > It must mean both because we don't know whether a hot unplug happened or
> > not.  The ->remove callbacks don't tell us.
> 
> I would describe it differently: Since you don't know whether a hot-unplug 
> occurred, you might as well assume it did not.  There's no harm in this, 
> because if the HBA really was unplugged then it doesn't matter what you 
> do; everything will fail.
> 

I have asked this multiple times, but I will again. If the hba knows to
fail everything by some internal state in the LLDD why does it not also
know to flush all commands back to the scsi mid layer in this unplugged
state so we do not have to deal with canceling them from the mid-layer. 

I think it has been stated many times if commands always go in through
queuecommand and always return through scsi_done it makes the chances of
errors a lot less.

> But those sd flush-cache commands are a problem.  Presumably you want to 
> send them _after_ all the outstanding commands have finished or been 
> cancelled.  What's the right way to allow those commands while rejecting 
> all others?

We have some notion of this already if you look in scsi_prep_fn (i.e.,
specials_only), but this is based on sdev state not shost. Checking for a
specials_only flag in scsi_dispatch may not be to clean. This may be why
we should look at a shost state model update and align sdev and shost to
clearly state what will happen to command.s

-andmike
--
Michael Anderson
andmike@us.ibm.com


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:04                 ` Alan Stern
  2005-06-21 20:10                   ` Christoph Hellwig
@ 2005-06-21 21:08                   ` Mike Anderson
  2005-06-21 21:37                     ` Alan Stern
  1 sibling, 1 reply; 30+ messages in thread
From: Mike Anderson @ 2005-06-21 21:08 UTC (permalink / raw)
  To: Alan Stern; +Cc: James Bottomley, SCSI development list

Alan Stern [stern@rowland.harvard.edu] wrote:
> > Moving scsi_forget_host to after scsi_host_cancel will cause the sd cache
> > flush routines to fail.
> 
> This objection runs up against an issue we discussed some time ago.  
> Should the intended meaning of scsi_remove_host be simply that the kernel
> needs to stop using the HBA reasonably soon?  In that case you are right.  
> Or should the intended meaning be that the HBA is actually gone
> (hot-unplugged) and all further attempts to use it will fail?  In that
> case it doesn't matter.  The best ways to resolve this issue may be to
> have a separate scsi_host_gone routine or to add an extra argument to
> scsi_remove_host.
> 

I believe this was discussed in the thread below or possible another (we
have had this conversation a number of times), and the action then was not
to create another interface.
http://marc.theaimsgroup.com/?t=108701426000002&r=1&w=2


> I rather agree in principle that the cancel functionality isn't really
> needed.  Removing it will require some tricky changes to the LLDDs,
> however.  And the changes will all have to be made at once.  If some LLDDs 
> are changed and others aren't, then (depending on whether scsi_host_cancel 
> has been removed) either the changed ones will oops as they try to cancel 
> an already-cancelled command or the unchanged ones will oops as 
> uncancelled commands time out.  I've seen both kinds of errors in working 
> with usb-storage.

We really should have scsi_times_out check the state of the host and
possible the device to stop calling into the host when removes are in
progress.

> 
> > The bit is set to SHOST_REMOVE then scsi_host_cancel is called which will
> > set the bit SHOST_CANCEL. Later on scan is stopped only if state is
> > SHOST_REMOVE. Is that what you wanted?
> 
> Remember, at the moment the state is a bit-vector.  It can have both
> SHOST_CANCEL and SHOST_REMOVE set at the same time.  That is what I
> wanted.  Changing to a host state model will of course require you to do
> things differently.

Yes, I guess I should know that :-(. I had my head in the new host state
model. Yes changing to the host state model did require some different
checks, but the concept is the same.

> 
> > >  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;
> > 
> > It might be better to have a wrapper function so if we change the cases
> > where we would allow scanning we can change just one place. Also we might
> > cover more states if we reverse the logic on the check and look for the
> > case we allow scanning (see previous comment about cancel). This is what I
> > did in my previous patch.
> 
> That's okay with me.  So long as all the scanning pathways are covered and 
> all scanning is stopped before scsi_forget_host runs, you can feel free to 
> improve the implementation details.
> 

I already did this in the previous patch series I posted, but received not
comments so I guess there is no need to wrap it.

> 
> I didn't do it that way because it can't be made to work correctly with
> the current code -- there's no way to know whether a target has already
> been removed.  Adding a target state model would make your approach
> feasible, but James has said that targets don't merit a state model.
> 
> Driver model klists also have their disadvantages.  If you delete a node 
> from a klist asynchronously then you cannot re-use it; it must be allowed 
> to deallocate itself when the refcount goes to 0.  And it's not possible 
> to remove nodes from a klist synchronously while traversing the klist.

Is this still true for klists. I thought the locking updates and the
addition of a klist_iter was to fix this issue though I have not spent
much time looking through the code since the changes.

-andmike
--
Michael Anderson
andmike@us.ibm.com


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:58                       ` Mike Anderson
@ 2005-06-21 21:22                         ` Alan Stern
  2005-06-22 13:44                         ` Luben Tuikov
  1 sibling, 0 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-21 21:22 UTC (permalink / raw)
  To: Mike Anderson; +Cc: Christoph Hellwig, James Bottomley, SCSI development list

On Tue, 21 Jun 2005, Mike Anderson wrote:

> I have asked this multiple times, but I will again. If the hba knows to
> fail everything by some internal state in the LLDD why does it not also
> know to flush all commands back to the scsi mid layer in this unplugged
> state so we do not have to deal with canceling them from the mid-layer. 

With usb-storage, part of it is my fault.  It never occurred to me that we
should fail all outstanding commands prior to calling scsi_remove_host,
since I expected the midlayer to take care of things.  I'll try changing
usb-storage and see if it works with scsi_host_cancel coming after
scsi_forget_host (or even removed entirely!).

> I think it has been stated many times if commands always go in through
> queuecommand and always return through scsi_done it makes the chances of
> errors a lot less.

I agree.  The current state must have arisen for historical reasons.  

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 21:08                   ` Mike Anderson
@ 2005-06-21 21:37                     ` Alan Stern
  0 siblings, 0 replies; 30+ messages in thread
From: Alan Stern @ 2005-06-21 21:37 UTC (permalink / raw)
  To: Mike Anderson; +Cc: James Bottomley, SCSI development list

On Tue, 21 Jun 2005, Mike Anderson wrote:

> > I rather agree in principle that the cancel functionality isn't really
> > needed.  Removing it will require some tricky changes to the LLDDs,
> > however.  And the changes will all have to be made at once.  If some LLDDs 
> > are changed and others aren't, then (depending on whether scsi_host_cancel 
> > has been removed) either the changed ones will oops as they try to cancel 
> > an already-cancelled command or the unchanged ones will oops as 
> > uncancelled commands time out.  I've seen both kinds of errors in working 
> > with usb-storage.
> 
> We really should have scsi_times_out check the state of the host and
> possible the device to stop calling into the host when removes are in
> progress.

Or when removes have been completed!

> > That's okay with me.  So long as all the scanning pathways are covered and 
> > all scanning is stopped before scsi_forget_host runs, you can feel free to 
> > improve the implementation details.
> > 
> 
> I already did this in the previous patch series I posted, but received not
> comments so I guess there is no need to wrap it.

I haven't had a chance to take a detailed look at your changes.  In fact I
wasn't even aware of them until yesterday (because I'm not subscribed to
linux-scsi).  I'll try them out when there's time.

> > Driver model klists also have their disadvantages.  If you delete a node 
> > from a klist asynchronously then you cannot re-use it; it must be allowed 
> > to deallocate itself when the refcount goes to 0.  And it's not possible 
> > to remove nodes from a klist synchronously while traversing the klist.
> 
> Is this still true for klists. I thought the locking updates and the
> addition of a klist_iter was to fix this issue though I have not spent
> much time looking through the code since the changes.

klist_iter fixes the problem of one thread deleting nodes while another
thread is traversing the klist.  However the deleted nodes aren't marked
in any way, so the traversing thread might not realize they had been
deleted.  Also, since the klist_iter itself holds a reference to the
deleted node you can't do a synchronous remove -- it will deadlock.

This ended up causing difficulties for the driver_detach routine in the 
driver model core.  I had to rewrite it, avoiding the use of a klist_iter.

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:33                     ` Alan Stern
  2005-06-21 20:58                       ` Mike Anderson
@ 2005-06-22 13:36                       ` Luben Tuikov
  2005-06-22 15:12                         ` Alan Stern
  1 sibling, 1 reply; 30+ messages in thread
From: Luben Tuikov @ 2005-06-22 13:36 UTC (permalink / raw)
  To: Alan Stern
  Cc: Christoph Hellwig, Mike Anderson, James Bottomley,
	SCSI development list

On 06/21/05 16:33, Alan Stern wrote:
> On Tue, 21 Jun 2005, Christoph Hellwig wrote:
> 
> 
>>On Tue, Jun 21, 2005 at 04:04:06PM -0400, Alan Stern wrote:
>>
>>>This objection runs up against an issue we discussed some time ago.  
>>>Should the intended meaning of scsi_remove_host be simply that the kernel
>>>needs to stop using the HBA reasonably soon?  In that case you are right.  
>>>Or should the intended meaning be that the HBA is actually gone
>>>(hot-unplugged) and all further attempts to use it will fail?  In that

For the success of the state machine, states must be discrete.
scsi_remove_host() cannot mean "reasonably soon".  If it _has_ to mean
that, then _add_ another state.

If one wants to consolidate the hot-unplugging with notified unplug,
one might as well aim for the supercase "hot-unplug".  Its implementation
would surely satisfy notified unplug.

>>>case it doesn't matter.  The best ways to resolve this issue may be to
>>>have a separate scsi_host_gone routine or to add an extra argument to
>>>scsi_remove_host.
>>
>>It must mean both because we don't know whether a hot unplug happened or
>>not.  The ->remove callbacks don't tell us.
> 
> 
> I would describe it differently: Since you don't know whether a hot-unplug 
> occurred, you might as well assume it did not.  There's no harm in this, 
> because if the HBA really was unplugged then it doesn't matter what you 
> do; everything will fail.

Conclusion is right, premise is not.  Assume that there was hot-unplug.
It is the supercase which absolves notified unplug.

(Hot unplug of targets is a bit different in that both the hw and the
LLDD knows of the event and some things need not be waited to time out...)

> But those sd flush-cache commands are a problem.  Presumably you want to 
> send them _after_ all the outstanding commands have finished or been 
> cancelled.  What's the right way to allow those commands while rejecting 
> all others?

I don't know.  But to be honest, I'd imagine sending flush-cache as soon
as possible without waiting for anything to cancel or timeout or return.
That is, you want to have the best chance.

The lower layers should make sure that if the device is gone, that is
they _know_ about the event, an error is returned.

So that the treatment of flush-cache is no different than already queued
commands.

So in effect, you (usb-storage) know about a condition (say device is gone)
you should act on it right in queuecommand() and not accept the command,
as opposed to hoping for the midlayer to turn around and preempt those commands.

	Luben
P.S. USB is perfect, since you're notified on unplug.



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-21 20:58                       ` Mike Anderson
  2005-06-21 21:22                         ` Alan Stern
@ 2005-06-22 13:44                         ` Luben Tuikov
  1 sibling, 0 replies; 30+ messages in thread
From: Luben Tuikov @ 2005-06-22 13:44 UTC (permalink / raw)
  To: Mike Anderson
  Cc: Alan Stern, Christoph Hellwig, James Bottomley,
	SCSI development list

On 06/21/05 16:58, Mike Anderson wrote:
> Alan Stern [stern@rowland.harvard.edu] wrote:
>>I would describe it differently: Since you don't know whether a hot-unplug 
>>occurred, you might as well assume it did not.  There's no harm in this, 
>>because if the HBA really was unplugged then it doesn't matter what you 
>>do; everything will fail.
>>
> 
> 
> I have asked this multiple times, but I will again. If the hba knows to
> fail everything by some internal state in the LLDD why does it not also
> know to flush all commands back to the scsi mid layer in this unplugged
> state so we do not have to deal with canceling them from the mid-layer. 

This is a good question.  The way I see it: follow the path of the event
and at each logical point act on it:
	device-->SDS-->HA-->LLDD-->SCSI midlayer-->etc.
That is, at each point of the unplug event's path, act on that
unplug event, meaning HA first, then LLDD, then SCSI midlayer.

The HA and LLDD would work together, but if they _know_ about "device gone"
they should stop accepting commands to that device and return them to
the midlayer, along with saying hey, this device is gone, along with
returning all previously queued but infinished commands and rejecting new.

The other way around, a notified unplug, the event travels the same path
but in opposite direction.  So the same actions should follow but
in opposite direction: SCSI midlayer first, LLDD, HA, finally device,
just as the event from userspace travels (remove device).

(We have to go to the device eventualy in case there any reservations on
the LU.)
 
> I think it has been stated many times if commands always go in through
> queuecommand and always return through scsi_done it makes the chances of
> errors a lot less.

Completely agree!

>>But those sd flush-cache commands are a problem.  Presumably you want to 
>>send them _after_ all the outstanding commands have finished or been 
>>cancelled.  What's the right way to allow those commands while rejecting 
>>all others?
> 
> 
> We have some notion of this already if you look in scsi_prep_fn (i.e.,
> specials_only), but this is based on sdev state not shost. Checking for a
> specials_only flag in scsi_dispatch may not be to clean. This may be why
> we should look at a shost state model update and align sdev and shost to
> clearly state what will happen to command.s

Check host state first, then target then lu...

	Luben


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-22 13:36                       ` Luben Tuikov
@ 2005-06-22 15:12                         ` Alan Stern
  2005-06-22 15:46                           ` Luben Tuikov
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-22 15:12 UTC (permalink / raw)
  To: Luben Tuikov
  Cc: Christoph Hellwig, Mike Anderson, James Bottomley,
	SCSI development list

On Wed, 22 Jun 2005, Luben Tuikov wrote:

> For the success of the state machine, states must be discrete.
> scsi_remove_host() cannot mean "reasonably soon".  If it _has_ to mean
> that, then _add_ another state.
> 
> If one wants to consolidate the hot-unplugging with notified unplug,
> one might as well aim for the supercase "hot-unplug".  Its implementation
> would surely satisfy notified unplug.

Certainly.  In the UNPLUG_NOTIFY state, only commands like SYNCHRONIZE 
CACHE should be allowed.

> The lower layers should make sure that if the device is gone, that is
> they _know_ about the event, an error is returned.
> 
> So that the treatment of flush-cache is no different than already queued
> commands.

For the unplug case you are right.  For the notify case it's a little 
tricky because of the interaction with command cancellation (if commands 
need to be cancelled) and the need to allow the cache flush without 
allowing other commands.

> So in effect, you (usb-storage) know about a condition (say device is gone)
> you should act on it right in queuecommand() and not accept the command,
> as opposed to hoping for the midlayer to turn around and preempt those commands.
> 
> 	Luben
> P.S. USB is perfect, since you're notified on unplug.

Yes.  But in fact usb-storage has a small loophole.  Before calling
scsi_remove_host, we set a flag that causes the queuecommand routine to
reject all new commands and any currently-executing command to fail-fast.  
However there is a small window between the time a command is accepted and
the time it starts to execute (because execution takes place in a
separate kernel thread).  If a command has been accepted and has not
started to execute, then usb-storage doesn't cancel it.  We've been
relying on the midlayer to cancel it for us.

There's no reason usb-storage can't cancel the command before calling
scsi_remove_host.  I tried out a patch to do just that, and it worked
fine.  (Waiting until after scsi_remove_host to cancel the command doesn't
work, for the obvious reason that the midlayer may have already cancelled
it.)  I'll submit the patch; it should solve this particular problem for
usb-storage.  I don't know how any other hot-unpluggable LLDDs will handle
cancellation during unplug.

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-22 15:12                         ` Alan Stern
@ 2005-06-22 15:46                           ` Luben Tuikov
  2005-06-22 16:16                             ` Alan Stern
  0 siblings, 1 reply; 30+ messages in thread
From: Luben Tuikov @ 2005-06-22 15:46 UTC (permalink / raw)
  To: Alan Stern
  Cc: Christoph Hellwig, Mike Anderson, James Bottomley,
	SCSI development list

On 06/22/05 11:12, Alan Stern wrote:
> 
> For the unplug case you are right.  For the notify case it's a little 
> tricky because of the interaction with command cancellation (if commands 
> need to be cancelled) and the need to allow the cache flush without 
> allowing other commands.

The notify event can "travel" right behind flush-cache and "close doors"
behind.  (since it travels from the application client to the device)

> Yes.  But in fact usb-storage has a small loophole.  Before calling
> scsi_remove_host, we set a flag that causes the queuecommand routine to
> reject all new commands and any currently-executing command to fail-fast.  
> However there is a small window between the time a command is accepted and
> the time it starts to execute (because execution takes place in a
> separate kernel thread).  If a command has been accepted and has not
> started to execute, then usb-storage doesn't cancel it.  We've been
> relying on the midlayer to cancel it for us.

Sounds like a race to me.  You should always be able to
identify/find a command at any point after it has been submitted
by queuecommand().

> There's no reason usb-storage can't cancel the command before calling
> scsi_remove_host.  I tried out a patch to do just that, and it worked
> fine.  (Waiting until after scsi_remove_host to cancel the command doesn't
> work, for the obvious reason that the midlayer may have already cancelled
> it.)  I'll submit the patch; it should solve this particular problem for

Yes indeed: the proper order is:
	* lock the front door,
	* empty all rooms,
	* call scsi_remove_host().

This is on _hot-unplug_ since the event came from the SDS.
In the event of a notified unplug, the event would be known to
SCSI Core _before_ it is known to the LLDD, so SCSI Core would
lock the "fence gate", send flush-cache (which would seem as any
other command to you), then wait for that to return (which would
mean no other commands are pending, since this was the last command
to be sent after locking the "fence gate") and then it would
call scsi_remove_host() or whatever appropriate.

> usb-storage.  I don't know how any other hot-unpluggable LLDDs will handle
> cancellation during unplug.

In case the LLDD is implemented properly, just doing nothing should suffice. ;-)

	Luben



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-22 15:46                           ` Luben Tuikov
@ 2005-06-22 16:16                             ` Alan Stern
  2005-06-22 16:53                               ` Luben Tuikov
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Stern @ 2005-06-22 16:16 UTC (permalink / raw)
  To: Luben Tuikov
  Cc: Christoph Hellwig, Mike Anderson, James Bottomley,
	SCSI development list

On Wed, 22 Jun 2005, Luben Tuikov wrote:

> The notify event can "travel" right behind flush-cache and "close doors"
> behind.  (since it travels from the application client to the device)

Are you sure about that?  One way to generate such a notify event is to
rmmod the LLDD.  The event is then generated by the LLDD and it travels up
to the midlayer, by way of scsi_remove_host.  You seem to be saying that
there should be a separate API for the LLDD to notify the midlayer that it
is about to be unloaded.

> Yes indeed: the proper order is:
> 	* lock the front door,
> 	* empty all rooms,
> 	* call scsi_remove_host().
> 
> This is on _hot-unplug_ since the event came from the SDS.
> In the event of a notified unplug, the event would be known to
> SCSI Core _before_ it is known to the LLDD, so SCSI Core would
> lock the "fence gate", send flush-cache (which would seem as any
> other command to you), then wait for that to return (which would
> mean no other commands are pending, since this was the last command
> to be sent after locking the "fence gate") and then it would
> call scsi_remove_host() or whatever appropriate.

Currently there is nothing like your "fence gate", although there should 
be.

> > usb-storage.  I don't know how any other hot-unpluggable LLDDs will handle
> > cancellation during unplug.
> 
> In case the LLDD is implemented properly, just doing nothing should suffice. ;-)

So should the midlayer no longer try to cancel outstanding commands when a 
host is removed?  Can it rely on the LLDDs to do so?

Alan Stern


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Questions about scsi_target_reap and starget/sdev lifecyle
  2005-06-22 16:16                             ` Alan Stern
@ 2005-06-22 16:53                               ` Luben Tuikov
  0 siblings, 0 replies; 30+ messages in thread
From: Luben Tuikov @ 2005-06-22 16:53 UTC (permalink / raw)
  To: Alan Stern
  Cc: Christoph Hellwig, Mike Anderson, James Bottomley,
	SCSI development list

On 06/22/05 12:16, Alan Stern wrote:
> On Wed, 22 Jun 2005, Luben Tuikov wrote:
> 
> 
>>The notify event can "travel" right behind flush-cache and "close doors"
>>behind.  (since it travels from the application client to the device)
> 
> 
> Are you sure about that?  One way to generate such a notify event is to
> rmmod the LLDD.  The event is then generated by the LLDD and it travels up
> to the midlayer, by way of scsi_remove_host.  You seem to be saying that
> there should be a separate API for the LLDD to notify the midlayer that it
> is about to be unloaded.

Alan, I'd consider this to be "hot-unplug" event (as you describe it).

(In which case the other order of rules apply (set flag for queuecommand(),
empty queues, call scsi_remove_host()).

> So should the midlayer no longer try to cancel outstanding commands when a 
> host is removed?  Can it rely on the LLDDs to do so?

Well, let's think about it.  If the LLDD has emptied its queues, and
the LLDD doesn't own any commands, then the midlayer would have
nothing to cancel (midlayer's pending queue is empty).

Nevertheless, the midlayer should inspect its pending queue to see
if it needs to preempt any outstanding commands, in case the LLDD
didn't return them.

	Luben

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2005-06-22 16:53 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox