In order to make use of the new ability of eBPF programs to access
hardware PMU counter, we need to get the event & map match. So we
introduce the struct perf_event_map_def that contains struct bpf_map
_def and struct perf_event_attr. We can get the necessary info from
'maps' section and store the pointers to struct perf_event in
BPF_MAP_TYPE_PERF_EVENT_ARRAY maps.

Signed-off-by: Kaixu Xia <[email protected]>
---
 tools/lib/bpf/libbpf.c | 76 ++++++++++++++++++++++++++++++++++++++++----------
 tools/lib/bpf/libbpf.h | 13 +++++++++
 2 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1ff6a19..83d79c4 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -570,7 +570,7 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int 
idx)
 
 static int
 bpf_program__collect_reloc(struct bpf_program *prog,
-                          size_t nr_maps, GElf_Shdr *shdr,
+                          size_t max_maps, GElf_Shdr *shdr,
                           Elf_Data *data, Elf_Data *symbols)
 {
        int i, nrels;
@@ -616,9 +616,9 @@ bpf_program__collect_reloc(struct bpf_program *prog,
                }
 
                map_idx = sym.st_value / sizeof(struct bpf_map_def);
-               if (map_idx >= nr_maps) {
+               if (map_idx >= max_maps) {
                        pr_warning("bpf relocation: map_idx %d large than %d\n",
-                                  (int)map_idx, (int)nr_maps - 1);
+                                  (int)map_idx, (int)max_maps - 1);
                        return -EINVAL;
                }
 
@@ -629,11 +629,42 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 }
 
 static int
+bpf_object__collect_perf_event_maps(void *data, int **pfd)
+{
+       int i, event_fd;
+       int maps_fd = **pfd;
+       int attr_length = ATTR_LENGTH;
+       int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+       struct perf_event_attr *attr;
+
+       attr = (struct perf_event_attr *)(data + sizeof(struct bpf_map_def));
+       if (attr->type != PERF_TYPE_RAW &&
+           attr->type != PERF_TYPE_HARDWARE)
+               return -EINVAL;
+       attr->disabled = 1;
+
+       do {
+               (*pfd)++;
+               **pfd = -1;
+       } while (--attr_length);
+
+       for (i = 0; i < nr_cpus; i++) {
+               event_fd = perf_event_open(attr, -1/*pid*/, i/*cpu*/, 
-1/*group_fd*/, 0);
+               if (event_fd < 0) {
+                       pr_warning("event syscall failed\n");
+                       return -EINVAL;
+               }
+               bpf_update_elem(maps_fd, &i, &event_fd, BPF_ANY);
+       }
+       return 0;
+}
+
+static int
 bpf_object__create_maps(struct bpf_object *obj)
 {
        unsigned int i;
-       size_t nr_maps;
-       int *pfd;
+       size_t nr_maps, j;
+       int *pfd, err;
 
        nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def);
        if (!obj->maps_buf || !nr_maps) {
@@ -664,24 +695,37 @@ bpf_object__create_maps(struct bpf_object *obj)
                                      def.value_size,
                                      def.max_entries);
                if (*pfd < 0) {
-                       size_t j;
-                       int err = *pfd;
-
+                       err = *pfd;
                        pr_warning("failed to create map: %s\n",
                                   strerror(errno));
-                       for (j = 0; j < i; j++)
-                               zclose(obj->map_fds[j]);
-                       obj->nr_map_fds = 0;
-                       zfree(&obj->map_fds);
-                       return err;
+                       goto out_close;
                }
                pr_debug("create map: fd=%d\n", *pfd);
+
+               if (def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
+                       void *data = obj->maps_buf + i * sizeof(struct 
bpf_map_def);
+
+                       err = bpf_object__collect_perf_event_maps(data, &pfd);
+                       if (err < 0) {
+                               pr_warning("failed to collect perf_event maps: 
%s\n",
+                                           strerror(errno));
+                               goto out_close;
+                       }
+                       i += ATTR_LENGTH;
+               }
                pfd++;
        }
 
        zfree(&obj->maps_buf);
        obj->maps_buf_sz = 0;
        return 0;
+
+out_close:
+       for (j = 0; j < i; j++)
+               zclose(obj->map_fds[j]);
+       obj->nr_map_fds = 0;
+       zfree(&obj->map_fds);
+       return err;
 }
 
 static int
@@ -705,6 +749,8 @@ bpf_program__relocate(struct bpf_program *prog, int 
*map_fds)
                        return -ERANGE;
                }
                insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+               if (map_fds[map_idx] == -1)
+                       return -EINVAL;
                insns[insn_idx].imm = map_fds[map_idx];
        }
 
@@ -748,7 +794,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
                Elf_Data *data = obj->efile.reloc[i].data;
                int idx = shdr->sh_info;
                struct bpf_program *prog;
-               size_t nr_maps = obj->maps_buf_sz /
+               size_t max_maps = obj->maps_buf_sz /
                                 sizeof(struct bpf_map_def);
 
                if (shdr->sh_type != SHT_REL) {
@@ -763,7 +809,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
                        return -ENOENT;
                }
 
-               err = bpf_program__collect_reloc(prog, nr_maps,
+               err = bpf_program__collect_reloc(prog, max_maps,
                                                 shdr, data,
                                                 obj->efile.symbols);
                if (err)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 9fa7b09..8361dd5 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -10,6 +10,7 @@
 
 #include <stdio.h>
 #include <stdbool.h>
+#include <linux/perf_event.h>
 
 /*
  * In include/linux/compiler-gcc.h, __printf is defined. However
@@ -100,4 +101,16 @@ struct bpf_map_def {
        unsigned int max_entries;
 };
 
+#define ATTR_LENGTH ((sizeof(struct perf_event_attr) + \
+                    sizeof(struct bpf_map_def) - 1) /\
+                    sizeof(struct bpf_map_def))
+
+struct perf_event_map_def {
+       struct bpf_map_def map_def;
+       union {
+               struct perf_event_attr attr;
+               struct bpf_map_def align[ATTR_LENGTH];
+       };
+};
+
 #endif
-- 
1.8.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to