Re: requirements for suspend()

Alan Stern <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
On Mon, 7 May 2007, Oliver Neukum wrote:

> Am Samstag, 5. Mai 2007 17:30 schrieb Alan Stern:
> 
> > The bound isn't on the time a device may have data -- it's on the time 
> > allowed for a transfer to complete.  And the upper bound would be imposed 
> > by the driver.  How long is it willing to hold up suspend processing 
> > because of ongoing I/O?  The driver must decide one way or another.
> 
> OK, what do you think of this approach?

Looks basically okay.  There are a few issues...

> --- a/include/linux/usb.h	2007-05-04 10:50:18.000000000 +0200
> +++ b/include/linux/usb.h	2007-05-07 14:48:09.000000000 +0200

>  /**
>   * struct urb - USB Request Block
>   * @urb_list: For use by current owner of the URB.
> + * @anchor: to anchor URBs to a common mooring

Don't you want to add a kerneldoc line for anchor_list too?

> @@ -1270,6 +1286,11 @@ extern struct urb *usb_get_urb(struct ur
>  extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
>  extern int usb_unlink_urb(struct urb *urb);
>  extern void usb_kill_urb(struct urb *urb);
> +extern void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor *new);

usb_kill_anchored_urbs() instead of _associated_ ?

> +extern void usb_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a);

Perhaps usb_add_urb_to_anchor() ?
Or even usb_anchor_urb() -- like usb_unanchor_urb() ?

> +extern void usb_unanchor_urb(struct urb *urb);
> +extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout);
> +extern void usb_free_anchored_urbs(struct usb_anchor *anchor);

Is this last one really needed?  See below...

> --- a/drivers/usb/core/urb.c	2007-05-04 10:53:14.000000000 +0200
> +++ b/drivers/usb/core/urb.c	2007-05-07 14:48:12.000000000 +0200

> @@ -11,6 +12,9 @@
>  static void urb_destroy(struct kref *kref)
>  {
>  	struct urb *urb = to_urb(kref);
> +	unsigned long flags;
> +
> +	usb_unanchor_urb(urb);
>  	kfree(urb);
>  }

The anchor should take a reference to the URB; then urb_destroy() won't
need to be changed.

> +/**
> + * usb_associate_urb_to_anchor - anchors an URB while it is processed
> + * @u: pointer to the urb to anchor
> + * @a: pointer to the anchor
> + *
> + * This can be called to have access to URBs which are to be executed
> + * without bothering to track them
> + */
> +
> +void usb_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a)

How come you sometimes refer to the URB function argument as "u" and
sometimes as "urb"?  Ditto for "a" and "anchor"?

> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&a->lock, flags);
> +	list_add_tail(&u->anchor_list, &a->urb_list);
> +	u->anchor = a;
> +	spin_unlock_irqrestore(&a->lock, flags);
> +}

Call usb_get_urb() here.

> +void usb_unanchor_urb(struct urb *urb)
> +{
> +	unsigned long flags;
> +	struct usb_anchor *anchor;
> +
> +	if (!urb)
> +		return;
> +
> +	anchor = urb->anchor;
> +	if (!anchor)
> +		return;
> +
> +	spin_lock_irqsave(&anchor->lock, flags);
> +	urb->anchor = NULL;
> +	list_del(&urb->anchor_list);
> +	spin_unlock_irqrestore(&anchor->lock, flags);
> +	if (list_empty(&anchor->urb_list))
> +		wake_up(&anchor->wait);
> +}

And call usb_put_urb() here.

> +/**
> + * usb_kill_associated_urbs - cancel transfer requests en masse
> + * @old: anchor the requests are bound to
> + * @new: anchor the request which were canceled shall be bound to, may be NULL
> + *
> + * this allows all outstanding URBs to be cancelled and bound to an anchor
> + * for later processing
> + */
> +
> +void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor *new)
> +{
> +	unsigned long flags;
> +	struct urb *victim;

How come you sometimes refer to URB local variables as "victim", sometimes 
as "lazarus", and sometimes as "n", but never as "urb"?

> +
> +	spin_lock_irqsave(&old->lock, flags);
> +	while (!list_empty(&old->urb_list)) {
> +		victim = list_entry(old->urb_list.prev, struct urb, anchor_list);
> +		list_del(&victim->anchor_list);

You're better off not removing victim from its old anchor at all;
usb_kill_urb() will do that for you during completion handling.  And you 
certainly don't want to call list_del() without also setting 
victim->anchor to NULL.

> +		/* we must make sure the URB isn't freed before we kill it*/
> +		usb_get_urb(victim);
> +		spin_unlock_irqrestore(&old->lock, flags);
> +		usb_kill_urb(victim);
> +		/* we may transfer only if we killed */
> +		if (new && victim->status == -ENOENT)
> +			list_add(&victim->anchor_list, &new->urb_list);

Likewise, don't call list_add() without setting victim->anchor.  Better 
yet, call usb_anchor_urb().

Or better still, don't allow this "new" argument at all.  It's not clear 
why you might need it.  It doesn't get used in the model usb-skeleton 
code, for instance.

> +		else
> +			usb_put_urb(victim);

Get rid of the "else"; since you always increment the refcount, you must 
always decrement it.

> +		spin_lock_irqsave(&old->lock, flags);
> +	}
> +	spin_unlock_irqrestore(&old->lock, flags);
> +}
> +
> +int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout)
> +{
> +	int res;
> +
> +	res = wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), timeout);

Maybe make this an interruptible wait?

> +	return res;
> +}
> +
> +void usb_free_anchored_urbs(struct usb_anchor *anchor)
> +{
> +	struct urb *lazarus, *n;
> +
> +	list_for_each_entry_safe(lazarus, n, &anchor->urb_list, urb_list) {
> +		usb_buffer_free(lazarus->dev, lazarus->transfer_buffer_length,
> +		lazarus->transfer_buffer, lazarus->transfer_dma);
> +		list_del(&lazarus->anchor_list);
> +		usb_free_urb(lazarus);	
> +	}
> +}

I don't see the point of this routine.  For example, usb-skeleton frees
the transfer buffer during skel_write_bulk_callback(); you wouldn't want
to free it again.

In general the fire-and-forget model has to work that way.  URBs and their
buffers will be freed by the completion handler.

Alan Stern


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.