<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=koi8-r">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
It seems the original patch mail didn't go through.</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>От:</b> Konstantin Khorenko <khorenko@virtuozzo.com><br>
<b>Отправлено:</b> 28 октября 2025 г. 19:15<br>
<b>Кому:</b> Pavel Tikhomirov <ptikhomirov@virtuozzo.com>; Aleksei Oladko <aleksey.oladko@virtuozzo.com><br>
<b>Копия:</b> devel@openvz.org <devel@openvz.org><br>
<b>Тема:</b> Re: [Devel] [RESEND PATCH vz10 1/2] ve/kthread: fix race when work can be added to stopped kthread worker #VSTOR-106887</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">And where is the original patch mail?<br>
<br>
i don't see it in the list:<br>
<a href="https://lists.openvz.org/pipermail/devel/2025-October/date.html">https://lists.openvz.org/pipermail/devel/2025-October/date.html</a><br>
<br>
--<br>
Best regards,<br>
<br>
Konstantin Khorenko,<br>
Virtuozzo Linux Kernel Team<br>
<br>
On 10/21/25 12:25, Pavel Tikhomirov wrote:<br>
> Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com><br>
> <br>
> (for both patches, will merge it tomorrow)<br>
> <br>
> On 10/21/25 17:18, Aleksei Oladko wrote:<br>
>> This patch reintroduces the feature from commit 6d43ed1, which was reverted<br>
>> due to a scenario where a hanging user-mode-helper in one container could<br>
>> block the startup of other containers.<br>
>><br>
>> Race can be reproduced with steps below:<br>
>><br>
>> 1) Add these test patch to increase the race probability:<br>
>><br>
>> kernel/umh.c<br>
>> @@ -455,6 +456,8 @@ int call_usermodehelper_exec_ve(struct ve_struct *ve,<br>
>>                   sub_info->queue = call_usermodehelper_queue_ve;<br>
>>                   kthread_init_work(&sub_info->ve_work,<br>
>>                                     call_usermodehelper_exec_work_ve);<br>
>> +             while (VE_IS_RUNNING(ve))<br>
>> +                       cond_resched();<br>
>>           } else {<br>
>>                   sub_info->queue = call_usermodehelper_queue;<br>
>>                   INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);<br>
>><br>
>> 2) Set corepattern to type pipe in CT:<br>
>><br>
>> echo "|/bin/dd of=/vz/coredumps/core-%e-%t.%p" > /proc/sys/kernel/core_pattern<br>
>><br>
>> 3) Generate high CPU load on all cores using tools like stress-ng<br>
>><br>
>> stress-ng --futex $(nproc) --timeout 60s<br>
>><br>
>> 4) Produce a segfault inside a container and next try to stop the<br>
>> container killing init.<br>
>><br>
>> Coredump creates "dd" work and ads it to ve_umh_worker, which is already<br>
>> stopped and will never handle these work, and our work will hang<br>
>> forever, and container will never stop:<br>
>><br>
>> [<0>] call_usermodehelper_exec+0x168/0x1a0<br>
>> [<0>] call_usermodehelper_exec_ve+0x96/0xe0<br>
>> [<0>] do_coredump+0x60f/0xf40<br>
>> [<0>] get_signal+0x834/0x960<br>
>> [<0>] arch_do_signal_or_restart+0x29/0xf0<br>
>> [<0>] irqentry_exit_to_user_mode+0x12e/0x1a0<br>
>> [<0>] asm_exc_page_fault+0x26/0x30<br>
>><br>
>> Fix is:<br>
>><br>
>> 1) Before calling call_usermodehelper_exec for a ve, check that<br>
>> the ve is running before adding work.<br>
>><br>
>> 2) Add separate hepler counters for each ve.<br>
>><br>
>> 3) In ve_stop_ns, after setting the VE_STATE_STOPPING state,<br>
>> wait for the running helpers count to reach 0 before stopping umh.<br>
>><br>
>> There are 2 cases:<br>
>><br>
>> If the call_usermodehelper_exec_ve thread reaches call_usermodehelper_exec<br>
>> after ve_stop_ns started wait_khelpers, it will already see that<br>
>> the ve is no in the running state and will no queue the work.<br>
>><br>
>> If the call_usermodehelper_exec_ve thread reaches call_usermodehelper_exec<br>
>> before the VE_STATE_STOPPING state is set for the ve, then ve_stop_ns will<br>
>> wait in wait_khelpers until all works have been queued. After that all<br>
>> queued works will be processed in kthread_flush_worker.<br>
>><br>
>> <a href="https://virtuozzo.atlassian.net/browse/VSTOR-106887">https://virtuozzo.atlassian.net/browse/VSTOR-106887</a><br>
>><br>
>> Signed-off-by: Aleksei Oladko <aleksey.oladko@virtuozzo.com><br>
>> ---<br>
>>    include/linux/umh.h |  2 ++<br>
>>    include/linux/ve.h  |  2 ++<br>
>>    kernel/umh.c        | 27 ++++++++++++++++++++++++++-<br>
>>    kernel/ve/ve.c      |  5 +++++<br>
>>    4 files changed, 35 insertions(+), 1 deletion(-)<br>
>><br>
>> diff --git a/include/linux/umh.h b/include/linux/umh.h<br>
>> index 5647f1e64e39..35fc4023df74 100644<br>
>> --- a/include/linux/umh.h<br>
>> +++ b/include/linux/umh.h<br>
>> @@ -56,6 +56,8 @@ extern int<br>
>>    call_usermodehelper_exec_ve(struct ve_struct *ve,<br>
>>                           struct subprocess_info *info, int wait);<br>
>>    <br>
>> +extern void wait_khelpers(struct ve_struct *ve);<br>
>> +<br>
>>    #else /* CONFIG_VE */<br>
>>    <br>
>>    #define call_usermodehelper_ve(ve, ...)           \<br>
>> diff --git a/include/linux/ve.h b/include/linux/ve.h<br>
>> index e944132f972f..37562dff25aa 100644<br>
>> --- a/include/linux/ve.h<br>
>> +++ b/include/linux/ve.h<br>
>> @@ -93,6 +93,8 @@ struct ve_struct {<br>
>>       struct kthread_worker   *kthreadd_worker;<br>
>>       struct task_struct      *kthreadd_task;<br>
>>    <br>
>> +    atomic_t                umh_running_helpers;<br>
>> +    struct wait_queue_head  umh_helpers_waitq;<br>
>>       struct kthread_worker   umh_worker;<br>
>>       struct task_struct      *umh_task;<br>
>>    <br>
>> diff --git a/kernel/umh.c b/kernel/umh.c<br>
>> index bd49c108eb90..699403efe382 100644<br>
>> --- a/kernel/umh.c<br>
>> +++ b/kernel/umh.c<br>
>> @@ -447,9 +447,30 @@ static void call_usermodehelper_exec_work_ve(struct kthread_work *work)<br>
>>       }<br>
>>    }<br>
>>    <br>
>> +static void umh_running_helpers_inc(struct ve_struct *ve)<br>
>> +{<br>
>> +    atomic_inc(&ve->umh_running_helpers);<br>
>> +    smp_mb__after_atomic();<br>
>> +}<br>
>> +<br>
>> +static void umh_running_helpers_dec(struct ve_struct *ve)<br>
>> +{<br>
>> +    if (atomic_dec_and_test(&ve->umh_running_helpers))<br>
>> +            wake_up(&ve->umh_helpers_waitq);<br>
>> +}<br>
>> +<br>
>> +void wait_khelpers(struct ve_struct *ve)<br>
>> +{<br>
>> +    wait_event(ve->umh_helpers_waitq,<br>
>> +               atomic_read(&ve->umh_running_helpers) == 0);<br>
>> +}<br>
>> +EXPORT_SYMBOL(wait_khelpers);<br>
>> +<br>
>>    int call_usermodehelper_exec_ve(struct ve_struct *ve,<br>
>>                               struct subprocess_info *sub_info, int wait)<br>
>>    {<br>
>> +    int ret = -EBUSY;<br>
>> +<br>
>>       if (!ve_is_super(ve)) {<br>
>>               sub_info->ve = ve;<br>
>>               sub_info->queue = call_usermodehelper_queue_ve;<br>
>> @@ -460,7 +481,11 @@ int call_usermodehelper_exec_ve(struct ve_struct *ve,<br>
>>               INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);<br>
>>       }<br>
>>    <br>
>> -    return call_usermodehelper_exec(sub_info, wait);<br>
>> +    umh_running_helpers_inc(ve);<br>
>> +    if (VE_IS_RUNNING(ve))<br>
>> +            ret = call_usermodehelper_exec(sub_info, wait);<br>
>> +    umh_running_helpers_dec(ve);<br>
>> +    return ret;<br>
>>    }<br>
>>    EXPORT_SYMBOL(call_usermodehelper_exec_ve);<br>
>>    <br>
>> diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c<br>
>> index cb77f7b7e4cd..d5dc15942ab5 100644<br>
>> --- a/kernel/ve/ve.c<br>
>> +++ b/kernel/ve/ve.c<br>
>> @@ -88,6 +88,8 @@ struct ve_struct ve0 = {<br>
>>       .nd_neigh_nr            = ATOMIC_INIT(0),<br>
>>       .mnt_nr                 = ATOMIC_INIT(0),<br>
>>       .meminfo_val            = VE_MEMINFO_SYSTEM,<br>
>> +    .umh_running_helpers    = ATOMIC_INIT(0),<br>
>> +    .umh_helpers_waitq      = __WAIT_QUEUE_HEAD_INITIALIZER(ve0.umh_helpers_waitq),<br>
>>       .vdso_64                = (struct vdso_image*)&vdso_image_64,<br>
>>       .vdso_32                = (struct vdso_image*)&vdso_image_32,<br>
>>       .release_list_lock      = __SPIN_LOCK_UNLOCKED(<br>
>> @@ -480,6 +482,8 @@ static int ve_start_umh(struct ve_struct *ve)<br>
>>    {<br>
>>       struct task_struct *task;<br>
>>    <br>
>> +    atomic_set(&ve->umh_running_helpers, 0);<br>
>> +    init_waitqueue_head(&ve->umh_helpers_waitq);<br>
>>       kthread_init_worker(&ve->umh_worker);<br>
>>    <br>
>>       task = kthread_create_on_node_ve_flags(ve, 0, kthread_worker_fn,<br>
>> @@ -814,6 +818,7 @@ void ve_stop_ns(struct pid_namespace *pid_ns)<br>
>>        */<br>
>>       up_write(&ve->op_sem);<br>
>>    <br>
>> +    wait_khelpers(ve);<br>
>>       /*<br>
>>        * release_agent works on top of umh_worker, so we must make sure, that<br>
>>        * ve workqueue is stopped first.<br>
> <br>
<br>
</div>
</span></font></div>
</body>
</html>