perf daemon: Force waipid for all session on SIGCHLD delivery

If we don't process SIGCHLD before another comes, we will see just one
SIGCHLD as a result. In this case current code will miss exit
notification for a session and wait forever.

Adding extra waitpid check for all sessions when SIGCHLD is received, to
make sure we don't miss any session exit.

Also fix close condition for signal_fd.

Reported-by: Ian Rogers <irogers@google.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/20210320221013.1619613-1-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Jiri Olsa 2021-03-20 23:10:12 +01:00 committed by Arnaldo Carvalho de Melo
parent 1a096ae46e
commit 1833b64fee

View File

@ -402,35 +402,42 @@ static pid_t handle_signalfd(struct daemon *daemon)
int status;
pid_t pid;
/*
* Take signal fd data as pure signal notification and check all
* the sessions state. The reason is that multiple signals can get
* coalesced in kernel and we can receive only single signal even
* if multiple SIGCHLD were generated.
*/
err = read(daemon->signal_fd, &si, sizeof(struct signalfd_siginfo));
if (err != sizeof(struct signalfd_siginfo))
if (err != sizeof(struct signalfd_siginfo)) {
pr_err("failed to read signal fd\n");
return -1;
}
list_for_each_entry(session, &daemon->sessions, list) {
if (session->pid != (int) si.ssi_pid)
if (session->pid == -1)
continue;
pid = waitpid(session->pid, &status, 0);
if (pid == session->pid) {
if (WIFEXITED(status)) {
pr_info("session '%s' exited, status=%d\n",
session->name, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
pr_info("session '%s' killed (signal %d)\n",
session->name, WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
pr_info("session '%s' stopped (signal %d)\n",
session->name, WSTOPSIG(status));
} else {
pr_info("session '%s' Unexpected status (0x%x)\n",
session->name, status);
}
pid = waitpid(session->pid, &status, WNOHANG);
if (pid <= 0)
continue;
if (WIFEXITED(status)) {
pr_info("session '%s' exited, status=%d\n",
session->name, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
pr_info("session '%s' killed (signal %d)\n",
session->name, WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
pr_info("session '%s' stopped (signal %d)\n",
session->name, WSTOPSIG(status));
} else {
pr_info("session '%s' Unexpected status (0x%x)\n",
session->name, status);
}
session->state = KILL;
session->pid = -1;
return pid;
}
return 0;
@ -443,7 +450,6 @@ static int daemon_session__wait(struct daemon_session *session, struct daemon *d
.fd = daemon->signal_fd,
.events = POLLIN,
};
pid_t wpid = 0, pid = session->pid;
time_t start;
start = time(NULL);
@ -452,7 +458,7 @@ static int daemon_session__wait(struct daemon_session *session, struct daemon *d
int err = poll(&pollfd, 1, 1000);
if (err > 0) {
wpid = handle_signalfd(daemon);
handle_signalfd(daemon);
} else if (err < 0) {
perror("failed: poll\n");
return -1;
@ -460,7 +466,7 @@ static int daemon_session__wait(struct daemon_session *session, struct daemon *d
if (start + secs < time(NULL))
return -1;
} while (wpid != pid);
} while (session->pid != -1);
return 0;
}
@ -1344,7 +1350,7 @@ out:
close(sock_fd);
if (conf_fd != -1)
close(conf_fd);
if (conf_fd != -1)
if (signal_fd != -1)
close(signal_fd);
pr_info("daemon exited\n");