Am Mittwoch, 15. Januar 2003 21:54 schrieb Alan Stern: > On Wed, 15 Jan 2003, Oliver Neukum wrote: > > Am Mittwoch, 15. Januar 2003 17:04 schrieb Alan Stern: > > > 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. > > > > Eh, how so? usb_hcd_giveback_urb() does call unlink_urb() > > unconditionally. > > This is where my lack of knowledge of the details underlying the usb core > shows through. Okay, looking at hcd.c I see where urb_unlink() is called > and how it releases the bandwidth. I'm surprised; is it really supposed > to work this way?
Questionable. Probably it was alright during the days of automatic resubmission. But it's another issue. > I recall a long discussion a few months ago between you and David Brownell > concerning the question of how to reserve bandwidth. The upshot was that There was such a discussion, but I did hardly take part. > there was no need, because bandwidth was allocated on demand and would be > retained as long as an urb was resubmitted by its completion handler. > Looking at the code, it appears that is not the case -- > usb_release_bandwidth() is called whether the urb is resubmitted or not. > > I don't know what ought to happen here. Somebody will have to fill me in. > > > The URB cannot finish regularly because that would be a hardware > > IRQ reentering itself. The generic IRQ code should prevent that. > > Okay, there's no reason to worry about the possibility of a resubmitted > urb being COMPLETING, and hence no reason to acquire the handler_lock > before setting the state to COMPLETING. (But what if the resubmitted urb > goes to a different usb device, on a different bus attached to a different > IRQ line? Can't one hardware IRQ interrupt another? What if it is > fielded by a different processor?) Then we may have a problem. I've a plan for that. Could we defer this to a seperate thread? > > So the only cause of a status change at that point can be > > usb_unlink_urb(). In that case going to CANCELED seem to be correct and > > not causing a problem. > > > > IMHO changing the status to CANCELED is no problem and neither > > is spinning on the handler_lock. Submitting and unlinking an URB > > from a completion handler is arcane anyway. > > The situation I had in mind (rare, I admit -- but that's where the > gremlins live) is that the handler always resubmits, but one time through > the loop another processor decides to unlink the urb somewhere between > when it was resubmitted and when the handler returned. When that > happens the state will be CANCELED upon the return from the handler, and > so usb_hcd_giveback_urb() will proceed to change the state to IDLE. (It > has to assume this was a normally cancelled urb, not one that was > resubmitted and then cancelled.) This would be wrong; the state should > remain CANCELED since the handler for the cancelled urb has not yet been > called. Now the state is corrupted, something I would like to avoid. You are right, it's a bug. The decision must take the state upon entry into consideration. I'll fix that. > Although corrupting the state in this way seems innocent enough here, it > has the potential to lead to problems later on. Since it only requires a > little more programming effort to get things right, I think we should do > so. Ack. > > Could you explain why CANCELED and TERMINATING should be differentiated? > > It seems to me that both usb_unlink_urb() and usb_submit_urb() would fail > > with an error in both cases. > > Well, the explanation above shows why something like TERMINATING needs to > exist. Differentiating them occurs in two places. Immediately after the > call to the completion handler, the state gets set to IDLE if it was > TERMINATING but not if it was CANCELED. Although usb_submit_urb() will > fail with an error in both cases, the errors ought to be different: If the > state was CANCELED then the urb was resubmitted _before its handler was > called_; definitely a programming bug. If the state was TERMINATING then Yes, but detecting errors is an accidental benefit. If you do something obviously stupid, you'll suffer deservedly. I don't that this is a consideration here. Simplicity is paramount IMHO. [..] > If you feel that my approach still isn't right and want to discuss it some > more, I'll be happy to oblige. Do you like this version? Regards Oliver # This is a BitKeeper generated patch for the following project: # Project Name: greg k-h's linux 2.5 USB kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1270 -> 1.1274 # include/linux/usb.h 1.122 -> 1.123 # drivers/usb/core/urb.c 1.19 -> 1.20 # drivers/usb/core/hcd.c 1.86 -> 1.90 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/01/14 [EMAIL PROTECTED] 1.1271 # - explicit URB state # - improvement in unlinking semantics # -------------------------------------------- # 03/01/14 [EMAIL PROTECTED] 1.1272 # - spinlocks in unlink notification path # - fix state in case of resubmission # -------------------------------------------- # 03/01/15 [EMAIL PROTECTED] 1.1273 # - remove incorrect spinlocking # -------------------------------------------- # 03/01/15 [EMAIL PROTECTED] 1.1274 # - correct state handling in unlink case # -------------------------------------------- # diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Wed Jan 15 22:28:59 2003 +++ b/drivers/usb/core/hcd.c Wed Jan 15 22:28:59 2003 @@ -1138,14 +1138,23 @@ goto done; } - /* Any status except -EINPROGRESS means something already started to - * unlink this URB from the hardware. So there's no more work to do. - * - * FIXME use better explicit urb state + /* + * We decide based upon the internal_state what's to be done */ - if (urb->status != -EINPROGRESS) { + switch (urb->internal_state) { + case URB_SUBMITTED: /* normal case */ + urb->internal_state = URB_CANCELED; + break; + default: + BUG(); + case URB_IDLE: + case URB_CANCELED: retval = -EBUSY; goto done; + case URB_COMPLETING: + retval = -EBUSY; + urb->internal_state = URB_CANCELED; + goto err_out_waiting; } /* maybe set up to block until the urb's completion fires. the @@ -1205,6 +1214,18 @@ if (retval && sys) dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval); return retval; + +err_out_waiting: + /* we are asked to unlink while the completing routine is already + running. We drop the locks and spin in case of synchronous unlink */ + spin_unlock (&hcd_data_lock); + spin_unlock_irqrestore (&urb->lock, flags); + if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) { + spin_lock_irq(&urb->handler_lock); + spin_unlock_irq(&urb->handler_lock); + } + + return retval; } /*-------------------------------------------------------------------------*/ @@ -1286,7 +1307,13 @@ */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) { + int old_state; + urb_unlink (urb); + spin_lock(&urb->lock); + if ((old_state = urb->internal_state) == URB_SUBMITTED) + urb->internal_state = URB_COMPLETING; + spin_unlock(&urb->lock); // NOTE: a generic device/urb monitoring hook would go here. // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev) @@ -1307,7 +1334,15 @@ } /* pass ownership to the completion handler */ + spin_lock(&urb->handler_lock); urb->complete (urb, regs); + spin_unlock(&urb->handler_lock); + /* we are now idle unless the urb was resubmitted */ + spin_lock(&urb->lock); + if (urb->internal_state == URB_COMPLETING || old_state == URB_CANCELED) + urb->internal_state = URB_IDLE; + spin_unlock(&urb->lock); + usb_put_urb (urb); } EXPORT_SYMBOL (usb_hcd_giveback_urb); diff -Nru a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c --- a/drivers/usb/core/urb.c Wed Jan 15 22:28:59 2003 +++ b/drivers/usb/core/urb.c Wed Jan 15 22:28:59 2003 @@ -44,6 +44,7 @@ memset(urb, 0, sizeof(*urb)); urb->count = (atomic_t)ATOMIC_INIT(1); spin_lock_init(&urb->lock); + spin_lock_init(&urb->handler_lock); return urb; } @@ -192,6 +193,8 @@ struct usb_device *dev; struct usb_operations *op; int is_out; + int r; + unsigned long flags; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; @@ -346,7 +349,28 @@ urb->interval = temp; } + spin_lock_irqsave(urb->lock, flags); + switch (urb->internal_state) { + case URB_IDLE: + case URB_COMPLETING: + urb->internal_state = URB_SUBMITTED; + break; + case URB_CANCELED: + r = -EINTR; + goto err_out; + case URB_SUBMITTED: + default: + BUG(); + r = -EBUSY; + goto err_out; + } + spin_unlock_irqrestore(urb->lock, flags); + return op->submit_urb (urb, mem_flags); + +err_out: + spin_unlock_irqrestore(urb->lock, flags); + return r; } /*-------------------------------------------------------------------*/ diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Wed Jan 15 22:28:59 2003 +++ b/include/linux/usb.h Wed Jan 15 22:28:59 2003 @@ -728,6 +728,8 @@ { spinlock_t lock; /* lock for the URB */ atomic_t count; /* reference count of the URB */ + int internal_state; /* state in the URB's lifecycle */ + spinlock_t handler_lock; /* lock during completion routine */ void *hcpriv; /* private data for host controller */ struct list_head urb_list; /* list pointer to all active urbs */ struct usb_device *dev; /* (in) pointer to associated device */ @@ -750,6 +752,11 @@ usb_complete_t complete; /* (in) completion routine */ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ }; + +#define URB_IDLE 0 +#define URB_SUBMITTED 1 +#define URB_COMPLETING 2 +#define URB_CANCELED 3 /* -------------------------------------------------------------------------- */ ------------------------------------------------------- This SF.NET email is sponsored by: A Thawte Code Signing Certificate is essential in establishing user confidence by providing assurance of authenticity and code integrity. Download our Free Code Signing guide: http://ads.sourceforge.net/cgi-bin/redirect.pl?thaw0028en _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel