Re: [RFC 8/9] UAS: Cancel pending URBs and free streams in pre-reset.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 7 Dec 2011, Sarah Sharp wrote:

> > >  I feel like I'm going
> > > in mental circles, and missing information about how the SCSI layer
> > > works.
> > 
> > What information?
> 
> I'm trying to figure out which functions in the UAS driver are entered
> with the SCSI host lock taken by the SCSI core.

In reality, none of them.

>  You've already said
> that the function to queue commands will have the SCSI host lock held.

Look more closely.  There's uas_queuecommand_lck(), which does the real
work, and uas_queuecommand(), created by the DEF_SCSI_QCMD macro.  
That one just grabs the host lock, assigns a serial number to the
command, calls the first routine, and releases the lock.  You can
bypass the macro entirely if you want.

> There's an interesting note in include/scsi/scsi_host.h:
> 
> 	/*
> 	 * This is an error handling strategy routine.  You don't need to
> 	 * define one of these if you don't want to - there is a default
> 	 * routine that is present that should work in most cases.  For those
> 	 * driver authors that have the inclination and ability to write their
> 	 * own strategy routine, this is where it is specified.  Note - the
> 	 * strategy routine is *ALWAYS* run in the context of the kernel eh
> 	 * thread.  Thus you are guaranteed to *NOT* be in an interrupt
> 	 * handler when you execute this, and you are also guaranteed to
> 	 * *NOT* have any other commands being queued while you are in the
> 	 * strategy routine. When you return from this function, operations
> 	 * return to normal.
> 	 *
> 	 * See scsi_error.c scsi_unjam_host for additional comments about
> 	 * what this function should and should not be attempting to do.
> 	 *
> 	 * Status: REQUIRED	(at least one of them)
> 	 */
> 	int (* eh_abort_handler)(struct scsi_cmnd *);
> 	int (* eh_device_reset_handler)(struct scsi_cmnd *);
> 	int (* eh_target_reset_handler)(struct scsi_cmnd *);
> 	int (* eh_bus_reset_handler)(struct scsi_cmnd *);
> 	int (* eh_host_reset_handler)(struct scsi_cmnd *);
> 
> Does that note mean that the SCSI host lock is held when
> eh_abort_handler and eh_host_reset_handler are called?

No.  Those methods are called in process context with no spinlocks
held.

> The note also seems to imply that since there's only one thread that's
> calling into the error handling methods, only one of the methods will be
> called at a time.

No, that's not right.  There is only one error handler thread, but 
other threads can call those methods.  For example, a user program can 
ask the SCSI-generic driver to do a device, target, bus, or host reset.

>  If the SCSI lock is held when those methods are
> called, and only one of them is called at once, then that loosens the
> constraints that I have to place in the UAS driver.
> 
> I asked James Bottomley on IRC about it yesterday, but he didn't answer.

Unfortunately he often doesn't respond quickly (or at all) to technical
questions.  It can be frustrating...

> Your suggestion about how to synchronize the resetting flag involved
> using the SCSI host lock.  But if the lock is taken by the SCSI core
> before it calls eh_host_reset_handler and then another driver attempts
> to reset the device and pre-reset tries to take the lock to sync the
> resetting flag, then we have a deadlock.

Nope, the lock isn't held.  And of course, you don't have to have to 
use the host lock if you don't want to.  Just put a private lock in 
your devinfo structure.  You already have a lock for the workqueue 
anyway.

(BTW, why is the devinfo structure allocated separately?  Is there any 
reason not to embed it in the Scsi_Host structure, the way usb-storage 
does?)

> > >  It would be best if you wrote this patch yourself.
> > > 
> > > There's nothing strictly speaking wrong with the original patchset,
> > > aside from the race condition between the abort handler and
> > > uas_stat_cmplt, is there?
> > 
> > I don't know; I didn't look at the patches in any detail.
> 
> Ok, you didn't look at the whole patchset.  Did you look at this patch
> in detail?

Probably not enough detail.  The description didn't seem right, so 
there wasn't much point poring through the code.

On the whole it appears more or less okay.  There are a few small
things.  For example, the srcu_struct should be embedded in devinfo
rather than allocated dynamically.

> What will the SCSI layer do if it tries to cancel a SCSI command, the
> abort fails, but then the scsi_done function gets called with a
> successful status?  I don't know, because I'm not a SCSI expert.

Once a command times out, scsi_done doesn't do anything.  I think you
don't want the abort routine to return failure when a reset is in
progress -- if it does, the SCSI error handler will start its own
device reset.  (In fact, it looks like usb-storage returns the wrong
value here.)  Just wait until the command completes, one way or
another, and return success.  Or maybe return FAST_IO_FAIL, which 
doesn't appear to be documented anywhere but is used in the SCSI error 
handler.

> From a UAS perspective, I think we're fine.  If the completion handler
> is running at the same time as the abort handler, we'll issue an ABORT
> TASK command for that tag.  If the device has completed the tag, it will
> just respond with an error status to the ABORT TASK and continue on its
> way.  We're in abort handler and that note says we won't see new
> commands issued while we're in an error handling function.  That means
> even if the tag completes and the UAS driver marks it as being free for
> new commands, we won't issue a new command with that tag until the abort
> handler returns.  So the UAS driver can't ever accidentally abort a tag
> it didn't mean to abort.
> 
> If the ABORT TASK does return successfully, then we know the completion
> handler for the status URB is never going to run, and we just kill them.

(If you kill an URB, its completion handler does nevertheless run.)

The only thing to watch out for would be if the completion handler gets
rid of some data structure while the abort handler is still using it.  
If that's not an issue in uas then there's nothing to worry about.

> (BTW, I realized how awful it sounds to talk about killing URBs in
> public when I was trying to explain this issue with my husband in a
> restaurant last night.  I said "I just don't care about them, so we'll
> kill them off" a little too loud.)

Did you get any funny looks from other people?  :-)

> > What you should do is break the problem into two pieces.  Work on 
> > aborting first, and then add resetting.  I suspect aborting may be more 
> > complex then resetting.
> > 
> > For aborting, the races to worry about are abort/resubmit and
> > abort/complete.  As mentioned above, abort/resubmit could be handled
> > via SRCU but abort/complete may need some sort of lock.

In short, the abort handler probably shouldn't return until the command
has completed, either successfully or unsuccessfully.  (My personal
impression is that the design of the SCSI and block layers is far too
baroque; parts of it -- like this one -- ought to be simplified.)

> Is there any problem with using two SRCU reader locks in the same
> driver?

No.

>  Can a function take an SRCU reader lock (say for the resetting
> flag) and also synchronize the readers for another flag (say the command
> abort flag)?

Yes.  But it mustn't call synchronize_srcu() if it's holding the 
corresponding read lock, obviously.

> > For resetting, the races to worry about are reset/queuecommand and
> > waiting for all outstanding commands to complete.  The queuecommand
> > race could be handled with either regular RCU or a spinlock.  Either a 
> > private lock or the host lock (you know, uas_queuecommand doesn't 
> > have to be called with the host lock held if you don't want it).
> 
> Do you mean I can ask the SCSI core to not hold the host lock when it
> calls into the queue function?  Or is that always true and the driver
> needs to grab the scsi host lock?

See above.

> > Waiting for the outstanding commands to complete can be done without 
> > locking if you don't mind wasting a little time in pre_reset.  All you 
> > have to do is test an atomic commands_in_flight counter, and if it is 
> > non-zero then sleep for a few tens of milliseconds and try again.
> 
> There's also a race condition between the pre-reset function and the
> abort handler, since the abort handler can submit URBs to issue the
> ABORT TASK command.
> 
> Say there's an atomic counter, and a resetting flag.
> 
>  1. The abort handler starts to run, and doesn't see the resetting flag,
>     but gets scheduled before it can submit the ABORT TASK command and
>     increment the number of outstanding commands.
> 
>  2. The completion handler for the status URB runs and decrements the
>     count to zero.
> 
>  3. Meanwhile, another driver asks for a reset, and pre-reset is called,
>     which finds the outstanding count zero.  Before it can tear down the
>     stream rings, the context is swapped out for the abort handler thread.
> 
>  4. The abort handler thread submits the ABORT TASK command and waits on
>     it.  At this point, if the pre-reset handler is swapped back in,
>     it's going to happily free the stream rings while the abort task
>     command is still on them.

Yes, this could be another application for SRCU.  Or you could simply
use your spinlock, since neither the abort handler nor the reset
routine is on the fast path.

> Faced with this sort of race condition, and a lack of knowledge about
> how the SCSI layers work with the scsi_host lock, this is where I start
> walking in mental circles, trying to devise a solution that doesn't
> involve the original RCU synchronization between the pre-reset function
> and the abort handler.

I hope things are a little clearer now.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux