diff --git a/fs/pidfs.c b/fs/pidfs.c index 257924f379af..80675b6bf884 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -89,23 +89,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. */ @@ -113,6 +96,7 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts) { struct pid *pid = pidfd_pid(file); bool thread = file->f_flags & PIDFD_THREAD; + struct task_struct *task; __poll_t poll_flags = 0; poll_wait(file, &pid->wait_pidfd, pts); @@ -120,8 +104,12 @@ 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)) - poll_flags = EPOLLIN | EPOLLRDNORM; + guard(rcu)(); + 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; return poll_flags; } diff --git a/kernel/pid.c b/kernel/pid.c index 58cbb6c7a3ec..d15fb6c3fe89 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -355,6 +355,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;