Let's see if we can't clear away some of the issues. On Mon, 3 May 2004, David Brownell wrote:
> Alan Stern wrote: > > > > I'm talking about protecting the physical topology of the entire tree. > > I still don't see why the _entire_ tree needs that sort > of protection. I hope I'm not missing something. It doesn't. On the other hand, we might not want to use hub->serialize to provide this protection, for a reason I'll explain below. Basically, though, what we want is some sort of read-protection while traversing the tree whereas serialize provides write-protection. (It's not quite so simple but that's the general idea.) Making serialize an rwsem doesn't quite solve the problem (see below). Adding a global topology semaphore appears to be the easiest solution. > > And I would find it acceptable > > to use hubdev->serialize to protect the links at the inner nodes if only I > > could see how to also get usb_reset_device() working reliably. And > > useable from within probe(). > > I think usb_reset_device(dev) would just need to call locktree(dev), > then fail cleanly on -ENODEV returns. > > As for usable from probe() ... I just had the germ of an idea. How about > splitting out the "logical" topology lock (protecting SET_CONFIGURATION, > while it's probing every interface) from the "physical" topology lock > (hub->serialize)? A single bit in "struct usb_device" would suffice > to prevent nested calls to the usb_set_configuration() routine, during > probe(), which would cause self-deadlock (and other chaos). I've been considering possibilities along those lines as well, but they're still far too cloudy to mention here. Instead, here's a description of the picture I'm building up: Locks will always go top-down. This automatically solves the "disconnecting a subtree and the whole tree simultaneously" problem. When the thread starting from the top reaches the hub above the subtree (i.e., the hub that reported a port connect change) it will find that hub locked by the subtree-disconnect thread. When the hub is unlocked the entire subtree below the port will be gone, so the whole-tree disconnect can proceed without difficulty. The big question is how to make device resets work. It's pretty clear that usb_reset_device(udev) needs to lock both the parent hub and udev. The problem is that the driver's disconnect() and suspend() routines will probably block waiting for usb_reset_device to return, so we can't simply acquire the semaphores in the usual way. This approach ought to work for acquiring hub->serialize: for (;;) { if (udev->state != USB_STATE_CONFIGURED) return -ENODEV; if (down_trylock(&hub->serialize)) break; wait_ms(100); } Yes, this is a polling loop. It's not so bad, because device resets are very rare. Furthermore, when a driver does a device reset it expects to encounter a long time delay. This won't deadlock with disconnect caused by unplugging the device (or unbinding the hub driver!) because the device's state will be changed to USB_STATE_NOTATTACHED. It won't deadlock with disconnect caused by rmmod'ing the driver because that won't lock the hub. To prevent deadlocks with suspend, your patch would have to change the device's state before calling its suspend() routine -- or else the driver's suspend() routine would have to avoid waiting for usb_reset_device to return. (Incidentally, I don't see the need for usb_suspend_device() to call locktree(). Wouldn't it suffice to lock the hub and then check that it is still USB_STATE_CONFIGURED?) Next we need to lock udev itself. However, if some other thread already has locked udev (for suspend or unbind) then we generally want to fail. So a simple down_trylock() will suffice. There are exceptions, though. What if udev is locked because a different interface is being bound or unbound? I don't know -- this comes under the heading of resetting a device with more than one interface, so let's ignore it. What if udev is locked because of a tree traversal? Then we don't want to fail; we just want to wait until the lock is released. This is the special reason I mentioned at the top of this email. Now you see the problem with making serialize into an rwsem: We want to acquire a write lock, we want to wait if the lock is currently held for reading, and we want to fail if the lock is currently held for writing. Rwsems won't do that. But if tree traversal used a separate semaphore then everything would be all right -- we could acquire udev->serialize and ignore the topology semaphore since we aren't going to change any children[] pointers. The last problem is dealing with device resets during probe. The first part of this problem -- locking the hub -- might be easy. Is there any reason why usb_new_device() can't be called with the hub unlocked? The second part of the problem is trickier. Do we need to hold udev->serialize while probing? Certainly we want to avoid suspend during probe and vice versa. Apart from that, perhaps it doesn't matter. Or perhaps we could adopt something along the lines of your suggestion, so that usb_reset_device() would know if it was being called from within probe and could skip acquiring the device lock. (But what if it was called by a driver for an interface other than the one being probed?) Or as a last resort, we can rule out resets during probe -- any driver that wants to do it will have to use a separate kernel thread or work queue. The disadvantage is that probe() will have to return before learning the result of the reset. Alan Stern ------------------------------------------------------- This SF.Net email is sponsored by: Oracle 10g Get certified on the hottest thing ever to hit the market... Oracle 10g. Take an Oracle 10g class now, and we'll give you the exam FREE. http://ads.osdn.com/?ad_id=3149&alloc_id=8166&op=click _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel