1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #include <bpf/bpf.h>
9 #include <bpf/libbpf.h>
10
11 #include <test_maps.h>
12
13 static int nr_cpus;
14
map_batch_update(int map_fd,__u32 max_entries,int * keys,__s64 * values,bool is_pcpu)15 static void map_batch_update(int map_fd, __u32 max_entries, int *keys,
16 __s64 *values, bool is_pcpu)
17 {
18 int i, j, err;
19 int cpu_offset = 0;
20 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
21 .elem_flags = 0,
22 .flags = 0,
23 );
24
25 for (i = 0; i < max_entries; i++) {
26 keys[i] = i;
27 if (is_pcpu) {
28 cpu_offset = i * nr_cpus;
29 for (j = 0; j < nr_cpus; j++)
30 (values + cpu_offset)[j] = i + 1 + j;
31 } else {
32 values[i] = i + 1;
33 }
34 }
35
36 err = bpf_map_update_batch(map_fd, keys, values, &max_entries, &opts);
37 CHECK(err, "bpf_map_update_batch()", "error:%s\n", strerror(errno));
38 }
39
map_batch_verify(int * visited,__u32 max_entries,int * keys,__s64 * values,bool is_pcpu)40 static void map_batch_verify(int *visited, __u32 max_entries, int *keys,
41 __s64 *values, bool is_pcpu)
42 {
43 int i, j;
44 int cpu_offset = 0;
45
46 memset(visited, 0, max_entries * sizeof(*visited));
47 for (i = 0; i < max_entries; i++) {
48 if (is_pcpu) {
49 cpu_offset = i * nr_cpus;
50 for (j = 0; j < nr_cpus; j++) {
51 __s64 value = (values + cpu_offset)[j];
52 CHECK(keys[i] + j + 1 != value,
53 "key/value checking",
54 "error: i %d j %d key %d value %lld\n", i,
55 j, keys[i], value);
56 }
57 } else {
58 CHECK(keys[i] + 1 != values[i], "key/value checking",
59 "error: i %d key %d value %lld\n", i, keys[i],
60 values[i]);
61 }
62 visited[i] = 1;
63 }
64 for (i = 0; i < max_entries; i++) {
65 CHECK(visited[i] != 1, "visited checking",
66 "error: keys array at index %d missing\n", i);
67 }
68 }
69
__test_map_lookup_and_update_batch(bool is_pcpu)70 static void __test_map_lookup_and_update_batch(bool is_pcpu)
71 {
72 int map_fd, *keys, *visited;
73 __u32 count, total, total_success;
74 const __u32 max_entries = 10;
75 __u64 batch = 0;
76 int err, step, value_size;
77 void *values;
78 DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
79 .elem_flags = 0,
80 .flags = 0,
81 );
82
83 map_fd = bpf_map_create(is_pcpu ? BPF_MAP_TYPE_PERCPU_ARRAY : BPF_MAP_TYPE_ARRAY,
84 "array_map", sizeof(int), sizeof(__s64), max_entries, NULL);
85 CHECK(map_fd == -1,
86 "bpf_map_create()", "error:%s\n", strerror(errno));
87
88 value_size = sizeof(__s64);
89 if (is_pcpu)
90 value_size *= nr_cpus;
91
92 keys = calloc(max_entries, sizeof(*keys));
93 values = calloc(max_entries, value_size);
94 visited = calloc(max_entries, sizeof(*visited));
95 CHECK(!keys || !values || !visited, "malloc()", "error:%s\n",
96 strerror(errno));
97
98 /* test 1: lookup in a loop with various steps. */
99 total_success = 0;
100 for (step = 1; step < max_entries; step++) {
101 map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
102 map_batch_verify(visited, max_entries, keys, values, is_pcpu);
103 memset(keys, 0, max_entries * sizeof(*keys));
104 memset(values, 0, max_entries * value_size);
105 batch = 0;
106 total = 0;
107 /* iteratively lookup/delete elements with 'step'
108 * elements each.
109 */
110 count = step;
111 while (true) {
112 err = bpf_map_lookup_batch(map_fd,
113 total ? &batch : NULL,
114 &batch, keys + total,
115 values + total * value_size,
116 &count, &opts);
117
118 CHECK((err && errno != ENOENT), "lookup with steps",
119 "error: %s\n", strerror(errno));
120
121 total += count;
122 if (err)
123 break;
124
125 }
126
127 CHECK(total != max_entries, "lookup with steps",
128 "total = %u, max_entries = %u\n", total, max_entries);
129
130 map_batch_verify(visited, max_entries, keys, values, is_pcpu);
131
132 total_success++;
133 }
134
135 CHECK(total_success == 0, "check total_success",
136 "unexpected failure\n");
137
138 free(keys);
139 free(values);
140 free(visited);
141 close(map_fd);
142 }
143
array_map_batch_ops(void)144 static void array_map_batch_ops(void)
145 {
146 __test_map_lookup_and_update_batch(false);
147 printf("test_%s:PASS\n", __func__);
148 }
149
array_percpu_map_batch_ops(void)150 static void array_percpu_map_batch_ops(void)
151 {
152 __test_map_lookup_and_update_batch(true);
153 printf("test_%s:PASS\n", __func__);
154 }
155
test_array_map_batch_ops(void)156 void test_array_map_batch_ops(void)
157 {
158 nr_cpus = libbpf_num_possible_cpus();
159
160 CHECK(nr_cpus < 0, "nr_cpus checking",
161 "error: get possible cpus failed");
162
163 array_map_batch_ops();
164 array_percpu_map_batch_ops();
165 }
166