1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Richard Palethorpe <rpalethorpe@suse.com>
4 *
5 * Trivial Extended Berkeley Packet Filter (eBPF) test.
6 *
7 * Sanity check creating and updating maps.
8 */
9 /*
10 * If test is executed in a loop and limit for locked memory (ulimit -l) is
11 * too low bpf() call can fail with EPERM due to deffered freeing.
12 */
13
14 #include <limits.h>
15 #include <string.h>
16
17 #include "config.h"
18 #include "tst_test.h"
19 #include "lapi/bpf.h"
20 #include "bpf_common.h"
21
22 #define VAL_SZ 1024
23
24 static void *key4;
25 static void *key8;
26 static char *val_set;
27 static char *val_get;
28 static union bpf_attr *attr;
29
30 struct map_type {
31 uint32_t id;
32 char *name;
33 int key_size;
34 void **key;
35 };
36
37 static const struct map_type map_types[] = {
38 {BPF_MAP_TYPE_HASH, "hash", 8, &key8},
39 {BPF_MAP_TYPE_ARRAY, "array", 4, &key4}
40 };
41
run(unsigned int n)42 void run(unsigned int n)
43 {
44 int fd, i;
45 void *key = *map_types[n].key;
46
47 memset(attr, 0, sizeof(*attr));
48 attr->map_type = map_types[n].id;
49 attr->key_size = map_types[n].key_size;
50 attr->value_size = VAL_SZ;
51 attr->max_entries = 1;
52
53 fd = bpf_map_create(attr);
54 tst_res(TPASS, "Created %s map", map_types[n].name);
55
56 memset(attr, 0, sizeof(*attr));
57 attr->map_fd = fd;
58 attr->key = ptr_to_u64(key);
59 attr->value = ptr_to_u64(val_get);
60
61 memset(val_get, 'x', VAL_SZ);
62
63 TEST(bpf(BPF_MAP_LOOKUP_ELEM, attr, sizeof(*attr)));
64
65 switch (map_types[n].id) {
66 case BPF_MAP_TYPE_HASH:
67 if (TST_RET != -1 || TST_ERR != ENOENT) {
68 tst_res(TFAIL | TTERRNO,
69 "Empty hash map lookup should fail with ENOENT");
70 } else {
71 tst_res(TPASS | TTERRNO, "Empty hash map lookup");
72 }
73 break;
74 case BPF_MAP_TYPE_ARRAY:
75 if (TST_RET != -1) {
76 for (i = 0; i < VAL_SZ; i++) {
77 if (val_get[i] != 0) {
78 tst_res(TFAIL,
79 "Preallocated array map val not zero");
80 break;
81 }
82 }
83 if (i < VAL_SZ)
84 tst_res(TPASS, "Preallocated array map lookup");
85 } else {
86 tst_res(TFAIL | TERRNO, "Prellocated array map lookup");
87 }
88 break;
89 }
90
91 memset(attr, 0, sizeof(*attr));
92 attr->map_fd = fd;
93 attr->key = ptr_to_u64(key);
94 attr->value = ptr_to_u64(val_set);
95 attr->flags = BPF_ANY;
96
97 TEST(bpf(BPF_MAP_UPDATE_ELEM, attr, sizeof(*attr)));
98 if (TST_RET == -1) {
99 tst_brk(TFAIL | TTERRNO,
100 "Update %s map element",
101 map_types[n].name);
102 } else {
103 tst_res(TPASS,
104 "Update %s map element",
105 map_types[n].name);
106 }
107
108 memset(attr, 0, sizeof(*attr));
109 attr->map_fd = fd;
110 attr->key = ptr_to_u64(key);
111 attr->value = ptr_to_u64(val_get);
112
113 TEST(bpf(BPF_MAP_LOOKUP_ELEM, attr, sizeof(*attr)));
114 if (TST_RET == -1) {
115 tst_res(TFAIL | TTERRNO,
116 "%s map lookup missing",
117 map_types[n].name);
118 } else if (memcmp(val_set, val_get, (size_t) VAL_SZ)) {
119 tst_res(TFAIL,
120 "%s map lookup returned different value",
121 map_types[n].name);
122 } else {
123 tst_res(TPASS, "%s map lookup", map_types[n].name);
124 }
125
126 SAFE_CLOSE(fd);
127 }
128
setup(void)129 static void setup(void)
130 {
131 unsigned int i;
132
133 rlimit_bump_memlock();
134
135 memcpy(key8, "12345678", 8);
136 memset(key4, 0, 4);
137
138 for (i = 0; i < VAL_SZ; i++)
139 val_set[i] = i % 256;
140 }
141
142 static struct tst_test test = {
143 .tcnt = ARRAY_SIZE(map_types),
144 .test = run,
145 .setup = setup,
146 .min_kver = "3.19",
147 .bufs = (struct tst_buffers []) {
148 {&key4, .size = 4},
149 {&key8, .size = 8},
150 {&val_set, .size = VAL_SZ},
151 {&val_get, .size = VAL_SZ},
152 {&attr, .size = sizeof(*attr)},
153 {},
154 },
155 };
156