Using signalfd fd for tracking SIGCHLD signals as
notification for perf session termination.

Suggested-by: Alexei Budankov <abudan...@huawei.com>
Signed-off-by: Jiri Olsa <jo...@kernel.org>
---
 tools/perf/builtin-daemon.c | 142 ++++++++++++++++++++++++++++++++++--
 1 file changed, 137 insertions(+), 5 deletions(-)

diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c
index 16b24c30722d..644f196d7f39 100644
--- a/tools/perf/builtin-daemon.c
+++ b/tools/perf/builtin-daemon.c
@@ -31,6 +31,7 @@
 #include "asm/bug.h"
 #include "util.h"
 #include <api/fs/fs.h>
+#include <sys/signalfd.h>
 
 #define SESSION_OUTPUT  "output"
 
@@ -58,6 +59,7 @@ struct daemon {
        struct list_head         sessions;
        FILE                    *out;
        char                     perf[PATH_MAX];
+       int                      signal_fd;
 };
 
 static struct daemon __daemon = {
@@ -317,9 +319,110 @@ static void session__remove(struct session *session)
        session__free(session);
 }
 
-static void session__kill(struct session *session)
+static pid_t handle_signalfd(struct daemon *daemon)
+{
+       struct signalfd_siginfo si;
+       struct session *session;
+       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 = SESSION_STATE__KILL;
+               session->pid = -1;
+               return pid;
+       }
+
+       return 0;
+}
+
+static int session__wait(struct 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 {
+               if (poll(&pollfd, 1, 1000))
+                       wpid = handle_signalfd(daemon);
+
+               if (start + secs < time(NULL))
+                       return -1;
+       } while (wpid != pid);
+
+       return 0;
+}
+
+static bool daemon__has_alive_session(struct daemon *daemon)
+{
+       struct session *session;
+
+       list_for_each_entry(session, &daemon->sessions, list) {
+               if (session->state == SESSION_STATE__OK)
+                       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 {
+               if (poll(&pollfd, 1, 1000))
+                       handle_signalfd(daemon);
+
+               if (start + secs < time(NULL))
+                       return -1;
+       } while (daemon__has_alive_session(daemon));
+
+       return 0;
+}
+
+static void session__kill(struct session *session, struct daemon *daemon)
 {
        session__signal(session, SIGTERM);
+       if (session__wait(session, daemon, 10)) {
+               session__signal(session, SIGKILL);
+               session__wait(session, daemon, 10);
+       }
 }
 
 static int daemon__reconfig(struct daemon *daemon)
@@ -334,7 +437,7 @@ static int daemon__reconfig(struct daemon *daemon)
                /* Remove session. */
                if (session->state == SESSION_STATE__KILL) {
                        if (session->pid > 0) {
-                               session__kill(session);
+                               session__kill(session, daemon);
                                pr_info("reconfig: session '%s' killed\n", 
session->name);
                        }
                        session__remove(session);
@@ -344,7 +447,7 @@ static int daemon__reconfig(struct daemon *daemon)
                /* Reconfig session. */
                pr_debug2("reconfig: session '%s' start\n", session->name);
                if (session->pid > 0) {
-                       session__kill(session);
+                       session__kill(session, daemon);
                        pr_info("reconfig: session '%s' killed\n", 
session->name);
                }
                if (session__run(session, daemon))
@@ -359,6 +462,10 @@ static int daemon__reconfig(struct daemon *daemon)
 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__free(struct daemon *daemon)
@@ -679,6 +786,20 @@ static int setup_config(struct daemon *daemon)
        return daemon->config_real ? 0 : -1;
 }
 
+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)
 {
@@ -688,7 +809,7 @@ static int __cmd_start(struct daemon *daemon, struct option 
parent_options[],
                OPT_PARENT(parent_options),
                OPT_END()
        };
-       int sock_pos, file_pos, sock_fd, conf_fd;
+       int sock_pos, file_pos, signal_pos, sock_fd, conf_fd, signal_fd;
        struct fdarray fda;
        int err = 0;
 
@@ -720,8 +841,12 @@ static int __cmd_start(struct daemon *daemon, struct 
option parent_options[],
        if (conf_fd < 0)
                return -1;
 
+       signal_fd = setup_signalfd(daemon);
+       if (signal_fd < 0)
+               return -1;
+
        /* socket, inotify */
-       fdarray__init(&fda, 2);
+       fdarray__init(&fda, 3);
 
        sock_pos = fdarray__add(&fda, sock_fd, POLLIN|POLLERR|POLLHUP, 0);
        if (sock_pos < 0)
@@ -731,6 +856,10 @@ static int __cmd_start(struct daemon *daemon, struct 
option parent_options[],
        if (file_pos < 0)
                return -1;
 
+       signal_pos = fdarray__add(&fda, signal_fd, POLLIN|POLLERR|POLLHUP, 0);
+       if (signal_pos < 0)
+               return -1;
+
        signal(SIGINT, sig_handler);
        signal(SIGTERM, sig_handler);
 
@@ -744,6 +873,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);
@@ -755,6 +886,7 @@ static int __cmd_start(struct daemon *daemon, struct option 
parent_options[],
 
        close(sock_fd);
        close(conf_fd);
+       close(signal_fd);
        return err;
 }
 
-- 
2.26.2

Reply via email to