On Tue, Sep 21, 2021 at 1:24 AM David Laight David.Laight@aculab.com wrote:
From: Luis Chamberlain
Sent: 17 September 2021 20:47
When sysfs attributes use a lock also used on module removal we can
race to deadlock. This happens when for instance a sysfs file on
a driver is used, then at the same time we have module removal call
trigger. The module removal call code holds a lock, and then the sysfs
file entry waits for the same lock. While holding the lock the module
removal tries to remove the sysfs entries, but these cannot be removed
yet as one is waiting for a lock. This won't complete as the lock is
already held. Likewise module removal cannot complete, and so we deadlock.
Isn't the real problem the race between a sysfs file action and the
removal of the sysfs node?
Nope, that is taken care of by kernfs.
This isn't really related to module unload - except that may
well remove some sysfs nodes.
Nope, the issue is a deadlock that can happen due to a shared lock on
module removal and a driver sysfs operation.
This is the same problem as removing any other kind of driver callback.
There are three basic solutions:
- Use a global lock - not usually useful.
- Have the remove call sleep until any callbacks are complete.
- Have the remove just request removal and have a final
callback (from a different context).
Kernfs already does a sort of combination of 1) and 2) but 1) is using
atomic reference counts.
If the remove can sleep (as in 2) then there is a requirement
on the driver code to not hold any locks across the 'remove'
that can be acquired during the callbacks.
And this is the part that kernfs has no control over since the removal
and sysfs operation are implementation specific.
Now, for sysfs, you probably only want to sleep the remove code
while a read/write is in progress - not just because the node
is open.
That probably requires marking an open node 'invalid' and
deferring delete to close.
This is already done by kernfs.
None of this requires a reference count on the module.
You are missing the point to the other aspect of the try_module_get(),
it lets you also check if module exit has been entered. By using
try_module_get() you let the module exit trump proceeding with an
operation, therefore also preventing any potential use of a shared
lock on module exit and the driver specific sysfs operation.
Luis