summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-daemon.c
diff options
context:
space:
mode:
authorJiri Olsa <jolsa@kernel.org>2021-02-08 21:08:53 +0100
committerArnaldo Carvalho de Melo <acme@redhat.com>2021-02-11 10:02:54 -0300
commit12c1a415eb8dc258a33f04d6a4df288e0cc4e200 (patch)
tree2642667e7a1ce80dbd6368c7629898c7cc3b36e1 /tools/perf/builtin-daemon.c
parent88adb1194cc51a4d3f1930ddd6c8f0b0f9f3a936 (diff)
perf daemon: Add signalfd support
Use a signalfd fd to track SIGCHLD signals as notifications for perf session termination. This way we don't need to actively check for child status, being notified if there's change. Suggested-by: Alexei Budankov <abudankov@huawei.com> Signed-off-by: Jiri Olsa <jolsa@kernel.org> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Michael Petlan <mpetlan@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: https://lore.kernel.org/r/20210208200908.1019149-10-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-daemon.c')
-rw-r--r--tools/perf/builtin-daemon.c161
1 files changed, 155 insertions, 6 deletions
diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c
index 2241d0e36d92..c77668b50023 100644
--- a/tools/perf/builtin-daemon.c
+++ b/tools/perf/builtin-daemon.c
@@ -9,6 +9,7 @@
#include <string.h>
#include <signal.h>
#include <stdlib.h>
+#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
@@ -18,6 +19,8 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <sys/wait.h>
#include <poll.h>
#include <sys/stat.h>
#include "builtin.h"
@@ -83,6 +86,7 @@ struct daemon {
struct list_head sessions;
FILE *out;
char perf[PATH_MAX];
+ int signal_fd;
};
static struct daemon __daemon = {
@@ -368,6 +372,116 @@ static int daemon_session__run(struct daemon_session *session,
return -1;
}
+static pid_t handle_signalfd(struct daemon *daemon)
+{
+ struct daemon_session *session;
+ struct signalfd_siginfo si;
+ ssize_t err;
+ int status;
+ pid_t pid;
+
+ err = read(daemon->signal_fd, &si, sizeof(struct signalfd_siginfo));
+ if (err != sizeof(struct signalfd_siginfo))
+ return -1;
+
+ list_for_each_entry(session, &daemon->sessions, list) {
+
+ if (session->pid != (int) si.ssi_pid)
+ 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);
+ }
+ }
+
+ session->state = KILL;
+ session->pid = -1;
+ return pid;
+ }
+
+ return 0;
+}
+
+static int daemon_session__wait(struct daemon_session *session, struct daemon *daemon,
+ int secs)
+{
+ struct pollfd pollfd = {
+ .fd = daemon->signal_fd,
+ .events = POLLIN,
+ };
+ pid_t wpid = 0, pid = session->pid;
+ time_t start;
+
+ start = time(NULL);
+
+ do {
+ int err = poll(&pollfd, 1, 1000);
+
+ if (err > 0) {
+ wpid = handle_signalfd(daemon);
+ } else if (err < 0) {
+ perror("failed: poll\n");
+ return -1;
+ }
+
+ if (start + secs < time(NULL))
+ return -1;
+ } while (wpid != pid);
+
+ return 0;
+}
+
+static bool daemon__has_alive_session(struct daemon *daemon)
+{
+ struct daemon_session *session;
+
+ list_for_each_entry(session, &daemon->sessions, list) {
+ if (session->pid != -1)
+ return true;
+ }
+
+ return false;
+}
+
+static int daemon__wait(struct daemon *daemon, int secs)
+{
+ struct pollfd pollfd = {
+ .fd = daemon->signal_fd,
+ .events = POLLIN,
+ };
+ time_t start;
+
+ start = time(NULL);
+
+ do {
+ int err = poll(&pollfd, 1, 1000);
+
+ if (err > 0) {
+ handle_signalfd(daemon);
+ } else if (err < 0) {
+ perror("failed: poll\n");
+ return -1;
+ }
+
+ if (start + secs < time(NULL))
+ return -1;
+ } while (daemon__has_alive_session(daemon));
+
+ return 0;
+}
+
static int setup_server_socket(struct daemon *daemon)
{
struct sockaddr_un addr;
@@ -493,9 +607,14 @@ static int daemon_session__signal(struct daemon_session *session, int sig)
return kill(session->pid, sig);
}
-static void daemon_session__kill(struct daemon_session *session)
+static void daemon_session__kill(struct daemon_session *session,
+ struct daemon *daemon)
{
daemon_session__signal(session, SIGTERM);
+ if (daemon_session__wait(session, daemon, 10)) {
+ daemon_session__signal(session, SIGKILL);
+ daemon_session__wait(session, daemon, 10);
+ }
}
static void daemon__signal(struct daemon *daemon, int sig)
@@ -523,6 +642,10 @@ static void daemon_session__remove(struct daemon_session *session)
static void daemon__kill(struct daemon *daemon)
{
daemon__signal(daemon, SIGTERM);
+ if (daemon__wait(daemon, 10)) {
+ daemon__signal(daemon, SIGKILL);
+ daemon__wait(daemon, 10);
+ }
}
static void daemon__exit(struct daemon *daemon)
@@ -549,7 +672,7 @@ static int daemon__reconfig(struct daemon *daemon)
/* Remove session. */
if (session->state == KILL) {
if (session->pid > 0) {
- daemon_session__kill(session);
+ daemon_session__kill(session, daemon);
pr_info("reconfig: session '%s' killed\n", session->name);
}
daemon_session__remove(session);
@@ -558,7 +681,7 @@ static int daemon__reconfig(struct daemon *daemon)
/* Reconfig session. */
if (session->pid > 0) {
- daemon_session__kill(session);
+ daemon_session__kill(session, daemon);
pr_info("reconfig: session '%s' killed\n", session->name);
}
if (daemon_session__run(session, daemon))
@@ -724,6 +847,20 @@ static int go_background(struct daemon *daemon)
return 0;
}
+static int setup_signalfd(struct daemon *daemon)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ return -1;
+
+ daemon->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ return daemon->signal_fd;
+}
+
static int __cmd_start(struct daemon *daemon, struct option parent_options[],
int argc, const char **argv)
{
@@ -733,8 +870,8 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
OPT_PARENT(parent_options),
OPT_END()
};
- int sock_fd = -1, conf_fd = -1;
- int sock_pos, file_pos;
+ int sock_fd = -1, conf_fd = -1, signal_fd = -1;
+ int sock_pos, file_pos, signal_pos;
struct fdarray fda;
int err = 0;
@@ -766,7 +903,7 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
pr_info("daemon started (pid %d)\n", getpid());
- fdarray__init(&fda, 2);
+ fdarray__init(&fda, 3);
sock_fd = setup_server_socket(daemon);
if (sock_fd < 0)
@@ -776,6 +913,10 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
if (conf_fd < 0)
goto out;
+ signal_fd = setup_signalfd(daemon);
+ if (signal_fd < 0)
+ goto out;
+
sock_pos = fdarray__add(&fda, sock_fd, POLLIN|POLLERR|POLLHUP, 0);
if (sock_pos < 0)
goto out;
@@ -784,6 +925,10 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
if (file_pos < 0)
goto out;
+ signal_pos = fdarray__add(&fda, signal_fd, POLLIN|POLLERR|POLLHUP, 0);
+ if (signal_pos < 0)
+ goto out;
+
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
@@ -797,6 +942,8 @@ static int __cmd_start(struct daemon *daemon, struct option parent_options[],
err = handle_server_socket(daemon, sock_fd);
if (fda.entries[file_pos].revents & POLLIN)
err = handle_config_changes(daemon, conf_fd, &reconfig);
+ if (fda.entries[signal_pos].revents & POLLIN)
+ err = handle_signalfd(daemon) < 0;
if (reconfig)
err = setup_server_config(daemon);
@@ -813,6 +960,8 @@ out:
close(sock_fd);
if (conf_fd != -1)
close(conf_fd);
+ if (conf_fd != -1)
+ close(signal_fd);
pr_info("daemon exited\n");
fclose(daemon->out);