I agree with Oliver on this. Although the existing API provides enough mechanism for drivers to make sure that all their urbs are unlinked and all completion handlers are through, it's not easy to do correctly. Since practically every driver has to deal with this problem during unloading, the functionality deserves to be centralized in the core. Furthermore, doing so turns out to be relatively easy and can even provide better error checking.
Oliver, it turns out the advice I gave you yesterday about putting in the spinlocking stuff in unlink_complete() was wrong. That's probably what was causing your kernel to hang. I should know better than to make a hasty reply on a complicated topic. All right. I spent a long time thinking about the issues last night, and now I'm confident that I have a correct solution. First, a few comments. For ease of discussion, let's say that an urb is "finished" when the low-level HCD code is done with it. This happens just before the completion handler is called. The intended semantics for usb_unlink_urb() should be something like this: An ongoing urb is stopped reasonably quickly, and the completion handler is not allowed to resubmit it. If the urb has already finished and the completion handler is running, but the handler has not yet resubmitted, a resubmission will fail. If the handler has already resubmitted, then the unlink applies to the new incarnation of the urb. In any case, if the unlink is synchronous, it will not return until after the corresponding completion handler has finished running. It seems to me that at the point where the completion handler returns, it's necessary to distinguish between urbs that have and have not been resubmitted by the handler. There are at least two reasons for this. First, state changes (i.e., going to IDLE) should not happen for resubmitted urbs. Second (and more subtle), there are differences between resubmitting an urb from the handler as opposed to submitting it again after the handler is done. For example, any bandwidth reserved for the urb is kept when the handler resubmits, but it is deallocated otherwise. (I don't know where or even if this really happens -- logically the decision can only be made directly after the handler returns.) At any rate, this means that we need to guarantee, following the call to urb->complete(), that the urb is in different states depending on whether or not it was resubmitted. Normally, at this point a resubmitted urb will be SUBMITTED, which is okay. But it might finish or get cancelled, in which case the state would be COMPLETING or CANCELED, which is not good. We can take care of the COMPLETING possibility by only making the SUBMITTED -> COMPLETING transition while the handler_lock is held. The CANCELED possibility is a bit harder. I considered the idea of making usb_unlink_urb() spin if it is called for a resubmitted urb still in its completion handler, but that turned out to be neither desirable nor feasible. In the end, the best answer was to add a new state. Let's agree that when a CANCELED urb's handler is called, the state changes to TERMINATING, just as a SUBMITTED urb's state changes to COMPLETING. (The name TERMINATING is meant to suggest that an indefinite handler-automatically-resubmits loop will be broken.) That takes care of everything: when the completion handler returns, if the urb was resubmitted then its state will be either SUBMITTED or CANCELED, otherwise its state will be either COMPLETING or TERMINATING. If there turns out to be a driver that really needs to resubmit a cancelled urb from within its completion handler, it will be easy to write an API that changes the state from TERMINATING back to COMPLETING. Since there are now two spinlocks involved here, we have to take care to avoid deadlock. Completion handlers run with handler_lock held, and they have to be able to call usb_submit_urb() which acquires lock (I think -- correct me if this is wrong), so the rule must be: Whenever lock and handler_lock are both acquired, you have to acquire handler_lock first. The state diagram is now a little too complicated for my ASCII art skills. I'll just present it as a table. IDLE: the initial state. It is a BUG to destroy an urb that is not in the IDLE state (maybe urb->count already takes care of that). usb_submit_urb() --> SUBMITTED SUBMITTED: under control of the low-level HCD. HCD finishes --> COMPLETING usb_unlink_urb() --> CANCELED CANCELED: still under control of the HCD. HCD finishes --> TERMINATING COMPLETING: a non-cancelled urb passed to its completion handler. usb_unlink_urb() --> TERMINATING usb_submit_urb() --> SUBMITTED handler returns --> IDLE TERMINATING: a cancelled urb passed to its completion handler. handler returns --> IDLE Adjustments needed with respect to the last patch: Add the URB_TERMINATING state to usb.h. Remove the spinlock calls from unlink_complete(). Since unlink_complete() is itself a completion handler, the spinlock has already been acquired. Likewise, don't change the state to IDLE. In hcd_unlink_urb(): the CANCELED and TERMINATING cases are both BUGS (or maybe they are both just oopses) -- someone has tried to unlink the same urb twice. In the COMPLETING case, the state should change to TERMINATING. In usb_hcd_giveback_urb(): move spin_lock(&urb->handler_lock) to the start, immediately before spin_lock(&urb->lock). If the state was CANCELED, set it to TERMINATING. At the end, release handler_lock only after updating the state. Set the state to IDLE if it was either COMPLETING or TERMINATING. In usb_submit_urb(): if the state was either SUBMITTED or CANCELED then it's a BUG or an oops. If the state was TERMINATING, return -EINTR. Update the appropriate kerneldoc comments and the error code listing in Documentation/usb/error-codes.txt. Alan Stern ------------------------------------------------------- This SF.NET email is sponsored by: Take your first step towards giving your online business a competitive advantage. Test-drive a Thawte SSL certificate - our easy online guide will show you how. Click here to get started: http://ads.sourceforge.net/cgi-bin/redirect.pl?thaw0027en _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel