Re: [RFC] usbfs2 aio cancellation code

Zach Brown <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
>   It's making my
> head hurt, so I'd appreciate some code review.

That's what AIO does :).

> The in-kernel aio patches that Zack is working will ensure that the

(sekrit tip: Zac*k* Brown: did KT, now at Google.  Zac*h* Brown: me.   
Aren't you glad we both ended up in the same field?  Someone is  
having a laugh.)

> retry function and the cancel function will run serially.  If the  
> cancel
> function returns 0, the iocb will be destroyed.  If cancel returns  
> zero,
> it guarantees that the iocb will never be referenced again,  
> specifically
> by making sure that kick_iocb() and aio_complete() have never been and
> never will be called.

Yeah.  Think of it like reference counting.  By being called in the  
aio_ file operation methods you're being given a reference on the  
iocb.  Four things drop that reference:

  - *not* returning magical aio errnos from the aio_ functions
  - not returning magical aio errnos from ki_retry (after kick_iocb())
  - calling aio_complete()
  - returning 0 from ki_cancel

A provider of aio ops can do only one of these to drop their iocb  
reference.

> Some USB rules apply here too.  To cancel a USB transaction, you can
> call either usb_kill_urb() or usb_unlink_urb().  usb_kill_urb() will
> wait for the URB callback to complete, but it cannot be called with a
> spin lock held.  usb_unlink_urb() guarantees that the URB callback  
> will
> be called sometime in the future with an URB status that indicates it
> was cancelled.  usb_unlink_urb() will return -EBUSY if the URB  
> callback
> was about to run or was in the middle of running.

What happens if you call usb_unlink_urb() after the URB callback has  
been called?

I'm hoping that we can *just* call usb_unlink_urb() from ki_cancel  
and *always* return -EBUSY.  The URB callback will either be called  
very soon on error or will already be in flight or have been called.

The trick there is to make sure that the URB callback doesn't free  
resources that ki_cancel needs to be able to call usb_unlink_urb() to  
find that it's already been called.  I would propose putting a struct  
on iocb->private which is freed in the ki_dtor method.  It's called  
once all the iocb references have been dropped.

> I *think* I can follow the new aio rules, if the aio core provides a
> spinlock for the iocb priv pointer.

It wouldn't do that.  If you needed a spinlock you could put one in  
the struct that you hung off of iocb->private.

>   Here's how it would work:
>
> cancel
> 	ret = 0;
> 	lock and disable interrupts
> 	/* Check to see if the URB has been freed yet.
> 	 * If not, attempt to cancel the URB;
> 	 * fail if the URB callback has already started.
> 	 * We know that read_retry can't run while cancel is running. */
> 	if(iocb->priv == NULL ||
> 			usb_unlink_urb(iocb->priv->urb) == -EBUSY)
> 		ret = -EBUSY;
> 	unlock
> 	return ret

Hmm.  If usb_unlink_urb() returns 0 then operation is still in  
flight, right?  It'll be completed soon by the URB callback, but that  
hasn't happened yet.

So I don't think returning 0 from ki_cancel is right.  The iocb is  
still in flight.  The operation could still be DMAing.  I think we  
only want to return 0 once the operation is well and truly done.   
That's why sys_io_cancel() provides an event if it returns 0.

If all usb_unlink_urb() does is accelerate completion, perhaps now  
with an error, that's fine.  Just always return -EBUSY from  
ki_cancel.  To the app it'll look like their cancelation attempt  
raced with completion, as it might have done anyway.  You're still  
honoring the intent of the sys_io_cancel() call.

> Does the cancel function follow the new aio cancel rules (i.e. it
> doesn't return 0 if kick_iocb() or aio_complete() has been called or
> will be called)?  Are there any races?  I can't see any, but I've been
> banging my head against this a lot this weekend.

I got lost in the pseudo code for testing for cancelation and  
things.  How about this:

aio_ method:
	/* this does not race with ki_cancel */
	iocb->private = alloc_urb_goo();
	iocb->ki_dtor = free_urb_goo();
	queue URB callback
	if (read) {
		iocb->ki_retry = copy_read_result();
		return -EIOCBRETRY:
	} else
		return -EIOCBQUEUED:

URB callback:
	/* usb_unlink_urb() protects the race with ki_cancel */
	/* this cannot race with ki_retry, it triggers it */
	if (read)
		kick_iocb();
	else
		aio_complete();

copy_read_result:
	if (error)
		return error'
	copy_to_user();
	return bytes_copied;

ki_retry:
	some_usb_goo = iocb->private;
	/* usb_unlink_urb() protects us from races */
	/* urb goo won't be freed until ki_dtor, which we hold a reference  
for */
	usb_unlink_urb(some_usb_goo);
	return -EBUSY;

ki_dtor:
	free(iocb->private);

That should work, presuming that usb_unlink_urb() has strong internal  
serialization to have calls from ki_cancel fail if the callback is  
pending or has been completed already.

> p.s. Zack - did you mean to do something with the return value from  
> the
> cancel function?  AFAIK, it simply checks that it's nonzero.

I think it was wrong for the previous implementation to pass the  
errno directly to userspace.  I think userspace would be right to be  
awfully confused if it got, say, EINVAL from ki_cancel instead of  
from the parts of sys_io_cancel() that return EINVAL. We just map an  
error from the ki_cancel function into a consistent errno which just  
says that the operation is still pending.

- z

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
[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.