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 "bpf_common.h"
20
21 #define VAL_SZ 1024
22
23 static void *key4;
24 static void *key8;
25 static char *val_set;
26 static char *val_get;
27 static union bpf_attr *attr;
28
29 struct map_type {
30 uint32_t id;
31 char *name;
32 int key_size;
33 void **key;
34 };
35
36 static const struct map_type map_types[] = {
37 {BPF_MAP_TYPE_HASH, "hash", 8, &key8},
38 {BPF_MAP_TYPE_ARRAY, "array", 4, &key4}
39 };
40
run(unsigned int n)41 void run(unsigned int n)
42 {
43 int fd, i;
44 void *key = *map_types[n].key;
45
46 memset(attr, 0, sizeof(*attr));
47 attr->map_type = map_types[n].id;
48 attr->key_size = map_types[n].key_size;
49 attr->value_size = VAL_SZ;
50 attr->max_entries = 1;
51
52 fd = bpf_map_create(attr);
53 tst_res(TPASS, "Created %s map", map_types[n].name);
54
55 memset(attr, 0, sizeof(*attr));
56 attr->map_fd = fd;
57 attr->key = ptr_to_u64(key);
58 attr->value = ptr_to_u64(val_get);
59
60 memset(val_get, 'x', VAL_SZ);
61
62 TEST(bpf(BPF_MAP_LOOKUP_ELEM, attr, sizeof(*attr)));
63
64 switch (map_types[n].id) {
65 case BPF_MAP_TYPE_HASH:
66 if (TST_RET != -1 || TST_ERR != ENOENT) {
67 tst_res(TFAIL | TTERRNO,
68 "Empty hash map lookup should fail with ENOENT");
69 } else {
70 tst_res(TPASS | TTERRNO, "Empty hash map lookup");
71 }
72 break;
73 case BPF_MAP_TYPE_ARRAY:
74 if (TST_RET != -1) {
75 for (i = 0; i < VAL_SZ; i++) {
76 if (val_get[i] != 0) {
77 tst_res(TFAIL,
78 "Preallocated array map val not zero");
79 break;
80 }
81 }
82 if (i < VAL_SZ)
83 tst_res(TPASS, "Preallocated array map lookup");
84 } else {
85 tst_res(TFAIL | TTERRNO, "Prellocated array map lookup");
86 }
87 break;
88 }
89
90 memset(attr, 0, sizeof(*attr));
91 attr->map_fd = fd;
92 attr->key = ptr_to_u64(key);
93 attr->value = ptr_to_u64(val_set);
94 attr->flags = BPF_ANY;
95
96 TEST(bpf(BPF_MAP_UPDATE_ELEM, attr, sizeof(*attr)));
97 if (TST_RET == -1) {
98 tst_brk(TFAIL | TTERRNO,
99 "Update %s map element",
100 map_types[n].name);
101 } else {
102 tst_res(TPASS,
103 "Update %s map element",
104 map_types[n].name);
105 }
106
107 memset(attr, 0, sizeof(*attr));
108 attr->map_fd = fd;
109 attr->key = ptr_to_u64(key);
110 attr->value = ptr_to_u64(val_get);
111
112 TEST(bpf(BPF_MAP_LOOKUP_ELEM, attr, sizeof(*attr)));
113 if (TST_RET == -1) {
114 tst_res(TFAIL | TTERRNO,
115 "%s map lookup missing",
116 map_types[n].name);
117 } else if (memcmp(val_set, val_get, (size_t) VAL_SZ)) {
118 tst_res(TFAIL,
119 "%s map lookup returned different value",
120 map_types[n].name);
121 } else {
122 tst_res(TPASS, "%s map lookup", map_types[n].name);
123 }
124
125 SAFE_CLOSE(fd);
126 }
127
setup(void)128 static void setup(void)
129 {
130 unsigned int i;
131
132 rlimit_bump_memlock();
133
134 memcpy(key8, "12345678", 8);
135 memset(key4, 0, 4);
136
137 for (i = 0; i < VAL_SZ; i++)
138 val_set[i] = i % 256;
139 }
140
141 static struct tst_test test = {
142 .tcnt = ARRAY_SIZE(map_types),
143 .test = run,
144 .setup = setup,
145 .min_kver = "3.19",
146 .bufs = (struct tst_buffers []) {
147 {&key4, .size = 4},
148 {&key8, .size = 8},
149 {&val_set, .size = VAL_SZ},
150 {&val_get, .size = VAL_SZ},
151 {&attr, .size = sizeof(*attr)},
152 {},
153 },
154 };
155