[Devel] [PATCH vz10 14/24] sched/ve: read task ve locklessly in update_sched_lat() under rq->lock
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Fri Jul 24 16:38:12 MSK 2026
On 7/6/26 12:59, Konstantin Khorenko wrote:
> update_sched_lat() runs from the schedstats hot path
> (__update_stats_wait_end(), reached via set_next_entity() ->
> pick_next_task_fair() -> __schedule()) with rq->lock held - a raw
> spinlock. It used get_task_ve(), which takes task_lock() (a non-raw
> spinlock) plus a ve reference. Acquiring a non-raw spinlock under a raw
> spinlock is an invalid lock-nesting context; on a PROVE_RAW_LOCK_NESTING
> kernel lockdep reports it under load:
>
> BUG: Invalid wait context
> stress-ng-cache/... is trying to lock: &p->alloc_lock ... {3:3}
> 1 lock held: &rq->__lock ... {2:2}
> __update_stats_wait_end <- set_next_entity <- pick_next_task_fair
> <- __pick_next_task <- __schedule
>
> update_sched_lat() only needs the task's ve to bump a per-CPU latency
> counter, and the task is on this rq so its ve cannot be freed under us.
> Read it locklessly with task_ve() instead of taking task_lock() and a ve
> reference via get_task_ve(); this also drops the refcount churn from the
> scheduler hot path.
>
> Fixes: 68bbb887a36b ("ve: Switch from ->task_ve to *task_ve() helpers")
> Feature: ve: ve generic structures
> https://virtuozzo.atlassian.net/browse/VSTOR-137234
> Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
> ---
> kernel/sched/stats.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/sched/stats.c b/kernel/sched/stats.c
> index 08ed7c8a9c4a..27feb9c8b880 100644
> --- a/kernel/sched/stats.c
> +++ b/kernel/sched/stats.c
> @@ -23,7 +23,16 @@ void __update_stats_wait_start(struct rq *rq, struct task_struct *p,
> static inline void update_sched_lat(struct task_struct *t, u64 delta, u64 now)
> {
> #ifdef CONFIG_VE
> - struct ve_struct *ve __free(put_ve) = get_task_ve(t);
> + /*
> + * Called from the schedstats hot path (__update_stats_wait_end())
> + * with rq->lock held, i.e. under a raw spinlock. get_task_ve() takes
> + * task_lock() (a non-raw spinlock), which lockdep flags as an invalid
> + * wait context (BUG: Invalid wait context) under PROVE_RAW_LOCK_NESTING.
> + * We only need @t's ve to bump a per-CPU latency counter, and @t is
> + * on this rq (its ve cannot go away under us), so read it locklessly
> + * with task_ve() instead of taking task_lock() and a ve reference.
> + */
This comment violates https://docs.kernel.org/process/coding-style.html#commenting
We should not explain the history e.g. "It failed before with BUG:..." We should
only say what is important now.
E.g. something like:
Get task's ve lockles without reference. We can do it because we are in
scheduler and it's guaranteed that the task is not running at the moment and
ve is a stable pointer. We need to do it because we are in the raw spinlock
context and can't take non-raw tasklist lock here.
> + struct ve_struct *ve = task_ve(t);
>
> KSTAT_LAT_PCPU_ADD(&kstat_glob.sched_lat, delta);
> KSTAT_LAT_PCPU_ADD(&ve->sched_lat_ve, delta);
--
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.
More information about the Devel
mailing list