aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleg Nesterov <[email protected]>2024-02-02 14:12:26 +0100
committerChristian Brauner <[email protected]>2024-02-02 14:57:53 +0100
commit43f0df54c96fa5abcab9df8649c1e52119bf0238 (patch)
tree3233889920853f7ea8355d41d33f084dad59f799
parent64bef697d33b75fc06c5789b3f8108680271529f (diff)
pidfd_poll: report POLLHUP when pid_task() == NULL
Add another wake_up_all(wait_pidfd) into __change_pid() and change pidfd_poll() to include EPOLLHUP if task == NULL. This allows to wait until the target process/thread is reaped. TODO: change do_notify_pidfd() to use the keyed wakeups. Signed-off-by: Oleg Nesterov <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
-rw-r--r--kernel/fork.c22
-rw-r--r--kernel/pid.c5
2 files changed, 12 insertions, 15 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index 1a9b91055916..aa08193d124f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2071,20 +2071,6 @@ static void pidfd_show_fdinfo(struct seq_file *m, struct file *f)
}
#endif
-static bool pidfd_task_exited(struct pid *pid, bool thread)
-{
- struct task_struct *task;
- bool exited;
-
- rcu_read_lock();
- task = pid_task(pid, PIDTYPE_PID);
- exited = !task ||
- (READ_ONCE(task->exit_state) && (thread || thread_group_empty(task)));
- rcu_read_unlock();
-
- return exited;
-}
-
/*
* Poll support for process exit notification.
*/
@@ -2092,6 +2078,7 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
{
struct pid *pid = file->private_data;
bool thread = file->f_flags & PIDFD_THREAD;
+ struct task_struct *task;
__poll_t poll_flags = 0;
poll_wait(file, &pid->wait_pidfd, pts);
@@ -2099,8 +2086,13 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
* Depending on PIDFD_THREAD, inform pollers when the thread
* or the whole thread-group exits.
*/
- if (pidfd_task_exited(pid, thread))
+ rcu_read_lock();
+ task = pid_task(pid, PIDTYPE_PID);
+ if (!task)
+ poll_flags = EPOLLIN | EPOLLRDNORM | EPOLLHUP;
+ else if (task->exit_state && (thread || thread_group_empty(task)))
poll_flags = EPOLLIN | EPOLLRDNORM;
+ rcu_read_unlock();
return poll_flags;
}
diff --git a/kernel/pid.c b/kernel/pid.c
index e11144466828..62461c7c82b8 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -349,6 +349,11 @@ static void __change_pid(struct task_struct *task, enum pid_type type,
hlist_del_rcu(&task->pid_links[type]);
*pid_ptr = new;
+ if (type == PIDTYPE_PID) {
+ WARN_ON_ONCE(pid_has_task(pid, PIDTYPE_PID));
+ wake_up_all(&pid->wait_pidfd);
+ }
+
for (tmp = PIDTYPE_MAX; --tmp >= 0; )
if (pid_has_task(pid, tmp))
return;