w1: fix deadloop in __w1_remove_master_device()
[ Upstream commit 25d5648802f12ae486076ceca5d7ddf1fef792b2 ]
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: 1da177e4c3
("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>
This commit is contained in:
parent
c9ada35340
commit
82867190ce
@ -1131,6 +1131,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) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user