This is a tricky topic... There are several interrelated issues to consider.
1. Keep the children[] array? Getting rid of it wouldn't be as bad as you seem to think, but I don't object to keeping it. 2. Leave it in struct usb_device? At first I thought it would make more sense to move it into the hub private data structure, but now I don't think so. Doing that would require readers to keep a pointer to private data that could disappear at any time. Keeping it where it is seems simplest, even if not the most space-efficient. 3. Mark usb_devices as soon as we know they are unplugged? David is strongly in favor of this and I don't see any real problems with it either. It does raise the problem of protecting usbdev->state because it means state can change to NOTATTACHED at any time, even while usbdev->serialize is held by another task. 4. How to protect the global bus topology? So far this issue has been ignored, leading to incorrect code as I'll discuss below. For now just note that whatever mechanism protects the topology would also naturally protect ->state, since state = NOTATTACHED indicates a topology change. On Mon, 19 Jan 2004, Greg KH wrote: > > It's not a question of saving memory, it's the principle of not > > duplicating information and thereby avoiding the risk of having > > inconsistent records. I don't think it will complicate other parts of the > > USB code very much. Yes, it will be a little harder to go from a port > > number to the corresponding usb_device (it will require a search through > > the list of children). But that lookup only occurs while processing a > > connect or disconnect event, not very often. And also it will simplify > > the device_reset code, going from usb_device to parent port number -- > > the search that's in there now will become unnecessary. > > The scsi people tried to do this, and ran away screaming in terror (I > heard them accross the building, it wasn't pretty...) > > We would have to special case a lot of code in order to get this to work > properly (hint, how to detect a hub? everywhere that we walk the lists > we would have to export that logic, no fun... And other devices do have > children in the sysfs tree, scsi, tty, etc.) It's not all that bad. For instance, here's how to detect a hub: A struct usb_device is a hub iff it has 1 configuration with 1 interface and that interface is bound to the hub driver. Here's how to distinguish the usb_devices from the other children present in the sysfs tree: A struct device is embedded in a struct usb_device iff it is bound to the usb_generic_driver. Agreed, adding code to export these notions would make things a little messy. Furthermore, the USB device tree is not searched or walked in very many places. Of course there are the spots involved in adding/removing devices and doing device resets. The only others are usb_device_dump() in devices.c and match_device() in usb.c -- and they are incorrect. For instance, consider this code in match_device(): for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { ret_dev = match_device(dev->children[child], vendor_id, product_id); What's to prevent dev from disappearing while this loop executes? What's to prevent dev->children[child] from disappearing in the instant between the "if" and the recursive call to match_device()? Clearly some sort of "topology lock" is needed, and a spinlock seems best suited to the job. > Again, every bit of code that walks the usb device lists would have to > have access to those data structures in order to have a chance of > walking the lists in a semi-coherient manner. Look around the USB code > for everywhere we do this. Then look in the security/ directory for > another user :) The code in security/root_plug.c only uses the data structures indirectly, by way of match_device(). > > This lock should not see much contention. It will be held only very > > briefly in any path where a state gets changed. Something like this: > > > > spin_lock_irq(&global_state_lock); > > if (usbdev->state != USB_STATE_NOTATTACHED) > > usbdev->state = USB_STATE_CONFIGURED; > > else > > notattached = 1; > > spin_unlock_irq(&global_state_lock); > > if (notattached) ... > > > > Even with multiple probes running in parallel this won't cause any > > significant delays. > > > > The reason for adding the lock was David's firm belief that we should > > report a device disconnection (by setting the state to > > USB_STATE_NOTATTACHED) as soon as it is detected, rather than having to > > wait for a driver's disconnect() routine to run or to wait for > > usbdev->serialize. If the state is not protected by serialize then it > > needs some other sort of protection, and a global spinlock is the simplest > > way to do it. > > Why does the lock have to be taken to set the state in this case? > There is not more than one thread that is setting the state, right? > Just multiple users. No, there can be two threads setting the state. usb_set_configuration() could be executing code like the part quoted above concurrently with usb_mark_unplugged() -- a new routine that will change the state to NOTATTACHED as soon as we know the device is unplugged. If we don't implement David's idea then yes, only one thread would set the state at a time. But we would still need the topology lock, for the reasons given above. So why not have it protect state as well? > The main problem about a NOTATTACHED type state, is that we can never > test properly that the state is not NOTATTACHED, but only that it is > NOTATTACHED. I really don't understand that comment. Certainly we know at all times what value is stored in usbdev->state. Do you mean that we can't find out immediately when a device is physically unplugged? That's true, but I'm more concerned about maintaining the internal consistency of the data structures. When something is unplugged, we will update the data structures to reflect the new reality shortly afterwards. > > Another possibility suggested by David would be to add an extra > > "unplugged" flag to struct usb_device. That flag could be set as soon as > > we know the device is unplugged, with no protection at all since it would > > never get reset. > > That's fine with me too. But watch out, I did that with the driver > model during the 2.5 development cycle, and spent a lot of time trying > to clean up the mess it caused... I've decided it's simpler not to do this, just add the topology spinlock. > > As David said earlier, it's _not_ legal to access a device after returning > > from disconnect() because another driver might already be bound to the > > interface. Again, that's the rmmod case -- for disconnect obviously it > > doesn't matter. > > But after rmmod finished, the driver isn't present to be touching the > device. Well, it had better not, otherwise we have worse problems :) How about drivers that are unbound through usbfs? They will remain in memory. > > > We already do this today. > > > > Ah -- we do it when the device is unplugged. So far as I can see, we > > don't do it when usb_deregister() is called. > > That should be changed then. Yes. One of the changes I want to make rather soon is to insure that URBs are unlinked and endpoints disabled whenever a driver is unbound from an interface, regardless of the pathway involved. Likewise, if the device hasn't been unplugged then reinstall altsetting 0 after calling the driver's disconnect(). Alan Stern ------------------------------------------------------- The SF.Net email is sponsored by EclipseCon 2004 Premiere Conference on Open Tools Development and Integration See the breadth of Eclipse activity. February 3-5 in Anaheim, CA. http://www.eclipsecon.org/osdn _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel