I've been deliberately avoiding issues of simultaneous access,
interference, and locking as being secondary to the main problem of
figuring out how power management should work.  But the issues are
interrelated, so here are some general thoughts.


Let's take it as given that drivers need a guarantee that their probe(),
release(), suspend(), and resume() routines will be mutually exclusive --
that is, no more than one of them will be called for any particular device
at a time.  Some drivers may have additional routines they would like to
add to that list, particularly if they do some of their work in a separate
kernel thread.  (Routines that add or remove a child are a good example.)

One way to guarantee this is to use a per-driver semaphore.  It would do
even more; it would prevent a driver from suspending one device while
resuming another.  I suspect that's more mutual exclusion than we want.  
Furthermore, this approach has a re-entrancy problem.  It's not uncommon
on non-hot-pluggable buses for child discovery and registration to take
place during probe().  If a device's child (or grandchild, etc.) used the
same driver as the device itself, we would have deadlock.

The other natural possibilities are per-device semaphores or per-subsystem
semaphores like the driver model uses now.  Per-subsystem locking also 
involves re-entrancy problems (this causes difficulty for the USB hub 
driver, for example, which needs somehow to disconnect all the children 
when it is unbound from a hub).


Moving on, drivers sometimes need a guarantee that children will not be
added or removed beneath a particular node.  David has suggested that a
spinlock be used to protect the device-tree topology.  Consider, however,
that when we want to suspend a device, during the entire time that the
device's children and then the device itself are suspended we need to
guarantee that no new children will be added below the device.  A spinlock
can't accomplish this; some type of semaphore is needed.  (You could use a
spinlock in addition to a semaphore, like usbcore uses to protect device
states, which sometimes need to change without waiting for a semaphore to
be released.)

The topology lock might be an rwsem instead of a regular semaphore.  
Either way, there are three natural choices for its scope: a single
kernel-wide semaphore, a per-subsystem semaphore, and a per-device
semaphore.  The first choice is obviously not scalable, although on 
desktop-sized systems it would be acceptable.

Using per-subsystem semaphores runs into problems when you carry out
operations that affect multiple subsystems, like a system-wide suspend.  
At best it's more difficult, and at worst you would end up trying to
acquire (or trying to acquire write access to) the same subsystem's
semaphore multiple times.


So now I've discussed the need for two classes of semaphores: to serialize
access to drivers and to protect the tree topology.  Many operations 
naturally will involve both classes.  For example, registering a new child 
device (topology lock) involves probing for drivers (serialize lock).  And 
unbinding a driver (serialize lock) may require unregistering some 
children (topology lock).  This looks like a nice invitation to deadlock 
unless the two classes of semaphores are in fact one and the same.

Putting it all together, this analysis indicates the best approach is for
each struct device to include a semaphore (or possibly an rwsem) for
general locking.  The lock must be held when binding or unbinding a
driver, suspending or resuming the device, registering or unregistering
any children, and (presumably, although I didn't mentioned it above)
registering or unregistering the device itself.  It may be convenient to
add some more restrictions; for example, it would make sense to require
the device to be locked while resuming a child -- thereby preventing the
possibility that the resume task could interfere with another task trying
to suspend the device.  And individual drivers might want to use the lock
for other purposes too (such as device resets).


Not by coincidence, the scheme I've arrived at is essentially the one used
in usbcore.  Basically, I'm suggesting that it be formalized and made to
apply to the entire device-tree and power-hierarchy.

I don't think _too_ many changes would be needed.  There would have to be 
variants of device_add() and device_del() that require the caller to own 
the device lock.  Probably a number of bus drivers would need some 
updating, and who knows how many little alterations would have to 
sprinkled throughout the kernel.

Alan Stern



-------------------------------------------------------
This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170
Project Admins to receive an Apple iPod Mini FREE for your judgement on
who ports your project to Linux PPC the best. Sponsored by IBM.
Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to