[PATCH 7/9] w1: don't hold list_mutex while a slave is torn down

From: Greg Kroah-Hartman

Date: Tue Oct 27 2026 - 12:15:07 EST


From: Josef Bacik <josef@xxxxxxxxxxxxxx>

When the last reference to a slave is dropped, w1_unref_slave() holds
list_mutex of the master until the slave has been freed. In between it
calls the ->remove_slave() callback of the family driver and
device_unregister(). Both wait for others: for the sysfs callbacks
which are running, and in the case of w1_therm also for the file
therm_bulk_read of the master and for everybody who is talking to the
sensor. None of those can therefore take list_mutex, although the
callbacks of therm_bulk_read walk the list which it protects.

The mutex is only needed to make the decrement of the reference count and
the removal from the list one step for w1_search_slave() and
w1_slave_search_device(), which look the slave up in the list and, in the
first case, take a reference. Once the slave is off the list, nobody can
find it any more. Drop the mutex at that point.

The callers which hold the mutex of the master, which serialises
attaching and detaching, are not affected. The one which does not is
w1_process_cb(), when it drops the last reference to a slave which has
been detached while a netlink command for it was queued. Somebody who
attaches a slave with the same ID through sysfs or netlink at that very
moment used to wait until the old device was gone. Now the registration
of the new device may fail because the name is still in use.

This is a preparation for "w1: w1_therm: lock the list of slaves for the
bulk read".

Cc: stable <stable@xxxxxxxxxx>
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
drivers/w1/w1.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index fe7b73d2a553..4eb785c643c9 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -781,16 +781,24 @@ int w1_unref_slave(struct w1_slave *sl)
{
struct w1_master *dev = sl->master;
int refcnt;
+
+ /*
+ * Once it is off the list nobody can find the slave and take a new
+ * reference. The lock must not be held any longer than that: the rest
+ * waits for sysfs callbacks and for users of the family data.
+ */
mutex_lock(&dev->list_mutex);
refcnt = atomic_sub_return(1, &sl->refcnt);
+ if (refcnt == 0)
+ list_del(&sl->w1_slave_entry);
+ mutex_unlock(&dev->list_mutex);
+
if (refcnt == 0) {
struct w1_netlink_msg msg;

dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__,
sl->name, sl);

- list_del(&sl->w1_slave_entry);
-
memset(&msg, 0, sizeof(msg));
memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id));
msg.type = W1_SLAVE_REMOVE;
@@ -804,7 +812,6 @@ int w1_unref_slave(struct w1_slave *sl)
kfree(sl);
}
atomic_dec(&dev->refcnt);
- mutex_unlock(&dev->list_mutex);
return refcnt;
}


--
2.56.0