perf evlist: Implement control command handling functions
authorAlexey Budankov <alexey.budankov@linux.intel.com>
Fri, 17 Jul 2020 07:01:33 +0000 (10:01 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 22 Jul 2020 12:28:04 +0000 (09:28 -0300)
Implement functions of initialization, finalization and processing of
control command messages coming from control file descriptors.

Allocate control file descriptor as descriptor at struct pollfd object
of evsel_list for atomic poll() operation.

Signed-off-by: Alexey Budankov <alexey.budankov@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/62518ceb-1cc9-2aba-593b-55408d07c1bf@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/evlist.c
tools/perf/util/evlist.h

index 82d00255d4a60dd90ce1eb7b458cd41f59e5ef6d..e3fa3bf7498a2224d546cec1dfabe34028ad6a63 100644 (file)
@@ -1726,3 +1726,143 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
        }
        return leader;
 }
+
+int evlist__initialize_ctlfd(struct evlist *evlist, int fd, int ack)
+{
+       if (fd == -1) {
+               pr_debug("Control descriptor is not initialized\n");
+               return 0;
+       }
+
+       evlist->ctl_fd.pos = perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN,
+                                                    fdarray_flag__nonfilterable);
+       if (evlist->ctl_fd.pos < 0) {
+               evlist->ctl_fd.pos = -1;
+               pr_err("Failed to add ctl fd entry: %m\n");
+               return -1;
+       }
+
+       evlist->ctl_fd.fd = fd;
+       evlist->ctl_fd.ack = ack;
+
+       return 0;
+}
+
+bool evlist__ctlfd_initialized(struct evlist *evlist)
+{
+       return evlist->ctl_fd.pos >= 0;
+}
+
+int evlist__finalize_ctlfd(struct evlist *evlist)
+{
+       struct pollfd *entries = evlist->core.pollfd.entries;
+
+       if (!evlist__ctlfd_initialized(evlist))
+               return 0;
+
+       entries[evlist->ctl_fd.pos].fd = -1;
+       entries[evlist->ctl_fd.pos].events = 0;
+       entries[evlist->ctl_fd.pos].revents = 0;
+
+       evlist->ctl_fd.pos = -1;
+       evlist->ctl_fd.ack = -1;
+       evlist->ctl_fd.fd = -1;
+
+       return 0;
+}
+
+static int evlist__ctlfd_recv(struct evlist *evlist, enum evlist_ctl_cmd *cmd,
+                             char *cmd_data, size_t data_size)
+{
+       int err;
+       char c;
+       size_t bytes_read = 0;
+
+       memset(cmd_data, 0, data_size);
+       data_size--;
+
+       do {
+               err = read(evlist->ctl_fd.fd, &c, 1);
+               if (err > 0) {
+                       if (c == '\n' || c == '\0')
+                               break;
+                       cmd_data[bytes_read++] = c;
+                       if (bytes_read == data_size)
+                               break;
+               } else {
+                       if (err == -1)
+                               pr_err("Failed to read from ctlfd %d: %m\n", evlist->ctl_fd.fd);
+                       break;
+               }
+       } while (1);
+
+       pr_debug("Message from ctl_fd: \"%s%s\"\n", cmd_data,
+                bytes_read == data_size ? "" : c == '\n' ? "\\n" : "\\0");
+
+       if (err > 0) {
+               if (!strncmp(cmd_data, EVLIST_CTL_CMD_ENABLE_TAG,
+                            (sizeof(EVLIST_CTL_CMD_ENABLE_TAG)-1))) {
+                       *cmd = EVLIST_CTL_CMD_ENABLE;
+               } else if (!strncmp(cmd_data, EVLIST_CTL_CMD_DISABLE_TAG,
+                                   (sizeof(EVLIST_CTL_CMD_DISABLE_TAG)-1))) {
+                       *cmd = EVLIST_CTL_CMD_DISABLE;
+               }
+       }
+
+       return err;
+}
+
+static int evlist__ctlfd_ack(struct evlist *evlist)
+{
+       int err;
+
+       if (evlist->ctl_fd.ack == -1)
+               return 0;
+
+       err = write(evlist->ctl_fd.ack, EVLIST_CTL_CMD_ACK_TAG,
+                   sizeof(EVLIST_CTL_CMD_ACK_TAG));
+       if (err == -1)
+               pr_err("failed to write to ctl_ack_fd %d: %m\n", evlist->ctl_fd.ack);
+
+       return err;
+}
+
+int evlist__ctlfd_process(struct evlist *evlist, enum evlist_ctl_cmd *cmd)
+{
+       int err = 0;
+       char cmd_data[EVLIST_CTL_CMD_MAX_LEN];
+       int ctlfd_pos = evlist->ctl_fd.pos;
+       struct pollfd *entries = evlist->core.pollfd.entries;
+
+       if (!evlist__ctlfd_initialized(evlist) || !entries[ctlfd_pos].revents)
+               return 0;
+
+       if (entries[ctlfd_pos].revents & POLLIN) {
+               err = evlist__ctlfd_recv(evlist, cmd, cmd_data,
+                                        EVLIST_CTL_CMD_MAX_LEN);
+               if (err > 0) {
+                       switch (*cmd) {
+                       case EVLIST_CTL_CMD_ENABLE:
+                               evlist__enable(evlist);
+                               break;
+                       case EVLIST_CTL_CMD_DISABLE:
+                               evlist__disable(evlist);
+                               break;
+                       case EVLIST_CTL_CMD_ACK:
+                       case EVLIST_CTL_CMD_UNSUPPORTED:
+                       default:
+                               pr_debug("ctlfd: unsupported %d\n", *cmd);
+                               break;
+                       }
+                       if (!(*cmd == EVLIST_CTL_CMD_ACK || *cmd == EVLIST_CTL_CMD_UNSUPPORTED))
+                               evlist__ctlfd_ack(evlist);
+               }
+       }
+
+       if (entries[ctlfd_pos].revents & (POLLHUP | POLLERR))
+               evlist__finalize_ctlfd(evlist);
+       else
+               entries[ctlfd_pos].revents = 0;
+
+       return err;
+}
index 3510af0711861bb28f9692c17b0b10a28287b5ed..ec676accc76bd90c625cb53eab83095a2138bef0 100644 (file)
@@ -360,4 +360,22 @@ void perf_evlist__force_leader(struct evlist *evlist);
 struct evsel *perf_evlist__reset_weak_group(struct evlist *evlist,
                                                 struct evsel *evsel,
                                                bool close);
+#define EVLIST_CTL_CMD_ENABLE_TAG  "enable"
+#define EVLIST_CTL_CMD_DISABLE_TAG "disable"
+#define EVLIST_CTL_CMD_ACK_TAG     "ack\n"
+
+#define EVLIST_CTL_CMD_MAX_LEN 64
+
+enum evlist_ctl_cmd {
+       EVLIST_CTL_CMD_UNSUPPORTED = 0,
+       EVLIST_CTL_CMD_ENABLE,
+       EVLIST_CTL_CMD_DISABLE,
+       EVLIST_CTL_CMD_ACK
+};
+
+int evlist__initialize_ctlfd(struct evlist *evlist, int ctl_fd, int ctl_fd_ack);
+int evlist__finalize_ctlfd(struct evlist *evlist);
+bool evlist__ctlfd_initialized(struct evlist *evlist);
+int evlist__ctlfd_process(struct evlist *evlist, enum evlist_ctl_cmd *cmd);
+
 #endif /* __PERF_EVLIST_H */