Skip to content

Commit

Permalink
w1: fix deadloop in __w1_remove_master_device()
Browse files Browse the repository at this point in the history
[ Upstream commit 25d5648 ]

I got a deadloop report while doing device(ds2482) add/remove test:

  [  162.241881] w1_master_driver w1_bus_master1: Waiting for w1_bus_master1 to become free: refcnt=1.
  [  163.272251] w1_master_driver w1_bus_master1: Waiting for w1_bus_master1 to become free: refcnt=1.
  [  164.296157] w1_master_driver w1_bus_master1: Waiting for w1_bus_master1 to become free: refcnt=1.
  ...

__w1_remove_master_device() can't return, because the dev->refcnt is not zero.

w1_add_master_device()			|
  w1_alloc_dev()			|
    atomic_set(&dev->refcnt, 2)		|
  kthread_run()				|
					|__w1_remove_master_device()
					|  kthread_stop()
  // KTHREAD_SHOULD_STOP is set,	|
  // threadfn(w1_process) won't be	|
  // called.				|
  kthread()				|
					|  // refcnt will never be 0, it's deadloop.
					|  while (atomic_read(&dev->refcnt)) {...}

After calling w1_add_master_device(), w1_process() is not really
invoked, before w1_process() starting, if kthread_stop() is called
in __w1_remove_master_device(), w1_process() will never be called,
the refcnt can not be decreased, then it causes deadloop in remove
function because of non-zero refcnt.

We need to make sure w1_process() is really started, so move the
set refcnt into w1_process() to fix this problem.

Fixes: 1da177e ("Linux-2.6.12-rc2")
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
Link: https://lore.kernel.org/r/20221205080434.3149205-1-yangyingliang@huawei.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Yang Yingliang authored and gregkh committed Feb 1, 2023
1 parent e75485f commit 8bc7d87
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 3 deletions.
2 changes: 2 additions & 0 deletions drivers/w1/w1.c
Expand Up @@ -1166,6 +1166,8 @@ int w1_process(void *data)
/* remainder if it woke up early */
unsigned long jremain = 0;

atomic_inc(&dev->refcnt);

for (;;) {

if (!jremain && dev->search_count) {
Expand Down
5 changes: 2 additions & 3 deletions drivers/w1/w1_int.c
Expand Up @@ -51,10 +51,9 @@ static struct w1_master *w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
dev->search_count = w1_search_count;
dev->enable_pullup = w1_enable_pullup;

/* 1 for w1_process to decrement
* 1 for __w1_remove_master_device to decrement
/* For __w1_remove_master_device to decrement
*/
atomic_set(&dev->refcnt, 2);
atomic_set(&dev->refcnt, 1);

INIT_LIST_HEAD(&dev->slist);
INIT_LIST_HEAD(&dev->async_list);
Expand Down

0 comments on commit 8bc7d87

Please sign in to comment.