tools/bpf: add bpffs percpu map pretty print tests in test_btf
authorYonghong Song <yhs@fb.com>
Wed, 29 Aug 2018 21:43:14 +0000 (14:43 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 30 Aug 2018 12:03:53 +0000 (14:03 +0200)
The bpf selftest test_btf is extended to test bpffs
percpu map pretty print for percpu array, percpu hash and
percpu lru hash.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/testing/selftests/bpf/test_btf.c

index 6b5cfeb..f42b339 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/bpf.h>
 #include <linux/btf.h>
 #include <linux/err.h>
+#include <linux/kernel.h>
 #include <bpf/bpf.h>
 #include <sys/resource.h>
 #include <libelf.h>
@@ -45,7 +46,6 @@ static int count_result(int err)
        return err;
 }
 
-#define min(a, b) ((a) < (b) ? (a) : (b))
 #define __printf(a, b) __attribute__((format(printf, a, b)))
 
 __printf(1, 2)
@@ -130,6 +130,7 @@ struct btf_raw_test {
        bool map_create_err;
        bool ordered_map;
        bool lossless_map;
+       bool percpu_map;
        int hdr_len_delta;
        int type_off_delta;
        int str_off_delta;
@@ -2157,6 +2158,7 @@ static struct btf_pprint_test_meta {
        const char *map_name;
        bool ordered_map;
        bool lossless_map;
+       bool percpu_map;
 } pprint_tests_meta[] = {
 {
        .descr = "BTF pretty print array",
@@ -2164,6 +2166,7 @@ static struct btf_pprint_test_meta {
        .map_name = "pprint_test_array",
        .ordered_map = true,
        .lossless_map = true,
+       .percpu_map = false,
 },
 
 {
@@ -2172,6 +2175,7 @@ static struct btf_pprint_test_meta {
        .map_name = "pprint_test_hash",
        .ordered_map = false,
        .lossless_map = true,
+       .percpu_map = false,
 },
 
 {
@@ -2180,30 +2184,83 @@ static struct btf_pprint_test_meta {
        .map_name = "pprint_test_lru_hash",
        .ordered_map = false,
        .lossless_map = false,
+       .percpu_map = false,
+},
+
+{
+       .descr = "BTF pretty print percpu array",
+       .map_type = BPF_MAP_TYPE_PERCPU_ARRAY,
+       .map_name = "pprint_test_percpu_array",
+       .ordered_map = true,
+       .lossless_map = true,
+       .percpu_map = true,
+},
+
+{
+       .descr = "BTF pretty print percpu hash",
+       .map_type = BPF_MAP_TYPE_PERCPU_HASH,
+       .map_name = "pprint_test_percpu_hash",
+       .ordered_map = false,
+       .lossless_map = true,
+       .percpu_map = true,
+},
+
+{
+       .descr = "BTF pretty print lru percpu hash",
+       .map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
+       .map_name = "pprint_test_lru_percpu_hash",
+       .ordered_map = false,
+       .lossless_map = false,
+       .percpu_map = true,
 },
 
 };
 
 
-static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i)
+static void set_pprint_mapv(struct pprint_mapv *v, uint32_t i,
+                           int num_cpus, int rounded_value_size)
 {
-       v->ui32 = i;
-       v->si32 = -i;
-       v->unused_bits2a = 3;
-       v->bits28 = i;
-       v->unused_bits2b = 3;
-       v->ui64 = i;
-       v->aenum = i & 0x03;
+       int cpu;
+
+       for (cpu = 0; cpu < num_cpus; cpu++) {
+               v->ui32 = i + cpu;
+               v->si32 = -i;
+               v->unused_bits2a = 3;
+               v->bits28 = i;
+               v->unused_bits2b = 3;
+               v->ui64 = i;
+               v->aenum = i & 0x03;
+               v = (void *)v + rounded_value_size;
+       }
 }
 
+static int check_line(const char *expected_line, int nexpected_line,
+                     int expected_line_len, const char *line)
+{
+       if (CHECK(nexpected_line == expected_line_len,
+                 "expected_line is too long"))
+               return -1;
+
+       if (strcmp(expected_line, line)) {
+               fprintf(stderr, "unexpected pprint output\n");
+               fprintf(stderr, "expected: %s", expected_line);
+               fprintf(stderr, "    read: %s", line);
+               return -1;
+       }
+
+       return 0;
+}
+
+
 static int do_test_pprint(void)
 {
        const struct btf_raw_test *test = &pprint_test_template;
        struct bpf_create_map_attr create_attr = {};
+       bool ordered_map, lossless_map, percpu_map;
+       int err, ret, num_cpus, rounded_value_size;
+       struct pprint_mapv *mapv = NULL;
        unsigned int key, nr_read_elems;
-       bool ordered_map, lossless_map;
        int map_fd = -1, btf_fd = -1;
-       struct pprint_mapv mapv = {};
        unsigned int raw_btf_size;
        char expected_line[255];
        FILE *pin_file = NULL;
@@ -2212,7 +2269,6 @@ static int do_test_pprint(void)
        char *line = NULL;
        uint8_t *raw_btf;
        ssize_t nread;
-       int err, ret;
 
        fprintf(stderr, "%s......", test->descr);
        raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
@@ -2261,9 +2317,18 @@ static int do_test_pprint(void)
        if (CHECK(err, "bpf_obj_pin(%s): errno:%d.", pin_path, errno))
                goto done;
 
+       percpu_map = test->percpu_map;
+       num_cpus = percpu_map ? bpf_num_possible_cpus() : 1;
+       rounded_value_size = round_up(sizeof(struct pprint_mapv), 8);
+       mapv = calloc(num_cpus, rounded_value_size);
+       if (CHECK(!mapv, "mapv allocation failure")) {
+               err = -1;
+               goto done;
+       }
+
        for (key = 0; key < test->max_entries; key++) {
-               set_pprint_mapv(&mapv, key);
-               bpf_map_update_elem(map_fd, &key, &mapv, 0);
+               set_pprint_mapv(mapv, key, num_cpus, rounded_value_size);
+               bpf_map_update_elem(map_fd, &key, mapv, 0);
        }
 
        pin_file = fopen(pin_path, "r");
@@ -2286,33 +2351,74 @@ static int do_test_pprint(void)
        ordered_map = test->ordered_map;
        lossless_map = test->lossless_map;
        do {
+               struct pprint_mapv *cmapv;
                ssize_t nexpected_line;
                unsigned int next_key;
+               int cpu;
 
                next_key = ordered_map ? nr_read_elems : atoi(line);
-               set_pprint_mapv(&mapv, next_key);
-               nexpected_line = snprintf(expected_line, sizeof(expected_line),
-                                         "%u: {%u,0,%d,0x%x,0x%x,0x%x,{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n",
-                                         next_key,
-                                         mapv.ui32, mapv.si32,
-                                         mapv.unused_bits2a, mapv.bits28, mapv.unused_bits2b,
-                                         mapv.ui64,
-                                         mapv.ui8a[0], mapv.ui8a[1], mapv.ui8a[2], mapv.ui8a[3],
-                                         mapv.ui8a[4], mapv.ui8a[5], mapv.ui8a[6], mapv.ui8a[7],
-                                         pprint_enum_str[mapv.aenum]);
-
-               if (CHECK(nexpected_line == sizeof(expected_line),
-                         "expected_line is too long")) {
-                       err = -1;
-                       goto done;
+               set_pprint_mapv(mapv, next_key, num_cpus, rounded_value_size);
+               cmapv = mapv;
+
+               for (cpu = 0; cpu < num_cpus; cpu++) {
+                       if (percpu_map) {
+                               /* for percpu map, the format looks like:
+                                * <key>: {
+                                *      cpu0: <value_on_cpu0>
+                                *      cpu1: <value_on_cpu1>
+                                *      ...
+                                *      cpun: <value_on_cpun>
+                                * }
+                                *
+                                * let us verify the line containing the key here.
+                                */
+                               if (cpu == 0) {
+                                       nexpected_line = snprintf(expected_line,
+                                                                 sizeof(expected_line),
+                                                                 "%u: {\n",
+                                                                 next_key);
+
+                                       err = check_line(expected_line, nexpected_line,
+                                                        sizeof(expected_line), line);
+                                       if (err == -1)
+                                               goto done;
+                               }
+
+                               /* read value@cpu */
+                               nread = getline(&line, &line_len, pin_file);
+                               if (nread < 0)
+                                       break;
+                       }
+
+                       nexpected_line = snprintf(expected_line, sizeof(expected_line),
+                                                 "%s%u: {%u,0,%d,0x%x,0x%x,0x%x,"
+                                                 "{%lu|[%u,%u,%u,%u,%u,%u,%u,%u]},%s}\n",
+                                                 percpu_map ? "\tcpu" : "",
+                                                 percpu_map ? cpu : next_key,
+                                                 cmapv->ui32, cmapv->si32,
+                                                 cmapv->unused_bits2a,
+                                                 cmapv->bits28,
+                                                 cmapv->unused_bits2b,
+                                                 cmapv->ui64,
+                                                 cmapv->ui8a[0], cmapv->ui8a[1],
+                                                 cmapv->ui8a[2], cmapv->ui8a[3],
+                                                 cmapv->ui8a[4], cmapv->ui8a[5],
+                                                 cmapv->ui8a[6], cmapv->ui8a[7],
+                                                 pprint_enum_str[cmapv->aenum]);
+
+                       err = check_line(expected_line, nexpected_line,
+                                        sizeof(expected_line), line);
+                       if (err == -1)
+                               goto done;
+
+                       cmapv = (void *)cmapv + rounded_value_size;
                }
 
-               if (strcmp(expected_line, line)) {
-                       err = -1;
-                       fprintf(stderr, "unexpected pprint output\n");
-                       fprintf(stderr, "expected: %s", expected_line);
-                       fprintf(stderr, "    read: %s", line);
-                       goto done;
+               if (percpu_map) {
+                       /* skip the last bracket for the percpu map */
+                       nread = getline(&line, &line_len, pin_file);
+                       if (nread < 0)
+                               break;
                }
 
                nread = getline(&line, &line_len, pin_file);
@@ -2334,6 +2440,8 @@ static int do_test_pprint(void)
        err = 0;
 
 done:
+       if (mapv)
+               free(mapv);
        if (!err)
                fprintf(stderr, "OK");
        if (*btf_log_buf && (err || args.always_log))
@@ -2361,6 +2469,7 @@ static int test_pprint(void)
                pprint_test_template.map_name = pprint_tests_meta[i].map_name;
                pprint_test_template.ordered_map = pprint_tests_meta[i].ordered_map;
                pprint_test_template.lossless_map = pprint_tests_meta[i].lossless_map;
+               pprint_test_template.percpu_map = pprint_tests_meta[i].percpu_map;
 
                err |= count_result(do_test_pprint());
        }