On Fri, 2 Jan 2004, David Brownell wrote: > > For error recovery. If an attempt to disable the port fails, we can't > > simply leave things as they stand -- there will be an unaddressed device > > attached to the bus. It will be necessary to disconnect the entire hub to > > avoid problems. And doing that requires a lock on the hub's parent.
> I hope you're actually saying that if the device is in the DEFAULT > state (a brief instant during port reset) that might need to happen. As John Cleese once said, that was indeed the very nub of my gist. > Right now nothing does such things ... has that been making trouble? As far as I know it has never happened. > Hubs that can't disable ports would seem to be rather broken... Quite so. Actually, the most likely way I could imagine this happening is if the hub is unplugged at just the wrong time. Of course, in that case it doesn't matter whether we try to reset the hub or not. Broken hubs seem much more unlikely. This ties in with the earlier part of this thread. Managing malfunctioning hubs is an important aspect of maintaining the topology of the USB bus. Right now, hub.c contains special-case code to reset a hub for error-recovery. It ought to be possible to reuse that code for the situation when the hub won't disable a port. Even better, all the special-case code in hub.c:hub_reset() could be removed if we had a way to specify that usb_reset_device() should follow the "device morphed" path, regardless of whether the descriptors actually did change. For instance, we might say that this should happen whenever the device being reset is a hub. This is related to a subtle point concerning usb_disconnect(). Normally it's called when we want to remove a usb_device plus everything below it in the tree. But there are a few cases when the code wants to remove everything below the device while leaving the device itself intact. It does this by calling usb_disconnect() repeatedly, for each child of the device -- awkward but workable. One of those cases is in hub_reset(), which I mentioned above. The others are rather analogous although different in detail; they occur when a host controller fails -- everything below the root hub is disconnected but the root hub remains. In a way, failure of an HC can be treated as failure of the virtual root hub. The point I want to make here is that these cases could be simplified in a way by calling usb_reset_device() on the hub in question, letting it remove the hub along with all the children. If the hub is still present afterward, it will automatically be reprobed. Root hubs will require special treatment, of course, but in the end this may turn out to be a better approach. Getting back to the original topic, here are two points that I think everyone can agree on: When usb_disconnect() is processing the children[] array for a hub, it doesn't want the hub driver interfering by handling attach/unplug events concurrently. Because usb_disconnect() will remove the hub device after processing all its children, preventing the hub driver from interfering by means of a lock or semaphore is the wrong approach. When the lock is released, the hub driver would unblock and go ahead to attach or remove a child from the hub which is itself about to go away. Instead, the hub driver should be prevented from interfering by being unbound from the hub. Given this reasoning, there can be no doubt that it's best for usb_disconnect() to unbind the hub driver before processing the children[] array of a hub. There are other corollaries too, such as that usb_disconnect() needs to be able to tell whether a given device is a hub or not. I think that's a reasonable requirement; hubs are so closely connected to the maintenance of the bus topology that usbcore _should_ be aware of which devices are hubs. If usb_disconnect() follows this approach of unbinding the hub driver before doing anything else, then there's no possibility of contention for the children[] array between the hub driver and anything else. Of course, there's still the possibility of contention among other threads calling usb_disconnect(). The best way to avoid this is for usb_disconnect() to hold usbdev->serialize while processing a device and its children. In fact usb_disconnect() already does this anyway. David, does this amount to maintaining the same kind of lock tree you were talking about? There's one more point. usb_disconnect() is passed as an argument a pointer to the device entry in its parent's children[] array, and one of the first things it does is to zero out that entry. That means it ought to hold the parent's usbdev->serialize, but of course it can't acquire that semaphore on its own. The caller has to do it. This could be handled in two ways. In the first, the caller simply grabs the semaphore before calling usb_disconnect(). In the second, which I like better, the caller grabs the parent's serialize and zeros out the entry by itself. Then it releases the semaphore and calls usb_disconnect(), passing it a pointer to the device rather than a pointer to a pointer. This would permit an attractive concurrency in handling unplug events. Khubd could remove the pointer from the parent hub to the child device, then another thread could carry out all the work involved in removing the device structures, unbinding drivers, and so on. Khubd would not have to wait for that to complete -- I think; I'm not so sure about the underlying driver-model children pointers that would remain. Alan Stern ------------------------------------------------------- This SF.net email is sponsored by: IBM Linux Tutorials. Become an expert in LINUX or just sharpen your skills. Sign up for IBM's Free Linux Tutorials. Learn everything from the bash shell to sys admin. Click now! http://ads.osdn.com/?ad_id=1278&alloc_id=3371&op=click _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel