Linux Audio

Check our new training course

Loading...
v5.4
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2016 Facebook
 
 
 
 
  4 */
  5#define _GNU_SOURCE
  6#include <stdio.h>
  7#include <unistd.h>
  8#include <errno.h>
  9#include <string.h>
 10#include <assert.h>
 11#include <sched.h>
 12#include <stdlib.h>
 13#include <time.h>
 14
 15#include <sys/wait.h>
 16
 17#include <bpf/bpf.h>
 18#include <bpf/libbpf.h>
 19
 20#include "bpf_util.h"
 21#include "bpf_rlimit.h"
 22#include "../../../include/linux/filter.h"
 23
 24#define LOCAL_FREE_TARGET	(128)
 25#define PERCPU_FREE_TARGET	(4)
 26
 27static int nr_cpus;
 28
 29static int create_map(int map_type, int map_flags, unsigned int size)
 30{
 31	int map_fd;
 32
 33	map_fd = bpf_create_map(map_type, sizeof(unsigned long long),
 34				sizeof(unsigned long long), size, map_flags);
 35
 36	if (map_fd == -1)
 37		perror("bpf_create_map");
 38
 39	return map_fd;
 40}
 41
 42static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
 43					    void *value)
 44{
 45	struct bpf_load_program_attr prog;
 46	struct bpf_create_map_attr map;
 47	struct bpf_insn insns[] = {
 48		BPF_LD_MAP_VALUE(BPF_REG_9, 0, 0),
 49		BPF_LD_MAP_FD(BPF_REG_1, fd),
 50		BPF_LD_IMM64(BPF_REG_3, key),
 51		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
 52		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
 53		BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
 54		BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
 55		BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
 56		BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
 57		BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_1, 0),
 58		BPF_MOV64_IMM(BPF_REG_0, 42),
 59		BPF_JMP_IMM(BPF_JA, 0, 0, 1),
 60		BPF_MOV64_IMM(BPF_REG_0, 1),
 61		BPF_EXIT_INSN(),
 62	};
 63	__u8 data[64] = {};
 64	int mfd, pfd, ret, zero = 0;
 65	__u32 retval = 0;
 66
 67	memset(&map, 0, sizeof(map));
 68	map.map_type = BPF_MAP_TYPE_ARRAY;
 69	map.key_size = sizeof(int);
 70	map.value_size = sizeof(unsigned long long);
 71	map.max_entries = 1;
 72
 73	mfd = bpf_create_map_xattr(&map);
 74	if (mfd < 0)
 75		return -1;
 76
 77	insns[0].imm = mfd;
 78
 79	memset(&prog, 0, sizeof(prog));
 80	prog.prog_type = BPF_PROG_TYPE_SCHED_CLS;
 81	prog.insns = insns;
 82	prog.insns_cnt = ARRAY_SIZE(insns);
 83	prog.license = "GPL";
 84
 85	pfd = bpf_load_program_xattr(&prog, NULL, 0);
 86	if (pfd < 0) {
 87		close(mfd);
 88		return -1;
 89	}
 90
 91	ret = bpf_prog_test_run(pfd, 1, data, sizeof(data),
 92				NULL, NULL, &retval, NULL);
 93	if (ret < 0 || retval != 42) {
 94		ret = -1;
 95	} else {
 96		assert(!bpf_map_lookup_elem(mfd, &zero, value));
 97		ret = 0;
 98	}
 99	close(pfd);
100	close(mfd);
101	return ret;
102}
103
104static int map_subset(int map0, int map1)
105{
106	unsigned long long next_key = 0;
107	unsigned long long value0[nr_cpus], value1[nr_cpus];
108	int ret;
109
110	while (!bpf_map_get_next_key(map1, &next_key, &next_key)) {
111		assert(!bpf_map_lookup_elem(map1, &next_key, value1));
112		ret = bpf_map_lookup_elem(map0, &next_key, value0);
113		if (ret) {
114			printf("key:%llu not found from map. %s(%d)\n",
115			       next_key, strerror(errno), errno);
116			return 0;
117		}
118		if (value0[0] != value1[0]) {
119			printf("key:%llu value0:%llu != value1:%llu\n",
120			       next_key, value0[0], value1[0]);
121			return 0;
122		}
123	}
124	return 1;
125}
126
127static int map_equal(int lru_map, int expected)
128{
129	return map_subset(lru_map, expected) && map_subset(expected, lru_map);
130}
131
132static int sched_next_online(int pid, int *next_to_try)
133{
134	cpu_set_t cpuset;
135	int next = *next_to_try;
136	int ret = -1;
137
138	while (next < nr_cpus) {
139		CPU_ZERO(&cpuset);
140		CPU_SET(next++, &cpuset);
141		if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) {
142			ret = 0;
143			break;
144		}
145	}
146
147	*next_to_try = next;
148	return ret;
149}
150
151/* Size of the LRU map is 2
152 * Add key=1 (+1 key)
153 * Add key=2 (+1 key)
154 * Lookup Key=1
155 * Add Key=3
156 *   => Key=2 will be removed by LRU
157 * Iterate map.  Only found key=1 and key=3
158 */
159static void test_lru_sanity0(int map_type, int map_flags)
160{
161	unsigned long long key, value[nr_cpus];
162	int lru_map_fd, expected_map_fd;
163	int next_cpu = 0;
164
165	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
166	       map_flags);
167
168	assert(sched_next_online(0, &next_cpu) != -1);
169
170	if (map_flags & BPF_F_NO_COMMON_LRU)
171		lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
172	else
173		lru_map_fd = create_map(map_type, map_flags, 2);
174	assert(lru_map_fd != -1);
175
176	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
177	assert(expected_map_fd != -1);
178
179	value[0] = 1234;
180
181	/* insert key=1 element */
182
183	key = 1;
184	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
185	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
186				    BPF_NOEXIST));
187
188	/* BPF_NOEXIST means: add new element if it doesn't exist */
189	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
190	       /* key=1 already exists */
191	       && errno == EEXIST);
192
193	assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -1 &&
194	       errno == EINVAL);
195
196	/* insert key=2 element */
197
198	/* check that key=2 is not found */
199	key = 2;
200	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
201	       errno == ENOENT);
202
203	/* BPF_EXIST means: update existing element */
204	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
205	       /* key=2 is not there */
206	       errno == ENOENT);
207
208	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
209
210	/* insert key=3 element */
211
212	/* check that key=3 is not found */
213	key = 3;
214	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
215	       errno == ENOENT);
216
217	/* check that key=1 can be found and mark the ref bit to
218	 * stop LRU from removing key=1
219	 */
220	key = 1;
221	assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
222	assert(value[0] == 1234);
223
224	key = 3;
225	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
226	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
227				    BPF_NOEXIST));
228
229	/* key=2 has been removed from the LRU */
230	key = 2;
231	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
232	       errno == ENOENT);
233
234	assert(map_equal(lru_map_fd, expected_map_fd));
235
236	close(expected_map_fd);
237	close(lru_map_fd);
238
239	printf("Pass\n");
240}
241
242/* Size of the LRU map is 1.5*tgt_free
243 * Insert 1 to tgt_free (+tgt_free keys)
244 * Lookup 1 to tgt_free/2
245 * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys)
246 * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU
247 */
248static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
249{
250	unsigned long long key, end_key, value[nr_cpus];
251	int lru_map_fd, expected_map_fd;
252	unsigned int batch_size;
253	unsigned int map_size;
254	int next_cpu = 0;
255
256	if (map_flags & BPF_F_NO_COMMON_LRU)
257		/* This test is only applicable to common LRU list */
258		return;
259
260	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
261	       map_flags);
262
263	assert(sched_next_online(0, &next_cpu) != -1);
264
265	batch_size = tgt_free / 2;
266	assert(batch_size * 2 == tgt_free);
267
268	map_size = tgt_free + batch_size;
269	lru_map_fd = create_map(map_type, map_flags, map_size);
270	assert(lru_map_fd != -1);
271
272	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
273	assert(expected_map_fd != -1);
274
275	value[0] = 1234;
276
277	/* Insert 1 to tgt_free (+tgt_free keys) */
278	end_key = 1 + tgt_free;
279	for (key = 1; key < end_key; key++)
280		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
281					    BPF_NOEXIST));
282
283	/* Lookup 1 to tgt_free/2 */
284	end_key = 1 + batch_size;
285	for (key = 1; key < end_key; key++) {
286		assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
287		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
288					    BPF_NOEXIST));
289	}
290
291	/* Insert 1+tgt_free to 2*tgt_free
292	 * => 1+tgt_free/2 to LOCALFREE_TARGET will be
293	 * removed by LRU
294	 */
295	key = 1 + tgt_free;
296	end_key = key + tgt_free;
297	for (; key < end_key; key++) {
298		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
299					    BPF_NOEXIST));
300		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
301					    BPF_NOEXIST));
302	}
303
304	assert(map_equal(lru_map_fd, expected_map_fd));
305
306	close(expected_map_fd);
307	close(lru_map_fd);
308
309	printf("Pass\n");
310}
311
312/* Size of the LRU map 1.5 * tgt_free
313 * Insert 1 to tgt_free (+tgt_free keys)
314 * Update 1 to tgt_free/2
315 *   => The original 1 to tgt_free/2 will be removed due to
316 *      the LRU shrink process
317 * Re-insert 1 to tgt_free/2 again and do a lookup immeidately
318 * Insert 1+tgt_free to tgt_free*3/2
319 * Insert 1+tgt_free*3/2 to tgt_free*5/2
320 *   => Key 1+tgt_free to tgt_free*3/2
321 *      will be removed from LRU because it has never
322 *      been lookup and ref bit is not set
323 */
324static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
325{
326	unsigned long long key, value[nr_cpus];
327	unsigned long long end_key;
328	int lru_map_fd, expected_map_fd;
329	unsigned int batch_size;
330	unsigned int map_size;
331	int next_cpu = 0;
332
333	if (map_flags & BPF_F_NO_COMMON_LRU)
334		/* This test is only applicable to common LRU list */
335		return;
336
337	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
338	       map_flags);
339
340	assert(sched_next_online(0, &next_cpu) != -1);
341
342	batch_size = tgt_free / 2;
343	assert(batch_size * 2 == tgt_free);
344
345	map_size = tgt_free + batch_size;
346	lru_map_fd = create_map(map_type, map_flags, map_size);
347	assert(lru_map_fd != -1);
348
349	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
350	assert(expected_map_fd != -1);
351
352	value[0] = 1234;
353
354	/* Insert 1 to tgt_free (+tgt_free keys) */
355	end_key = 1 + tgt_free;
356	for (key = 1; key < end_key; key++)
357		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
358					    BPF_NOEXIST));
359
360	/* Any bpf_map_update_elem will require to acquire a new node
361	 * from LRU first.
362	 *
363	 * The local list is running out of free nodes.
364	 * It gets from the global LRU list which tries to
365	 * shrink the inactive list to get tgt_free
366	 * number of free nodes.
367	 *
368	 * Hence, the oldest key 1 to tgt_free/2
369	 * are removed from the LRU list.
370	 */
371	key = 1;
372	if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
373		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
374					    BPF_NOEXIST));
375		assert(!bpf_map_delete_elem(lru_map_fd, &key));
376	} else {
377		assert(bpf_map_update_elem(lru_map_fd, &key, value,
378					   BPF_EXIST));
379	}
380
381	/* Re-insert 1 to tgt_free/2 again and do a lookup
382	 * immeidately.
383	 */
384	end_key = 1 + batch_size;
385	value[0] = 4321;
386	for (key = 1; key < end_key; key++) {
387		assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
388		       errno == ENOENT);
389		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
390					    BPF_NOEXIST));
391		assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
392		assert(value[0] == 4321);
393		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
394					    BPF_NOEXIST));
395	}
396
397	value[0] = 1234;
398
399	/* Insert 1+tgt_free to tgt_free*3/2 */
400	end_key = 1 + tgt_free + batch_size;
401	for (key = 1 + tgt_free; key < end_key; key++)
402		/* These newly added but not referenced keys will be
403		 * gone during the next LRU shrink.
404		 */
405		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
406					    BPF_NOEXIST));
407
408	/* Insert 1+tgt_free*3/2 to  tgt_free*5/2 */
409	end_key = key + tgt_free;
410	for (; key < end_key; key++) {
411		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
412					    BPF_NOEXIST));
413		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
414					    BPF_NOEXIST));
415	}
416
417	assert(map_equal(lru_map_fd, expected_map_fd));
418
419	close(expected_map_fd);
420	close(lru_map_fd);
421
422	printf("Pass\n");
423}
424
425/* Size of the LRU map is 2*tgt_free
426 * It is to test the active/inactive list rotation
427 * Insert 1 to 2*tgt_free (+2*tgt_free keys)
428 * Lookup key 1 to tgt_free*3/2
429 * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys)
430 *  => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU
431 */
432static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
433{
434	unsigned long long key, end_key, value[nr_cpus];
435	int lru_map_fd, expected_map_fd;
436	unsigned int batch_size;
437	unsigned int map_size;
438	int next_cpu = 0;
439
440	if (map_flags & BPF_F_NO_COMMON_LRU)
441		/* This test is only applicable to common LRU list */
442		return;
443
444	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
445	       map_flags);
446
447	assert(sched_next_online(0, &next_cpu) != -1);
448
449	batch_size = tgt_free / 2;
450	assert(batch_size * 2 == tgt_free);
451
452	map_size = tgt_free * 2;
453	lru_map_fd = create_map(map_type, map_flags, map_size);
454	assert(lru_map_fd != -1);
455
456	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
457	assert(expected_map_fd != -1);
458
459	value[0] = 1234;
460
461	/* Insert 1 to 2*tgt_free (+2*tgt_free keys) */
462	end_key = 1 + (2 * tgt_free);
463	for (key = 1; key < end_key; key++)
464		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
465					    BPF_NOEXIST));
466
467	/* Lookup key 1 to tgt_free*3/2 */
468	end_key = tgt_free + batch_size;
469	for (key = 1; key < end_key; key++) {
470		assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
471		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
472					    BPF_NOEXIST));
473	}
474
475	/* Add 1+2*tgt_free to tgt_free*5/2
476	 * (+tgt_free/2 keys)
477	 */
478	key = 2 * tgt_free + 1;
479	end_key = key + batch_size;
480	for (; key < end_key; key++) {
481		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
482					    BPF_NOEXIST));
483		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
484					    BPF_NOEXIST));
485	}
486
487	assert(map_equal(lru_map_fd, expected_map_fd));
488
489	close(expected_map_fd);
490	close(lru_map_fd);
491
492	printf("Pass\n");
493}
494
495/* Test deletion */
496static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
497{
498	int lru_map_fd, expected_map_fd;
499	unsigned long long key, value[nr_cpus];
500	unsigned long long end_key;
501	int next_cpu = 0;
502
503	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
504	       map_flags);
505
506	assert(sched_next_online(0, &next_cpu) != -1);
507
508	if (map_flags & BPF_F_NO_COMMON_LRU)
509		lru_map_fd = create_map(map_type, map_flags,
510					3 * tgt_free * nr_cpus);
511	else
512		lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free);
513	assert(lru_map_fd != -1);
514
515	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0,
516				     3 * tgt_free);
517	assert(expected_map_fd != -1);
518
519	value[0] = 1234;
520
521	for (key = 1; key <= 2 * tgt_free; key++)
522		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
523					    BPF_NOEXIST));
524
525	key = 1;
526	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
527
528	for (key = 1; key <= tgt_free; key++) {
529		assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
530		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
531					    BPF_NOEXIST));
532	}
533
534	for (; key <= 2 * tgt_free; key++) {
535		assert(!bpf_map_delete_elem(lru_map_fd, &key));
536		assert(bpf_map_delete_elem(lru_map_fd, &key));
537	}
538
539	end_key = key + 2 * tgt_free;
540	for (; key < end_key; key++) {
541		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
542					    BPF_NOEXIST));
543		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
544					    BPF_NOEXIST));
545	}
546
547	assert(map_equal(lru_map_fd, expected_map_fd));
548
549	close(expected_map_fd);
550	close(lru_map_fd);
551
552	printf("Pass\n");
553}
554
555static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
556{
557	unsigned long long key, value[nr_cpus];
558
559	/* Ensure the last key inserted by previous CPU can be found */
560	assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value));
 
561	value[0] = 1234;
562
563	key = last_key + 1;
564	assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
565	assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value));
566
567	/* Cannot find the last key because it was removed by LRU */
568	assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -1 &&
569	       errno == ENOENT);
570}
571
572/* Test map with only one element */
573static void test_lru_sanity5(int map_type, int map_flags)
574{
575	unsigned long long key, value[nr_cpus];
576	int next_cpu = 0;
577	int map_fd;
578
579	if (map_flags & BPF_F_NO_COMMON_LRU)
580		return;
581
582	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
583	       map_flags);
584
585	map_fd = create_map(map_type, map_flags, 1);
586	assert(map_fd != -1);
587
588	value[0] = 1234;
589	key = 0;
590	assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
591
592	while (sched_next_online(0, &next_cpu) != -1) {
593		pid_t pid;
594
595		pid = fork();
596		if (pid == 0) {
597			do_test_lru_sanity5(key, map_fd);
598			exit(0);
599		} else if (pid == -1) {
600			printf("couldn't spawn process to test key:%llu\n",
601			       key);
602			exit(1);
603		} else {
604			int status;
605
606			assert(waitpid(pid, &status, 0) == pid);
607			assert(status == 0);
608			key++;
609		}
610	}
611
612	close(map_fd);
613	/* At least one key should be tested */
614	assert(key > 0);
615
616	printf("Pass\n");
617}
618
619/* Test list rotation for BPF_F_NO_COMMON_LRU map */
620static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
621{
622	int lru_map_fd, expected_map_fd;
623	unsigned long long key, value[nr_cpus];
624	unsigned int map_size = tgt_free * 2;
625	int next_cpu = 0;
626
627	if (!(map_flags & BPF_F_NO_COMMON_LRU))
628		return;
629
630	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
631	       map_flags);
632
633	assert(sched_next_online(0, &next_cpu) != -1);
634
635	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
636	assert(expected_map_fd != -1);
637
638	lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus);
639	assert(lru_map_fd != -1);
640
641	value[0] = 1234;
642
643	for (key = 1; key <= tgt_free; key++) {
644		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
645					    BPF_NOEXIST));
646		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
647					    BPF_NOEXIST));
648	}
649
650	for (; key <= tgt_free * 2; key++) {
651		unsigned long long stable_key;
652
653		/* Make ref bit sticky for key: [1, tgt_free] */
654		for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
655			/* Mark the ref bit */
656			assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd,
657								 stable_key, value));
658		}
659		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
660					    BPF_NOEXIST));
661	}
662
663	for (; key <= tgt_free * 3; key++) {
664		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
665					    BPF_NOEXIST));
666		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
667					    BPF_NOEXIST));
668	}
669
670	assert(map_equal(lru_map_fd, expected_map_fd));
671
672	close(expected_map_fd);
673	close(lru_map_fd);
674
675	printf("Pass\n");
676}
677
678/* Size of the LRU map is 2
679 * Add key=1 (+1 key)
680 * Add key=2 (+1 key)
681 * Lookup Key=1 (datapath)
682 * Lookup Key=2 (syscall)
683 * Add Key=3
684 *   => Key=2 will be removed by LRU
685 * Iterate map.  Only found key=1 and key=3
686 */
687static void test_lru_sanity7(int map_type, int map_flags)
688{
689	unsigned long long key, value[nr_cpus];
690	int lru_map_fd, expected_map_fd;
691	int next_cpu = 0;
692
693	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
694	       map_flags);
695
696	assert(sched_next_online(0, &next_cpu) != -1);
697
698	if (map_flags & BPF_F_NO_COMMON_LRU)
699		lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
700	else
701		lru_map_fd = create_map(map_type, map_flags, 2);
702	assert(lru_map_fd != -1);
703
704	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
705	assert(expected_map_fd != -1);
706
707	value[0] = 1234;
708
709	/* insert key=1 element */
710
711	key = 1;
712	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
713	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
714				    BPF_NOEXIST));
715
716	/* BPF_NOEXIST means: add new element if it doesn't exist */
717	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
718	       /* key=1 already exists */
719	       && errno == EEXIST);
720
721	/* insert key=2 element */
722
723	/* check that key=2 is not found */
724	key = 2;
725	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
726	       errno == ENOENT);
727
728	/* BPF_EXIST means: update existing element */
729	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
730	       /* key=2 is not there */
731	       errno == ENOENT);
732
733	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
734
735	/* insert key=3 element */
736
737	/* check that key=3 is not found */
738	key = 3;
739	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
740	       errno == ENOENT);
741
742	/* check that key=1 can be found and mark the ref bit to
743	 * stop LRU from removing key=1
744	 */
745	key = 1;
746	assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
747	assert(value[0] == 1234);
748
749	/* check that key=2 can be found and do _not_ mark ref bit.
750	 * this will be evicted on next update.
751	 */
752	key = 2;
753	assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
754	assert(value[0] == 1234);
755
756	key = 3;
757	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
758	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
759				    BPF_NOEXIST));
760
761	/* key=2 has been removed from the LRU */
762	key = 2;
763	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
764	       errno == ENOENT);
765
766	assert(map_equal(lru_map_fd, expected_map_fd));
767
768	close(expected_map_fd);
769	close(lru_map_fd);
770
771	printf("Pass\n");
772}
773
774/* Size of the LRU map is 2
775 * Add key=1 (+1 key)
776 * Add key=2 (+1 key)
777 * Lookup Key=1 (syscall)
778 * Lookup Key=2 (datapath)
779 * Add Key=3
780 *   => Key=1 will be removed by LRU
781 * Iterate map.  Only found key=2 and key=3
782 */
783static void test_lru_sanity8(int map_type, int map_flags)
784{
785	unsigned long long key, value[nr_cpus];
786	int lru_map_fd, expected_map_fd;
787	int next_cpu = 0;
788
789	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
790	       map_flags);
791
792	assert(sched_next_online(0, &next_cpu) != -1);
793
794	if (map_flags & BPF_F_NO_COMMON_LRU)
795		lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
796	else
797		lru_map_fd = create_map(map_type, map_flags, 2);
798	assert(lru_map_fd != -1);
799
800	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
801	assert(expected_map_fd != -1);
802
803	value[0] = 1234;
804
805	/* insert key=1 element */
806
807	key = 1;
808	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
809
810	/* BPF_NOEXIST means: add new element if it doesn't exist */
811	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
812	       /* key=1 already exists */
813	       && errno == EEXIST);
814
815	/* insert key=2 element */
816
817	/* check that key=2 is not found */
818	key = 2;
819	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
820	       errno == ENOENT);
821
822	/* BPF_EXIST means: update existing element */
823	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
824	       /* key=2 is not there */
825	       errno == ENOENT);
826
827	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
828	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
829				    BPF_NOEXIST));
830
831	/* insert key=3 element */
832
833	/* check that key=3 is not found */
834	key = 3;
835	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
836	       errno == ENOENT);
837
838	/* check that key=1 can be found and do _not_ mark ref bit.
839	 * this will be evicted on next update.
840	 */
841	key = 1;
842	assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
843	assert(value[0] == 1234);
844
845	/* check that key=2 can be found and mark the ref bit to
846	 * stop LRU from removing key=2
847	 */
848	key = 2;
849	assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
850	assert(value[0] == 1234);
851
852	key = 3;
853	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
854	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
855				    BPF_NOEXIST));
856
857	/* key=1 has been removed from the LRU */
858	key = 1;
859	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
860	       errno == ENOENT);
861
862	assert(map_equal(lru_map_fd, expected_map_fd));
863
864	close(expected_map_fd);
865	close(lru_map_fd);
866
867	printf("Pass\n");
868}
869
870int main(int argc, char **argv)
871{
872	int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
873			     BPF_MAP_TYPE_LRU_PERCPU_HASH};
874	int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
875	int t, f;
876
877	setbuf(stdout, NULL);
878
879	nr_cpus = bpf_num_possible_cpus();
880	assert(nr_cpus != -1);
881	printf("nr_cpus:%d\n\n", nr_cpus);
882
883	for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) {
884		unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ?
885			PERCPU_FREE_TARGET : LOCAL_FREE_TARGET;
886
887		for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) {
888			test_lru_sanity0(map_types[t], map_flags[f]);
889			test_lru_sanity1(map_types[t], map_flags[f], tgt_free);
890			test_lru_sanity2(map_types[t], map_flags[f], tgt_free);
891			test_lru_sanity3(map_types[t], map_flags[f], tgt_free);
892			test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
893			test_lru_sanity5(map_types[t], map_flags[f]);
894			test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
895			test_lru_sanity7(map_types[t], map_flags[f]);
896			test_lru_sanity8(map_types[t], map_flags[f]);
897
898			printf("\n");
899		}
900	}
901
902	return 0;
903}
v4.17
 
  1/*
  2 * Copyright (c) 2016 Facebook
  3 *
  4 * This program is free software; you can redistribute it and/or
  5 * modify it under the terms of version 2 of the GNU General Public
  6 * License as published by the Free Software Foundation.
  7 */
  8#define _GNU_SOURCE
  9#include <stdio.h>
 10#include <unistd.h>
 11#include <errno.h>
 12#include <string.h>
 13#include <assert.h>
 14#include <sched.h>
 15#include <stdlib.h>
 16#include <time.h>
 17
 18#include <sys/wait.h>
 19
 20#include <bpf/bpf.h>
 
 21
 22#include "bpf_util.h"
 23#include "bpf_rlimit.h"
 
 24
 25#define LOCAL_FREE_TARGET	(128)
 26#define PERCPU_FREE_TARGET	(4)
 27
 28static int nr_cpus;
 29
 30static int create_map(int map_type, int map_flags, unsigned int size)
 31{
 32	int map_fd;
 33
 34	map_fd = bpf_create_map(map_type, sizeof(unsigned long long),
 35				sizeof(unsigned long long), size, map_flags);
 36
 37	if (map_fd == -1)
 38		perror("bpf_create_map");
 39
 40	return map_fd;
 41}
 42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 43static int map_subset(int map0, int map1)
 44{
 45	unsigned long long next_key = 0;
 46	unsigned long long value0[nr_cpus], value1[nr_cpus];
 47	int ret;
 48
 49	while (!bpf_map_get_next_key(map1, &next_key, &next_key)) {
 50		assert(!bpf_map_lookup_elem(map1, &next_key, value1));
 51		ret = bpf_map_lookup_elem(map0, &next_key, value0);
 52		if (ret) {
 53			printf("key:%llu not found from map. %s(%d)\n",
 54			       next_key, strerror(errno), errno);
 55			return 0;
 56		}
 57		if (value0[0] != value1[0]) {
 58			printf("key:%llu value0:%llu != value1:%llu\n",
 59			       next_key, value0[0], value1[0]);
 60			return 0;
 61		}
 62	}
 63	return 1;
 64}
 65
 66static int map_equal(int lru_map, int expected)
 67{
 68	return map_subset(lru_map, expected) && map_subset(expected, lru_map);
 69}
 70
 71static int sched_next_online(int pid, int *next_to_try)
 72{
 73	cpu_set_t cpuset;
 74	int next = *next_to_try;
 75	int ret = -1;
 76
 77	while (next < nr_cpus) {
 78		CPU_ZERO(&cpuset);
 79		CPU_SET(next++, &cpuset);
 80		if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) {
 81			ret = 0;
 82			break;
 83		}
 84	}
 85
 86	*next_to_try = next;
 87	return ret;
 88}
 89
 90/* Size of the LRU amp is 2
 91 * Add key=1 (+1 key)
 92 * Add key=2 (+1 key)
 93 * Lookup Key=1
 94 * Add Key=3
 95 *   => Key=2 will be removed by LRU
 96 * Iterate map.  Only found key=1 and key=3
 97 */
 98static void test_lru_sanity0(int map_type, int map_flags)
 99{
100	unsigned long long key, value[nr_cpus];
101	int lru_map_fd, expected_map_fd;
102	int next_cpu = 0;
103
104	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
105	       map_flags);
106
107	assert(sched_next_online(0, &next_cpu) != -1);
108
109	if (map_flags & BPF_F_NO_COMMON_LRU)
110		lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
111	else
112		lru_map_fd = create_map(map_type, map_flags, 2);
113	assert(lru_map_fd != -1);
114
115	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
116	assert(expected_map_fd != -1);
117
118	value[0] = 1234;
119
120	/* insert key=1 element */
121
122	key = 1;
123	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
124	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
125				    BPF_NOEXIST));
126
127	/* BPF_NOEXIST means: add new element if it doesn't exist */
128	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
129	       /* key=1 already exists */
130	       && errno == EEXIST);
131
132	assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -1 &&
133	       errno == EINVAL);
134
135	/* insert key=2 element */
136
137	/* check that key=2 is not found */
138	key = 2;
139	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
140	       errno == ENOENT);
141
142	/* BPF_EXIST means: update existing element */
143	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
144	       /* key=2 is not there */
145	       errno == ENOENT);
146
147	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
148
149	/* insert key=3 element */
150
151	/* check that key=3 is not found */
152	key = 3;
153	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
154	       errno == ENOENT);
155
156	/* check that key=1 can be found and mark the ref bit to
157	 * stop LRU from removing key=1
158	 */
159	key = 1;
160	assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
161	assert(value[0] == 1234);
162
163	key = 3;
164	assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
165	assert(!bpf_map_update_elem(expected_map_fd, &key, value,
166				    BPF_NOEXIST));
167
168	/* key=2 has been removed from the LRU */
169	key = 2;
170	assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1);
 
171
172	assert(map_equal(lru_map_fd, expected_map_fd));
173
174	close(expected_map_fd);
175	close(lru_map_fd);
176
177	printf("Pass\n");
178}
179
180/* Size of the LRU map is 1.5*tgt_free
181 * Insert 1 to tgt_free (+tgt_free keys)
182 * Lookup 1 to tgt_free/2
183 * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys)
184 * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU
185 */
186static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
187{
188	unsigned long long key, end_key, value[nr_cpus];
189	int lru_map_fd, expected_map_fd;
190	unsigned int batch_size;
191	unsigned int map_size;
192	int next_cpu = 0;
193
194	if (map_flags & BPF_F_NO_COMMON_LRU)
195		/* This test is only applicable to common LRU list */
196		return;
197
198	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
199	       map_flags);
200
201	assert(sched_next_online(0, &next_cpu) != -1);
202
203	batch_size = tgt_free / 2;
204	assert(batch_size * 2 == tgt_free);
205
206	map_size = tgt_free + batch_size;
207	lru_map_fd = create_map(map_type, map_flags, map_size);
208	assert(lru_map_fd != -1);
209
210	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
211	assert(expected_map_fd != -1);
212
213	value[0] = 1234;
214
215	/* Insert 1 to tgt_free (+tgt_free keys) */
216	end_key = 1 + tgt_free;
217	for (key = 1; key < end_key; key++)
218		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
219					    BPF_NOEXIST));
220
221	/* Lookup 1 to tgt_free/2 */
222	end_key = 1 + batch_size;
223	for (key = 1; key < end_key; key++) {
224		assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
225		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
226					    BPF_NOEXIST));
227	}
228
229	/* Insert 1+tgt_free to 2*tgt_free
230	 * => 1+tgt_free/2 to LOCALFREE_TARGET will be
231	 * removed by LRU
232	 */
233	key = 1 + tgt_free;
234	end_key = key + tgt_free;
235	for (; key < end_key; key++) {
236		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
237					    BPF_NOEXIST));
238		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
239					    BPF_NOEXIST));
240	}
241
242	assert(map_equal(lru_map_fd, expected_map_fd));
243
244	close(expected_map_fd);
245	close(lru_map_fd);
246
247	printf("Pass\n");
248}
249
250/* Size of the LRU map 1.5 * tgt_free
251 * Insert 1 to tgt_free (+tgt_free keys)
252 * Update 1 to tgt_free/2
253 *   => The original 1 to tgt_free/2 will be removed due to
254 *      the LRU shrink process
255 * Re-insert 1 to tgt_free/2 again and do a lookup immeidately
256 * Insert 1+tgt_free to tgt_free*3/2
257 * Insert 1+tgt_free*3/2 to tgt_free*5/2
258 *   => Key 1+tgt_free to tgt_free*3/2
259 *      will be removed from LRU because it has never
260 *      been lookup and ref bit is not set
261 */
262static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
263{
264	unsigned long long key, value[nr_cpus];
265	unsigned long long end_key;
266	int lru_map_fd, expected_map_fd;
267	unsigned int batch_size;
268	unsigned int map_size;
269	int next_cpu = 0;
270
271	if (map_flags & BPF_F_NO_COMMON_LRU)
272		/* This test is only applicable to common LRU list */
273		return;
274
275	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
276	       map_flags);
277
278	assert(sched_next_online(0, &next_cpu) != -1);
279
280	batch_size = tgt_free / 2;
281	assert(batch_size * 2 == tgt_free);
282
283	map_size = tgt_free + batch_size;
284	lru_map_fd = create_map(map_type, map_flags, map_size);
285	assert(lru_map_fd != -1);
286
287	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
288	assert(expected_map_fd != -1);
289
290	value[0] = 1234;
291
292	/* Insert 1 to tgt_free (+tgt_free keys) */
293	end_key = 1 + tgt_free;
294	for (key = 1; key < end_key; key++)
295		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
296					    BPF_NOEXIST));
297
298	/* Any bpf_map_update_elem will require to acquire a new node
299	 * from LRU first.
300	 *
301	 * The local list is running out of free nodes.
302	 * It gets from the global LRU list which tries to
303	 * shrink the inactive list to get tgt_free
304	 * number of free nodes.
305	 *
306	 * Hence, the oldest key 1 to tgt_free/2
307	 * are removed from the LRU list.
308	 */
309	key = 1;
310	if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
311		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
312					    BPF_NOEXIST));
313		assert(!bpf_map_delete_elem(lru_map_fd, &key));
314	} else {
315		assert(bpf_map_update_elem(lru_map_fd, &key, value,
316					   BPF_EXIST));
317	}
318
319	/* Re-insert 1 to tgt_free/2 again and do a lookup
320	 * immeidately.
321	 */
322	end_key = 1 + batch_size;
323	value[0] = 4321;
324	for (key = 1; key < end_key; key++) {
325		assert(bpf_map_lookup_elem(lru_map_fd, &key, value));
 
326		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
327					    BPF_NOEXIST));
328		assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
329		assert(value[0] == 4321);
330		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
331					    BPF_NOEXIST));
332	}
333
334	value[0] = 1234;
335
336	/* Insert 1+tgt_free to tgt_free*3/2 */
337	end_key = 1 + tgt_free + batch_size;
338	for (key = 1 + tgt_free; key < end_key; key++)
339		/* These newly added but not referenced keys will be
340		 * gone during the next LRU shrink.
341		 */
342		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
343					    BPF_NOEXIST));
344
345	/* Insert 1+tgt_free*3/2 to  tgt_free*5/2 */
346	end_key = key + tgt_free;
347	for (; key < end_key; key++) {
348		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
349					    BPF_NOEXIST));
350		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
351					    BPF_NOEXIST));
352	}
353
354	assert(map_equal(lru_map_fd, expected_map_fd));
355
356	close(expected_map_fd);
357	close(lru_map_fd);
358
359	printf("Pass\n");
360}
361
362/* Size of the LRU map is 2*tgt_free
363 * It is to test the active/inactive list rotation
364 * Insert 1 to 2*tgt_free (+2*tgt_free keys)
365 * Lookup key 1 to tgt_free*3/2
366 * Add 1+2*tgt_free to tgt_free*5/2 (+tgt_free/2 keys)
367 *  => key 1+tgt_free*3/2 to 2*tgt_free are removed from LRU
368 */
369static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
370{
371	unsigned long long key, end_key, value[nr_cpus];
372	int lru_map_fd, expected_map_fd;
373	unsigned int batch_size;
374	unsigned int map_size;
375	int next_cpu = 0;
376
377	if (map_flags & BPF_F_NO_COMMON_LRU)
378		/* This test is only applicable to common LRU list */
379		return;
380
381	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
382	       map_flags);
383
384	assert(sched_next_online(0, &next_cpu) != -1);
385
386	batch_size = tgt_free / 2;
387	assert(batch_size * 2 == tgt_free);
388
389	map_size = tgt_free * 2;
390	lru_map_fd = create_map(map_type, map_flags, map_size);
391	assert(lru_map_fd != -1);
392
393	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
394	assert(expected_map_fd != -1);
395
396	value[0] = 1234;
397
398	/* Insert 1 to 2*tgt_free (+2*tgt_free keys) */
399	end_key = 1 + (2 * tgt_free);
400	for (key = 1; key < end_key; key++)
401		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
402					    BPF_NOEXIST));
403
404	/* Lookup key 1 to tgt_free*3/2 */
405	end_key = tgt_free + batch_size;
406	for (key = 1; key < end_key; key++) {
407		assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
408		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
409					    BPF_NOEXIST));
410	}
411
412	/* Add 1+2*tgt_free to tgt_free*5/2
413	 * (+tgt_free/2 keys)
414	 */
415	key = 2 * tgt_free + 1;
416	end_key = key + batch_size;
417	for (; key < end_key; key++) {
418		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
419					    BPF_NOEXIST));
420		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
421					    BPF_NOEXIST));
422	}
423
424	assert(map_equal(lru_map_fd, expected_map_fd));
425
426	close(expected_map_fd);
427	close(lru_map_fd);
428
429	printf("Pass\n");
430}
431
432/* Test deletion */
433static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
434{
435	int lru_map_fd, expected_map_fd;
436	unsigned long long key, value[nr_cpus];
437	unsigned long long end_key;
438	int next_cpu = 0;
439
440	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
441	       map_flags);
442
443	assert(sched_next_online(0, &next_cpu) != -1);
444
445	if (map_flags & BPF_F_NO_COMMON_LRU)
446		lru_map_fd = create_map(map_type, map_flags,
447					3 * tgt_free * nr_cpus);
448	else
449		lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free);
450	assert(lru_map_fd != -1);
451
452	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0,
453				     3 * tgt_free);
454	assert(expected_map_fd != -1);
455
456	value[0] = 1234;
457
458	for (key = 1; key <= 2 * tgt_free; key++)
459		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
460					    BPF_NOEXIST));
461
462	key = 1;
463	assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
464
465	for (key = 1; key <= tgt_free; key++) {
466		assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
467		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
468					    BPF_NOEXIST));
469	}
470
471	for (; key <= 2 * tgt_free; key++) {
472		assert(!bpf_map_delete_elem(lru_map_fd, &key));
473		assert(bpf_map_delete_elem(lru_map_fd, &key));
474	}
475
476	end_key = key + 2 * tgt_free;
477	for (; key < end_key; key++) {
478		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
479					    BPF_NOEXIST));
480		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
481					    BPF_NOEXIST));
482	}
483
484	assert(map_equal(lru_map_fd, expected_map_fd));
485
486	close(expected_map_fd);
487	close(lru_map_fd);
488
489	printf("Pass\n");
490}
491
492static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
493{
494	unsigned long long key, value[nr_cpus];
495
496	/* Ensure the last key inserted by previous CPU can be found */
497	assert(!bpf_map_lookup_elem(map_fd, &last_key, value));
498
499	value[0] = 1234;
500
501	key = last_key + 1;
502	assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
503	assert(!bpf_map_lookup_elem(map_fd, &key, value));
504
505	/* Cannot find the last key because it was removed by LRU */
506	assert(bpf_map_lookup_elem(map_fd, &last_key, value));
 
507}
508
509/* Test map with only one element */
510static void test_lru_sanity5(int map_type, int map_flags)
511{
512	unsigned long long key, value[nr_cpus];
513	int next_cpu = 0;
514	int map_fd;
515
516	if (map_flags & BPF_F_NO_COMMON_LRU)
517		return;
518
519	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
520	       map_flags);
521
522	map_fd = create_map(map_type, map_flags, 1);
523	assert(map_fd != -1);
524
525	value[0] = 1234;
526	key = 0;
527	assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
528
529	while (sched_next_online(0, &next_cpu) != -1) {
530		pid_t pid;
531
532		pid = fork();
533		if (pid == 0) {
534			do_test_lru_sanity5(key, map_fd);
535			exit(0);
536		} else if (pid == -1) {
537			printf("couldn't spawn process to test key:%llu\n",
538			       key);
539			exit(1);
540		} else {
541			int status;
542
543			assert(waitpid(pid, &status, 0) == pid);
544			assert(status == 0);
545			key++;
546		}
547	}
548
549	close(map_fd);
550	/* At least one key should be tested */
551	assert(key > 0);
552
553	printf("Pass\n");
554}
555
556/* Test list rotation for BPF_F_NO_COMMON_LRU map */
557static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
558{
559	int lru_map_fd, expected_map_fd;
560	unsigned long long key, value[nr_cpus];
561	unsigned int map_size = tgt_free * 2;
562	int next_cpu = 0;
563
564	if (!(map_flags & BPF_F_NO_COMMON_LRU))
565		return;
566
567	printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
568	       map_flags);
569
570	assert(sched_next_online(0, &next_cpu) != -1);
571
572	expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
573	assert(expected_map_fd != -1);
574
575	lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus);
576	assert(lru_map_fd != -1);
577
578	value[0] = 1234;
579
580	for (key = 1; key <= tgt_free; key++) {
581		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
582					    BPF_NOEXIST));
583		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
584					    BPF_NOEXIST));
585	}
586
587	for (; key <= tgt_free * 2; key++) {
588		unsigned long long stable_key;
589
590		/* Make ref bit sticky for key: [1, tgt_free] */
591		for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
592			/* Mark the ref bit */
593			assert(!bpf_map_lookup_elem(lru_map_fd, &stable_key,
594						    value));
595		}
596		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
597					    BPF_NOEXIST));
598	}
599
600	for (; key <= tgt_free * 3; key++) {
601		assert(!bpf_map_update_elem(lru_map_fd, &key, value,
602					    BPF_NOEXIST));
603		assert(!bpf_map_update_elem(expected_map_fd, &key, value,
604					    BPF_NOEXIST));
605	}
606
607	assert(map_equal(lru_map_fd, expected_map_fd));
608
609	close(expected_map_fd);
610	close(lru_map_fd);
611
612	printf("Pass\n");
613}
614
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615int main(int argc, char **argv)
616{
617	int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
618			     BPF_MAP_TYPE_LRU_PERCPU_HASH};
619	int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
620	int t, f;
621
622	setbuf(stdout, NULL);
623
624	nr_cpus = bpf_num_possible_cpus();
625	assert(nr_cpus != -1);
626	printf("nr_cpus:%d\n\n", nr_cpus);
627
628	for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) {
629		unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ?
630			PERCPU_FREE_TARGET : LOCAL_FREE_TARGET;
631
632		for (t = 0; t < sizeof(map_types) / sizeof(*map_types); t++) {
633			test_lru_sanity0(map_types[t], map_flags[f]);
634			test_lru_sanity1(map_types[t], map_flags[f], tgt_free);
635			test_lru_sanity2(map_types[t], map_flags[f], tgt_free);
636			test_lru_sanity3(map_types[t], map_flags[f], tgt_free);
637			test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
638			test_lru_sanity5(map_types[t], map_flags[f]);
639			test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
 
 
640
641			printf("\n");
642		}
643	}
644
645	return 0;
646}