Implement functions of initialization, finalization and processing
of control command messages coming from control file descriptors.
Allocate control file descriptor as a static descriptor in struct
fdarray of struct perf_evlist object by perf_evlist__add_pollfd_stat().

Signed-off-by: Alexey Budankov <alexey.budan...@linux.intel.com>
---
 tools/perf/util/evlist.c | 128 +++++++++++++++++++++++++++++++++++++++
 tools/perf/util/evlist.h |  17 ++++++
 2 files changed, 145 insertions(+)

diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 47541b5cab46..fbd98f741af9 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1718,3 +1718,131 @@ 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_stat(&evlist->core, fd, 
POLLIN);
+       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;
+}
+
+int evlist__finalize_ctlfd(struct evlist *evlist)
+{
+       if (evlist->ctl_fd.pos == -1)
+               return 0;
+
+       evlist->core.pollfd.stat_entries[evlist->ctl_fd.pos].fd = -1;
+       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--);
+
+       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,
+                            strlen(EVLIST_CTL_CMD_ENABLE_TAG))) {
+                       *cmd = EVLIST_CTL_CMD_ENABLE;
+               } else if (!strncmp(cmd_data, EVLIST_CTL_CMD_DISABLE_TAG,
+                                   strlen(EVLIST_CTL_CMD_DISABLE_TAG))) {
+                       *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 *stat_entries = evlist->core.pollfd.stat_entries;
+
+       if (ctlfd_pos == -1 || !stat_entries[ctlfd_pos].revents)
+               return 0;
+
+       if (stat_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 (stat_entries[ctlfd_pos].revents & (POLLHUP | POLLERR))
+               evlist__finalize_ctlfd(evlist);
+       else
+               stat_entries[ctlfd_pos].revents = 0;
+
+       return err;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 0d8b361f1c8e..bccf0a970371 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -360,4 +360,21 @@ 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);
+int evlist__ctlfd_process(struct evlist *evlist, enum evlist_ctl_cmd *cmd);
+
 #endif /* __PERF_EVLIST_H */
-- 
2.24.1

Reply via email to