forked from luck/tmp_suning_uos_patched
psi: Fix race between psi_trigger_create/destroy
[ Upstream commit 8f91efd870ea5d8bc10b0fcc9740db51cd4c0c83 ]
Race detected between psi_trigger_destroy/create as shown below, which
cause panic by accessing invalid psi_system->poll_wait->wait_queue_entry
and psi_system->poll_timer->entry->next. Under this modification, the
race window is removed by initialising poll_wait and poll_timer in
group_init which are executed only once at beginning.
psi_trigger_destroy() psi_trigger_create()
mutex_lock(trigger_lock);
rcu_assign_pointer(poll_task, NULL);
mutex_unlock(trigger_lock);
mutex_lock(trigger_lock);
if (!rcu_access_pointer(group->poll_task)) {
timer_setup(poll_timer, poll_timer_fn, 0);
rcu_assign_pointer(poll_task, task);
}
mutex_unlock(trigger_lock);
synchronize_rcu();
del_timer_sync(poll_timer); <-- poll_timer has been reinitialized by
psi_trigger_create()
So, trigger_lock/RCU correctly protects destruction of
group->poll_task but misses this race affecting poll_timer and
poll_wait.
Fixes: 461daba06b
("psi: eliminate kthread_worker from psi trigger scheduling mechanism")
Co-developed-by: ziwei.dai <ziwei.dai@unisoc.com>
Signed-off-by: ziwei.dai <ziwei.dai@unisoc.com>
Co-developed-by: ke.wang <ke.wang@unisoc.com>
Signed-off-by: ke.wang <ke.wang@unisoc.com>
Signed-off-by: Zhaoyang Huang <zhaoyang.huang@unisoc.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Suren Baghdasaryan <surenb@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Link: https://lkml.kernel.org/r/1623371374-15664-1-git-send-email-huangzhaoyang@gmail.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
8d7debe744
commit
6bfcb61789
|
@ -179,6 +179,8 @@ struct psi_group psi_system = {
|
|||
|
||||
static void psi_avgs_work(struct work_struct *work);
|
||||
|
||||
static void poll_timer_fn(struct timer_list *t);
|
||||
|
||||
static void group_init(struct psi_group *group)
|
||||
{
|
||||
int cpu;
|
||||
|
@ -198,6 +200,8 @@ static void group_init(struct psi_group *group)
|
|||
memset(group->polling_total, 0, sizeof(group->polling_total));
|
||||
group->polling_next_update = ULLONG_MAX;
|
||||
group->polling_until = 0;
|
||||
init_waitqueue_head(&group->poll_wait);
|
||||
timer_setup(&group->poll_timer, poll_timer_fn, 0);
|
||||
rcu_assign_pointer(group->poll_task, NULL);
|
||||
}
|
||||
|
||||
|
@ -1126,9 +1130,7 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group,
|
|||
return ERR_CAST(task);
|
||||
}
|
||||
atomic_set(&group->poll_wakeup, 0);
|
||||
init_waitqueue_head(&group->poll_wait);
|
||||
wake_up_process(task);
|
||||
timer_setup(&group->poll_timer, poll_timer_fn, 0);
|
||||
rcu_assign_pointer(group->poll_task, task);
|
||||
}
|
||||
|
||||
|
@ -1180,6 +1182,7 @@ static void psi_trigger_destroy(struct kref *ref)
|
|||
group->poll_task,
|
||||
lockdep_is_held(&group->trigger_lock));
|
||||
rcu_assign_pointer(group->poll_task, NULL);
|
||||
del_timer(&group->poll_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1192,17 +1195,14 @@ static void psi_trigger_destroy(struct kref *ref)
|
|||
*/
|
||||
synchronize_rcu();
|
||||
/*
|
||||
* Destroy the kworker after releasing trigger_lock to prevent a
|
||||
* Stop kthread 'psimon' after releasing trigger_lock to prevent a
|
||||
* deadlock while waiting for psi_poll_work to acquire trigger_lock
|
||||
*/
|
||||
if (task_to_destroy) {
|
||||
/*
|
||||
* After the RCU grace period has expired, the worker
|
||||
* can no longer be found through group->poll_task.
|
||||
* But it might have been already scheduled before
|
||||
* that - deschedule it cleanly before destroying it.
|
||||
*/
|
||||
del_timer_sync(&group->poll_timer);
|
||||
kthread_stop(task_to_destroy);
|
||||
}
|
||||
kfree(t);
|
||||
|
|
Loading…
Reference in New Issue
Block a user