test_lru_map.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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. #include <sys/wait.h>
  15. #include <bpf/bpf.h>
  16. #include <bpf/libbpf.h>
  17. #include "bpf_util.h"
  18. #include "../../../include/linux/filter.h"
  19. #define LOCAL_FREE_TARGET (128)
  20. #define PERCPU_FREE_TARGET (4)
  21. static int nr_cpus;
  22. static int create_map(int map_type, int map_flags, unsigned int size)
  23. {
  24. LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = map_flags);
  25. int map_fd;
  26. map_fd = bpf_map_create(map_type, NULL, sizeof(unsigned long long),
  27. sizeof(unsigned long long), size, &opts);
  28. if (map_fd == -1)
  29. perror("bpf_map_create");
  30. return map_fd;
  31. }
  32. static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
  33. void *value)
  34. {
  35. struct bpf_insn insns[] = {
  36. BPF_LD_MAP_VALUE(BPF_REG_9, 0, 0),
  37. BPF_LD_MAP_FD(BPF_REG_1, fd),
  38. BPF_LD_IMM64(BPF_REG_3, key),
  39. BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
  40. BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
  41. BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
  42. BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
  43. BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
  44. BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
  45. BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_1, 0),
  46. BPF_MOV64_IMM(BPF_REG_0, 42),
  47. BPF_JMP_IMM(BPF_JA, 0, 0, 1),
  48. BPF_MOV64_IMM(BPF_REG_0, 1),
  49. BPF_EXIT_INSN(),
  50. };
  51. __u8 data[64] = {};
  52. int mfd, pfd, ret, zero = 0;
  53. LIBBPF_OPTS(bpf_test_run_opts, topts,
  54. .data_in = data,
  55. .data_size_in = sizeof(data),
  56. .repeat = 1,
  57. );
  58. mfd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), sizeof(__u64), 1, NULL);
  59. if (mfd < 0)
  60. return -1;
  61. insns[0].imm = mfd;
  62. pfd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, NULL, "GPL", insns, ARRAY_SIZE(insns), NULL);
  63. if (pfd < 0) {
  64. close(mfd);
  65. return -1;
  66. }
  67. ret = bpf_prog_test_run_opts(pfd, &topts);
  68. if (ret < 0 || topts.retval != 42) {
  69. ret = -1;
  70. } else {
  71. assert(!bpf_map_lookup_elem(mfd, &zero, value));
  72. ret = 0;
  73. }
  74. close(pfd);
  75. close(mfd);
  76. return ret;
  77. }
  78. static int map_subset(int map0, int map1)
  79. {
  80. unsigned long long next_key = 0;
  81. unsigned long long value0[nr_cpus], value1[nr_cpus];
  82. int ret;
  83. while (!bpf_map_get_next_key(map1, &next_key, &next_key)) {
  84. assert(!bpf_map_lookup_elem(map1, &next_key, value1));
  85. ret = bpf_map_lookup_elem(map0, &next_key, value0);
  86. if (ret) {
  87. printf("key:%llu not found from map. %s(%d)\n",
  88. next_key, strerror(errno), errno);
  89. return 0;
  90. }
  91. if (value0[0] != value1[0]) {
  92. printf("key:%llu value0:%llu != value1:%llu\n",
  93. next_key, value0[0], value1[0]);
  94. return 0;
  95. }
  96. }
  97. return 1;
  98. }
  99. static int map_equal(int lru_map, int expected)
  100. {
  101. return map_subset(lru_map, expected) && map_subset(expected, lru_map);
  102. }
  103. static int sched_next_online(int pid, int *next_to_try)
  104. {
  105. cpu_set_t cpuset;
  106. int next = *next_to_try;
  107. int ret = -1;
  108. while (next < nr_cpus) {
  109. CPU_ZERO(&cpuset);
  110. CPU_SET(next, &cpuset);
  111. next++;
  112. if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset)) {
  113. ret = 0;
  114. break;
  115. }
  116. }
  117. *next_to_try = next;
  118. return ret;
  119. }
  120. /* Derive target_free from map_size, same as bpf_common_lru_populate */
  121. static unsigned int __tgt_size(unsigned int map_size)
  122. {
  123. return (map_size / nr_cpus) / 2;
  124. }
  125. /* Inverse of how bpf_common_lru_populate derives target_free from map_size. */
  126. static unsigned int __map_size(unsigned int tgt_free)
  127. {
  128. return tgt_free * nr_cpus * 2;
  129. }
  130. /* Size of the LRU map is 2
  131. * Add key=1 (+1 key)
  132. * Add key=2 (+1 key)
  133. * Lookup Key=1
  134. * Add Key=3
  135. * => Key=2 will be removed by LRU
  136. * Iterate map. Only found key=1 and key=3
  137. */
  138. static void test_lru_sanity0(int map_type, int map_flags)
  139. {
  140. unsigned long long key, value[nr_cpus];
  141. int lru_map_fd, expected_map_fd;
  142. int next_cpu = 0;
  143. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  144. map_flags);
  145. assert(sched_next_online(0, &next_cpu) != -1);
  146. if (map_flags & BPF_F_NO_COMMON_LRU)
  147. lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
  148. else
  149. lru_map_fd = create_map(map_type, map_flags, 2);
  150. assert(lru_map_fd != -1);
  151. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
  152. assert(expected_map_fd != -1);
  153. value[0] = 1234;
  154. /* insert key=1 element */
  155. key = 1;
  156. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  157. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  158. BPF_NOEXIST));
  159. /* BPF_NOEXIST means: add new element if it doesn't exist */
  160. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
  161. /* key=1 already exists */
  162. assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -EINVAL);
  163. /* insert key=2 element */
  164. /* check that key=2 is not found */
  165. key = 2;
  166. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  167. /* BPF_EXIST means: update existing element */
  168. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
  169. /* key=2 is not there */
  170. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  171. /* insert key=3 element */
  172. /* check that key=3 is not found */
  173. key = 3;
  174. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  175. /* check that key=1 can be found and mark the ref bit to
  176. * stop LRU from removing key=1
  177. */
  178. key = 1;
  179. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  180. assert(value[0] == 1234);
  181. key = 3;
  182. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  183. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  184. BPF_NOEXIST));
  185. /* key=2 has been removed from the LRU */
  186. key = 2;
  187. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  188. /* lookup elem key=1 and delete it, then check it doesn't exist */
  189. key = 1;
  190. assert(!bpf_map_lookup_and_delete_elem(lru_map_fd, &key, &value));
  191. assert(value[0] == 1234);
  192. /* remove the same element from the expected map */
  193. assert(!bpf_map_delete_elem(expected_map_fd, &key));
  194. assert(map_equal(lru_map_fd, expected_map_fd));
  195. close(expected_map_fd);
  196. close(lru_map_fd);
  197. printf("Pass\n");
  198. }
  199. /* Verify that unreferenced elements are recycled before referenced ones.
  200. * Insert elements.
  201. * Reference a subset of these.
  202. * Insert more, enough to trigger recycling.
  203. * Verify that unreferenced are recycled.
  204. */
  205. static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
  206. {
  207. unsigned long long key, end_key, value[nr_cpus];
  208. int lru_map_fd, expected_map_fd;
  209. unsigned int batch_size;
  210. unsigned int map_size;
  211. int next_cpu = 0;
  212. if (map_flags & BPF_F_NO_COMMON_LRU)
  213. /* This test is only applicable to common LRU list */
  214. return;
  215. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  216. map_flags);
  217. assert(sched_next_online(0, &next_cpu) != -1);
  218. batch_size = tgt_free / 2;
  219. assert(batch_size * 2 == tgt_free);
  220. map_size = __map_size(tgt_free) + batch_size;
  221. lru_map_fd = create_map(map_type, map_flags, map_size);
  222. assert(lru_map_fd != -1);
  223. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
  224. assert(expected_map_fd != -1);
  225. value[0] = 1234;
  226. /* Insert map_size - batch_size keys */
  227. end_key = 1 + __map_size(tgt_free);
  228. for (key = 1; key < end_key; key++)
  229. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  230. BPF_NOEXIST));
  231. /* Lookup 1 to batch_size */
  232. end_key = 1 + batch_size;
  233. for (key = 1; key < end_key; key++) {
  234. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  235. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  236. BPF_NOEXIST));
  237. }
  238. /* Insert another map_size - batch_size keys
  239. * Map will contain 1 to batch_size plus these latest, i.e.,
  240. * => previous 1+batch_size to map_size - batch_size will have been
  241. * removed by LRU
  242. */
  243. key = 1 + __map_size(tgt_free);
  244. end_key = key + __map_size(tgt_free);
  245. for (; key < end_key; key++) {
  246. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  247. BPF_NOEXIST));
  248. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  249. BPF_NOEXIST));
  250. }
  251. assert(map_equal(lru_map_fd, expected_map_fd));
  252. close(expected_map_fd);
  253. close(lru_map_fd);
  254. printf("Pass\n");
  255. }
  256. /* Verify that insertions exceeding map size will recycle the oldest.
  257. * Verify that unreferenced elements are recycled before referenced.
  258. */
  259. static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
  260. {
  261. unsigned long long key, value[nr_cpus];
  262. unsigned long long end_key;
  263. int lru_map_fd, expected_map_fd;
  264. unsigned int batch_size;
  265. unsigned int map_size;
  266. int next_cpu = 0;
  267. if (map_flags & BPF_F_NO_COMMON_LRU)
  268. /* This test is only applicable to common LRU list */
  269. return;
  270. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  271. map_flags);
  272. assert(sched_next_online(0, &next_cpu) != -1);
  273. batch_size = tgt_free / 2;
  274. assert(batch_size * 2 == tgt_free);
  275. map_size = __map_size(tgt_free) + batch_size;
  276. lru_map_fd = create_map(map_type, map_flags, map_size);
  277. assert(lru_map_fd != -1);
  278. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
  279. assert(expected_map_fd != -1);
  280. value[0] = 1234;
  281. /* Insert map_size - batch_size keys */
  282. end_key = 1 + __map_size(tgt_free);
  283. for (key = 1; key < end_key; key++)
  284. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  285. BPF_NOEXIST));
  286. /* Any bpf_map_update_elem will require to acquire a new node
  287. * from LRU first.
  288. *
  289. * The local list is running out of free nodes.
  290. * It gets from the global LRU list which tries to
  291. * shrink the inactive list to get tgt_free
  292. * number of free nodes.
  293. *
  294. * Hence, the oldest key is removed from the LRU list.
  295. */
  296. key = 1;
  297. if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) {
  298. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  299. BPF_NOEXIST));
  300. assert(!bpf_map_delete_elem(lru_map_fd, &key));
  301. } else {
  302. assert(bpf_map_update_elem(lru_map_fd, &key, value,
  303. BPF_EXIST));
  304. }
  305. /* Re-insert 1 to batch_size again and do a lookup immediately.
  306. */
  307. end_key = 1 + batch_size;
  308. value[0] = 4321;
  309. for (key = 1; key < end_key; key++) {
  310. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  311. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  312. BPF_NOEXIST));
  313. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  314. assert(value[0] == 4321);
  315. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  316. BPF_NOEXIST));
  317. }
  318. value[0] = 1234;
  319. /* Insert batch_size new elements */
  320. key = 1 + __map_size(tgt_free);
  321. end_key = key + batch_size;
  322. for (; key < end_key; key++)
  323. /* These newly added but not referenced keys will be
  324. * gone during the next LRU shrink.
  325. */
  326. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  327. BPF_NOEXIST));
  328. /* Insert map_size - batch_size elements */
  329. end_key += __map_size(tgt_free);
  330. for (; key < end_key; key++) {
  331. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  332. BPF_NOEXIST));
  333. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  334. BPF_NOEXIST));
  335. }
  336. assert(map_equal(lru_map_fd, expected_map_fd));
  337. close(expected_map_fd);
  338. close(lru_map_fd);
  339. printf("Pass\n");
  340. }
  341. /* Test the active/inactive list rotation
  342. *
  343. * Fill the whole map, deplete the free list.
  344. * Reference all except the last lru->target_free elements.
  345. * Insert lru->target_free new elements. This triggers one shrink.
  346. * Verify that the non-referenced elements are replaced.
  347. */
  348. static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
  349. {
  350. unsigned long long key, end_key, value[nr_cpus];
  351. int lru_map_fd, expected_map_fd;
  352. unsigned int batch_size;
  353. unsigned int map_size;
  354. int next_cpu = 0;
  355. if (map_flags & BPF_F_NO_COMMON_LRU)
  356. /* This test is only applicable to common LRU list */
  357. return;
  358. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  359. map_flags);
  360. assert(sched_next_online(0, &next_cpu) != -1);
  361. batch_size = __tgt_size(tgt_free);
  362. map_size = tgt_free * 2;
  363. lru_map_fd = create_map(map_type, map_flags, map_size);
  364. assert(lru_map_fd != -1);
  365. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
  366. assert(expected_map_fd != -1);
  367. value[0] = 1234;
  368. /* Fill the map */
  369. end_key = 1 + map_size;
  370. for (key = 1; key < end_key; key++)
  371. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  372. BPF_NOEXIST));
  373. /* Reference all but the last batch_size */
  374. end_key = 1 + map_size - batch_size;
  375. for (key = 1; key < end_key; key++) {
  376. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  377. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  378. BPF_NOEXIST));
  379. }
  380. /* Insert new batch_size: replaces the non-referenced elements */
  381. key = 2 * tgt_free + 1;
  382. end_key = key + batch_size;
  383. for (; key < end_key; key++) {
  384. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  385. BPF_NOEXIST));
  386. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  387. BPF_NOEXIST));
  388. }
  389. assert(map_equal(lru_map_fd, expected_map_fd));
  390. close(expected_map_fd);
  391. close(lru_map_fd);
  392. printf("Pass\n");
  393. }
  394. /* Test deletion */
  395. static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
  396. {
  397. int lru_map_fd, expected_map_fd;
  398. unsigned long long key, value[nr_cpus];
  399. unsigned long long end_key;
  400. int next_cpu = 0;
  401. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  402. map_flags);
  403. assert(sched_next_online(0, &next_cpu) != -1);
  404. if (map_flags & BPF_F_NO_COMMON_LRU)
  405. lru_map_fd = create_map(map_type, map_flags,
  406. 3 * tgt_free * nr_cpus);
  407. else
  408. lru_map_fd = create_map(map_type, map_flags,
  409. 3 * __map_size(tgt_free));
  410. assert(lru_map_fd != -1);
  411. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0,
  412. 3 * tgt_free);
  413. assert(expected_map_fd != -1);
  414. value[0] = 1234;
  415. for (key = 1; key <= 2 * tgt_free; key++)
  416. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  417. BPF_NOEXIST));
  418. key = 1;
  419. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  420. for (key = 1; key <= tgt_free; key++) {
  421. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  422. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  423. BPF_NOEXIST));
  424. }
  425. for (; key <= 2 * tgt_free; key++) {
  426. assert(!bpf_map_delete_elem(lru_map_fd, &key));
  427. assert(bpf_map_delete_elem(lru_map_fd, &key));
  428. }
  429. end_key = key + 2 * tgt_free;
  430. for (; key < end_key; key++) {
  431. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  432. BPF_NOEXIST));
  433. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  434. BPF_NOEXIST));
  435. }
  436. assert(map_equal(lru_map_fd, expected_map_fd));
  437. close(expected_map_fd);
  438. close(lru_map_fd);
  439. printf("Pass\n");
  440. }
  441. static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
  442. {
  443. unsigned long long key, value[nr_cpus];
  444. /* Ensure the last key inserted by previous CPU can be found */
  445. assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value));
  446. value[0] = 1234;
  447. key = last_key + 1;
  448. assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
  449. assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value));
  450. /* Cannot find the last key because it was removed by LRU */
  451. assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -ENOENT);
  452. }
  453. /* Test map with only one element */
  454. static void test_lru_sanity5(int map_type, int map_flags)
  455. {
  456. unsigned long long key, value[nr_cpus];
  457. int next_cpu = 0;
  458. int map_fd;
  459. if (map_flags & BPF_F_NO_COMMON_LRU)
  460. return;
  461. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  462. map_flags);
  463. map_fd = create_map(map_type, map_flags, 1);
  464. assert(map_fd != -1);
  465. value[0] = 1234;
  466. key = 0;
  467. assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
  468. while (sched_next_online(0, &next_cpu) != -1) {
  469. pid_t pid;
  470. pid = fork();
  471. if (pid == 0) {
  472. do_test_lru_sanity5(key, map_fd);
  473. exit(0);
  474. } else if (pid == -1) {
  475. printf("couldn't spawn process to test key:%llu\n",
  476. key);
  477. exit(1);
  478. } else {
  479. int status;
  480. assert(waitpid(pid, &status, 0) == pid);
  481. assert(status == 0);
  482. key++;
  483. }
  484. }
  485. close(map_fd);
  486. /* At least one key should be tested */
  487. assert(key > 0);
  488. printf("Pass\n");
  489. }
  490. /* Test list rotation for BPF_F_NO_COMMON_LRU map */
  491. static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
  492. {
  493. int lru_map_fd, expected_map_fd;
  494. unsigned long long key, value[nr_cpus];
  495. unsigned int map_size = tgt_free * 2;
  496. int next_cpu = 0;
  497. if (!(map_flags & BPF_F_NO_COMMON_LRU))
  498. return;
  499. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  500. map_flags);
  501. assert(sched_next_online(0, &next_cpu) != -1);
  502. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size);
  503. assert(expected_map_fd != -1);
  504. lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus);
  505. assert(lru_map_fd != -1);
  506. value[0] = 1234;
  507. for (key = 1; key <= tgt_free; key++) {
  508. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  509. BPF_NOEXIST));
  510. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  511. BPF_NOEXIST));
  512. }
  513. for (; key <= tgt_free * 2; key++) {
  514. unsigned long long stable_key;
  515. /* Make ref bit sticky for key: [1, tgt_free] */
  516. for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
  517. /* Mark the ref bit */
  518. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd,
  519. stable_key, value));
  520. }
  521. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  522. BPF_NOEXIST));
  523. }
  524. for (; key <= tgt_free * 3; key++) {
  525. assert(!bpf_map_update_elem(lru_map_fd, &key, value,
  526. BPF_NOEXIST));
  527. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  528. BPF_NOEXIST));
  529. }
  530. assert(map_equal(lru_map_fd, expected_map_fd));
  531. close(expected_map_fd);
  532. close(lru_map_fd);
  533. printf("Pass\n");
  534. }
  535. /* Size of the LRU map is 2
  536. * Add key=1 (+1 key)
  537. * Add key=2 (+1 key)
  538. * Lookup Key=1 (datapath)
  539. * Lookup Key=2 (syscall)
  540. * Add Key=3
  541. * => Key=2 will be removed by LRU
  542. * Iterate map. Only found key=1 and key=3
  543. */
  544. static void test_lru_sanity7(int map_type, int map_flags)
  545. {
  546. unsigned long long key, value[nr_cpus];
  547. int lru_map_fd, expected_map_fd;
  548. int next_cpu = 0;
  549. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  550. map_flags);
  551. assert(sched_next_online(0, &next_cpu) != -1);
  552. if (map_flags & BPF_F_NO_COMMON_LRU)
  553. lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
  554. else
  555. lru_map_fd = create_map(map_type, map_flags, 2);
  556. assert(lru_map_fd != -1);
  557. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
  558. assert(expected_map_fd != -1);
  559. value[0] = 1234;
  560. /* insert key=1 element */
  561. key = 1;
  562. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  563. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  564. BPF_NOEXIST));
  565. /* BPF_NOEXIST means: add new element if it doesn't exist */
  566. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
  567. /* key=1 already exists */
  568. /* insert key=2 element */
  569. /* check that key=2 is not found */
  570. key = 2;
  571. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  572. /* BPF_EXIST means: update existing element */
  573. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
  574. /* key=2 is not there */
  575. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  576. /* insert key=3 element */
  577. /* check that key=3 is not found */
  578. key = 3;
  579. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  580. /* check that key=1 can be found and mark the ref bit to
  581. * stop LRU from removing key=1
  582. */
  583. key = 1;
  584. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  585. assert(value[0] == 1234);
  586. /* check that key=2 can be found and do _not_ mark ref bit.
  587. * this will be evicted on next update.
  588. */
  589. key = 2;
  590. assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
  591. assert(value[0] == 1234);
  592. key = 3;
  593. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  594. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  595. BPF_NOEXIST));
  596. /* key=2 has been removed from the LRU */
  597. key = 2;
  598. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  599. assert(map_equal(lru_map_fd, expected_map_fd));
  600. close(expected_map_fd);
  601. close(lru_map_fd);
  602. printf("Pass\n");
  603. }
  604. /* Size of the LRU map is 2
  605. * Add key=1 (+1 key)
  606. * Add key=2 (+1 key)
  607. * Lookup Key=1 (syscall)
  608. * Lookup Key=2 (datapath)
  609. * Add Key=3
  610. * => Key=1 will be removed by LRU
  611. * Iterate map. Only found key=2 and key=3
  612. */
  613. static void test_lru_sanity8(int map_type, int map_flags)
  614. {
  615. unsigned long long key, value[nr_cpus];
  616. int lru_map_fd, expected_map_fd;
  617. int next_cpu = 0;
  618. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  619. map_flags);
  620. assert(sched_next_online(0, &next_cpu) != -1);
  621. if (map_flags & BPF_F_NO_COMMON_LRU)
  622. lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
  623. else
  624. lru_map_fd = create_map(map_type, map_flags, 2);
  625. assert(lru_map_fd != -1);
  626. expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
  627. assert(expected_map_fd != -1);
  628. value[0] = 1234;
  629. /* insert key=1 element */
  630. key = 1;
  631. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  632. /* BPF_NOEXIST means: add new element if it doesn't exist */
  633. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -EEXIST);
  634. /* key=1 already exists */
  635. /* insert key=2 element */
  636. /* check that key=2 is not found */
  637. key = 2;
  638. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  639. /* BPF_EXIST means: update existing element */
  640. assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -ENOENT);
  641. /* key=2 is not there */
  642. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  643. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  644. BPF_NOEXIST));
  645. /* insert key=3 element */
  646. /* check that key=3 is not found */
  647. key = 3;
  648. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  649. /* check that key=1 can be found and do _not_ mark ref bit.
  650. * this will be evicted on next update.
  651. */
  652. key = 1;
  653. assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
  654. assert(value[0] == 1234);
  655. /* check that key=2 can be found and mark the ref bit to
  656. * stop LRU from removing key=2
  657. */
  658. key = 2;
  659. assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
  660. assert(value[0] == 1234);
  661. key = 3;
  662. assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
  663. assert(!bpf_map_update_elem(expected_map_fd, &key, value,
  664. BPF_NOEXIST));
  665. /* key=1 has been removed from the LRU */
  666. key = 1;
  667. assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -ENOENT);
  668. assert(map_equal(lru_map_fd, expected_map_fd));
  669. close(expected_map_fd);
  670. close(lru_map_fd);
  671. printf("Pass\n");
  672. }
  673. int main(int argc, char **argv)
  674. {
  675. int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
  676. BPF_MAP_TYPE_LRU_PERCPU_HASH};
  677. int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
  678. int t, f;
  679. setbuf(stdout, NULL);
  680. nr_cpus = bpf_num_possible_cpus();
  681. assert(nr_cpus != -1);
  682. printf("nr_cpus:%d\n\n", nr_cpus);
  683. /* Use libbpf 1.0 API mode */
  684. libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
  685. for (f = 0; f < ARRAY_SIZE(map_flags); f++) {
  686. unsigned int tgt_free = (map_flags[f] & BPF_F_NO_COMMON_LRU) ?
  687. PERCPU_FREE_TARGET : LOCAL_FREE_TARGET;
  688. for (t = 0; t < ARRAY_SIZE(map_types); t++) {
  689. test_lru_sanity0(map_types[t], map_flags[f]);
  690. test_lru_sanity1(map_types[t], map_flags[f], tgt_free);
  691. test_lru_sanity2(map_types[t], map_flags[f], tgt_free);
  692. test_lru_sanity3(map_types[t], map_flags[f], tgt_free);
  693. test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
  694. test_lru_sanity5(map_types[t], map_flags[f]);
  695. test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
  696. test_lru_sanity7(map_types[t], map_flags[f]);
  697. test_lru_sanity8(map_types[t], map_flags[f]);
  698. printf("\n");
  699. }
  700. }
  701. return 0;
  702. }