[RFC PATCH 2/4] bpf tools: Collect BPF_MAP_TYPE_PERF_EVENT_ARRAY map definitions from 'maps' section

From: Kaixu Xia
Date: Thu Aug 27 2015 - 06:44:26 EST


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 <xiakaixu@xxxxxxxxxx>
---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/