From mboxrd@z Thu Jan 1 00:00:00 1970 From: Pavel Tikhomirov Date: Thu, 13 Aug 2026 18:41:54 +0200 Subject: Re: [Devel] [PATCH VZ10] proc connector: pin task VE for the exit event notification In-Reply-To: <20260813-connectors-v1-1-ea5e7533e347@virtuozzo.com> References: <20260813-connectors-v1-1-ea5e7533e347@virtuozzo.com> Message-ID: List-Id: Reviewed-by: Pavel Tikhomirov On 8/13/26 13:13, Vasileios Almpanis wrote: > The exit event is reported after exit_notify(), so the parent might have > been woken up and reaped the exiting task via wait() -> release_task() > -> exit_ve_namespace(), which resets tsk->task_ve to ve0. If that wins > the race against the exiting task then the event will only be delivered > to the host VE listeners and the in-VE listeners will be skipped. For a > task inside a container this means the listener never receives an exit > event. > > The issue is caught by the LTP suite_kernel_misc.exec.cn_pec_sh test: > pec_listener terminates upon receiving the exit event of the pid given > via -p. When the lost exit event is a child's one, the test fails: > > cn_pec 3 TFAIL: Event was not detected by the event listener: > exit pid: 58388 exit_code: 0 exit_signal: 17 > > and when it is the event generator's own exit event, the listener > polls the netlink socket forever and the test hangs until the LTP > timeout kills it (~8h on coverage kernels): > > 22:51:37 cn_pec 2 TINFO: Testing exec event (nevents=10) > 07:11:37 Test timed out, sending SIGTERM! > > The race was captured using kprobes on the connector send path: > p:cnp/pexit proc_exit_connector task=$arg1:x64 > p:cnp/vexit exit_ve_namespace task=$arg1:x64 > p:cnp/pevcve proc_event_connector_ve what=$arg3:u32 > r:cnp/cnsend cn_netlink_send_mult_ve ret=$retval:s64 > > A normal exit looks like: > pexit -> pevcve(ve) -> cnsend ret=0 -> pevcve(ve0) > > The lost event (task 0xffff89a506d73980 is the exiting child, reaped by > its parent pid 465820 in between): > > 465831 [001] pexit: (proc_exit_connector) task=0xffff89a506d73980 > 465820 [002] vexit: (exit_ve_namespace) task=0xffff89a506d73980 > 465831 [001] pevcve: (proc_event_connector_ve) what=2147483648 > > Only one proc_event_connector_ve() call fires (ve0, no listeners) and > cn_netlink_send_mult_ve() is never reached: the event is dropped. > > This is the same race window that commit c565cc211694 ("proc > connector: report proper pid/tgid of an exited process") closed for > the task pid/tgid, but nothing pins the VE. > > Reproducer (fails within ~50 iterations in a CT on a coverage kernel): > > cd /opt/ltp/testcases/bin/ > export PATH=$PATH:/opt/ltp/testcases/bin > for i in $(seq 1 1024); do cn_pec.sh >/dev/null 2>&1 || break; done > > Solve this the same way. Pin the VE in do_exit() before exit_notify() > is called and use it in proc_exit_connector() instead of re-reading > task->task_ve. > > https://virtuozzo.atlassian.net/browse/VSTOR-140421 > Fixes: 95fa2f096b72 ("ve: Introduce VE namespace") > Signed-off-by: Vasileios Almpanis > > Feature: ve: ve generic structures > --- > drivers/connector/cn_proc.c | 10 ++++++++-- > include/linux/cn_proc.h | 7 +++++-- > kernel/exit.c | 5 ++++- > 3 files changed, 17 insertions(+), 5 deletions(-) > > diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c > index d41c8aca1866..cd75f1820914 100644 > --- a/drivers/connector/cn_proc.c > +++ b/drivers/connector/cn_proc.c > @@ -362,9 +362,15 @@ static bool fill_exit_event(struct proc_event *ev, struct ve_struct *ve, > return true; > } > > -void proc_exit_connector(struct task_struct *task, struct pids *pids) > +void proc_exit_connector(struct task_struct *task, struct pids *pids, > + struct ve_struct *ve) > { > - proc_event_connector(task, PROC_EVENT_EXIT, (long)pids, fill_exit_event); > + if (!ve_is_super(ve)) > + proc_event_connector_ve(task, ve, PROC_EVENT_EXIT, (long)pids, > + fill_exit_event); > + > + proc_event_connector_ve(task, get_ve0(), PROC_EVENT_EXIT, (long)pids, > + fill_exit_event); > } > > /* > diff --git a/include/linux/cn_proc.h b/include/linux/cn_proc.h > index 9701c13d82df..0e545774aa81 100644 > --- a/include/linux/cn_proc.h > +++ b/include/linux/cn_proc.h > @@ -17,6 +17,7 @@ > #ifndef CN_PROC_H > #define CN_PROC_H > > +#include > #include > > /* > @@ -36,7 +37,8 @@ void proc_sid_connector(struct task_struct *task); > void proc_ptrace_connector(struct task_struct *task, int which_id); > void proc_comm_connector(struct task_struct *task); > void proc_coredump_connector(struct task_struct *task); > -void proc_exit_connector(struct task_struct *task, struct pids *pids); > +void proc_exit_connector(struct task_struct *task, struct pids *pids, > + struct ve_struct *ve); > #else > static inline void proc_fork_connector(struct task_struct *task) > {} > @@ -61,7 +63,8 @@ static inline void proc_ptrace_connector(struct task_struct *task, > static inline void proc_coredump_connector(struct task_struct *task) > {} > > -static inline void proc_exit_connector(struct task_struct *task, struct pids *pids) > +static inline void proc_exit_connector(struct task_struct *task, struct pids *pids, > + struct ve_struct *ve) > {} > #endif /* CONFIG_PROC_EVENTS */ > #endif /* CN_PROC_H */ > diff --git a/kernel/exit.c b/kernel/exit.c > index 448a734270a7..94d9bddae2b8 100644 > --- a/kernel/exit.c > +++ b/kernel/exit.c > @@ -936,6 +936,7 @@ void __noreturn do_exit(long code) > struct task_struct *tsk = current; > int group_dead; > struct pids pids; > + struct ve_struct *ve; > > WARN_ON(irqs_disabled()); > > @@ -1021,8 +1022,10 @@ void __noreturn do_exit(long code) > exit_tasks_rcu_start(); > pids.pid = get_pid(task_pid(tsk)); > pids.tgid = get_pid(task_tgid(tsk)); > + ve = get_task_ve(tsk); > exit_notify(tsk, group_dead); > - proc_exit_connector(tsk, &pids); > + proc_exit_connector(tsk, &pids, ve); > + put_ve(ve); > put_pid(pids.tgid); > put_pid(pids.pid); > mpol_put_task_policy(tsk); > > --- > base-commit: 209f11f2c454a88761fb8f0a820fa22b6feae720 > change-id: 20260813-connectors-3571be4ae57b > -- Best regards, Pavel Tikhomirov Senior Software Developer, Virtuozzo.