test_maps.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. * Testsuite for eBPF maps
  3. *
  4. * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
  5. * Copyright (c) 2016 Facebook
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of version 2 of the GNU General Public
  9. * License as published by the Free Software Foundation.
  10. */
  11. #include <stdio.h>
  12. #include <unistd.h>
  13. #include <errno.h>
  14. #include <string.h>
  15. #include <assert.h>
  16. #include <stdlib.h>
  17. #include <sys/wait.h>
  18. #include <sys/resource.h>
  19. #include <linux/bpf.h>
  20. #include <bpf/bpf.h>
  21. #include "bpf_util.h"
  22. static int map_flags;
  23. static void test_hashmap(int task, void *data)
  24. {
  25. long long key, next_key, value;
  26. int fd;
  27. fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  28. 2, map_flags);
  29. if (fd < 0) {
  30. printf("Failed to create hashmap '%s'!\n", strerror(errno));
  31. exit(1);
  32. }
  33. key = 1;
  34. value = 1234;
  35. /* Insert key=1 element. */
  36. assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
  37. value = 0;
  38. /* BPF_NOEXIST means add new element if it doesn't exist. */
  39. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
  40. /* key=1 already exists. */
  41. errno == EEXIST);
  42. /* -1 is an invalid flag. */
  43. assert(bpf_map_update_elem(fd, &key, &value, -1) == -1 &&
  44. errno == EINVAL);
  45. /* Check that key=1 can be found. */
  46. assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
  47. key = 2;
  48. /* Check that key=2 is not found. */
  49. assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
  50. /* BPF_EXIST means update existing element. */
  51. assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 &&
  52. /* key=2 is not there. */
  53. errno == ENOENT);
  54. /* Insert key=2 element. */
  55. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
  56. /* key=1 and key=2 were inserted, check that key=0 cannot be
  57. * inserted due to max_entries limit.
  58. */
  59. key = 0;
  60. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
  61. errno == E2BIG);
  62. /* Update existing element, though the map is full. */
  63. key = 1;
  64. assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0);
  65. key = 2;
  66. assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
  67. key = 1;
  68. assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
  69. /* Check that key = 0 doesn't exist. */
  70. key = 0;
  71. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
  72. /* Iterate over two elements. */
  73. assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
  74. (next_key == 1 || next_key == 2));
  75. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
  76. (next_key == 1 || next_key == 2));
  77. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
  78. errno == ENOENT);
  79. /* Delete both elements. */
  80. key = 1;
  81. assert(bpf_map_delete_elem(fd, &key) == 0);
  82. key = 2;
  83. assert(bpf_map_delete_elem(fd, &key) == 0);
  84. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
  85. key = 0;
  86. /* Check that map is empty. */
  87. assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 &&
  88. errno == ENOENT);
  89. close(fd);
  90. }
  91. static void test_hashmap_percpu(int task, void *data)
  92. {
  93. unsigned int nr_cpus = bpf_num_possible_cpus();
  94. long long value[nr_cpus];
  95. long long key, next_key;
  96. int expected_key_mask = 0;
  97. int fd, i;
  98. fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
  99. sizeof(value[0]), 2, map_flags);
  100. if (fd < 0) {
  101. printf("Failed to create hashmap '%s'!\n", strerror(errno));
  102. exit(1);
  103. }
  104. for (i = 0; i < nr_cpus; i++)
  105. value[i] = i + 100;
  106. key = 1;
  107. /* Insert key=1 element. */
  108. assert(!(expected_key_mask & key));
  109. assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0);
  110. expected_key_mask |= key;
  111. /* BPF_NOEXIST means add new element if it doesn't exist. */
  112. assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 &&
  113. /* key=1 already exists. */
  114. errno == EEXIST);
  115. /* -1 is an invalid flag. */
  116. assert(bpf_map_update_elem(fd, &key, value, -1) == -1 &&
  117. errno == EINVAL);
  118. /* Check that key=1 can be found. Value could be 0 if the lookup
  119. * was run from a different CPU.
  120. */
  121. value[0] = 1;
  122. assert(bpf_map_lookup_elem(fd, &key, value) == 0 && value[0] == 100);
  123. key = 2;
  124. /* Check that key=2 is not found. */
  125. assert(bpf_map_lookup_elem(fd, &key, value) == -1 && errno == ENOENT);
  126. /* BPF_EXIST means update existing element. */
  127. assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == -1 &&
  128. /* key=2 is not there. */
  129. errno == ENOENT);
  130. /* Insert key=2 element. */
  131. assert(!(expected_key_mask & key));
  132. assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);
  133. expected_key_mask |= key;
  134. /* key=1 and key=2 were inserted, check that key=0 cannot be
  135. * inserted due to max_entries limit.
  136. */
  137. key = 0;
  138. assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 &&
  139. errno == E2BIG);
  140. /* Check that key = 0 doesn't exist. */
  141. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
  142. /* Iterate over two elements. */
  143. while (!bpf_map_get_next_key(fd, &key, &next_key)) {
  144. assert((expected_key_mask & next_key) == next_key);
  145. expected_key_mask &= ~next_key;
  146. assert(bpf_map_lookup_elem(fd, &next_key, value) == 0);
  147. for (i = 0; i < nr_cpus; i++)
  148. assert(value[i] == i + 100);
  149. key = next_key;
  150. }
  151. assert(errno == ENOENT);
  152. /* Update with BPF_EXIST. */
  153. key = 1;
  154. assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0);
  155. /* Delete both elements. */
  156. key = 1;
  157. assert(bpf_map_delete_elem(fd, &key) == 0);
  158. key = 2;
  159. assert(bpf_map_delete_elem(fd, &key) == 0);
  160. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT);
  161. key = 0;
  162. /* Check that map is empty. */
  163. assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 &&
  164. errno == ENOENT);
  165. close(fd);
  166. }
  167. static void test_arraymap(int task, void *data)
  168. {
  169. int key, next_key, fd;
  170. long long value;
  171. fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
  172. 2, 0);
  173. if (fd < 0) {
  174. printf("Failed to create arraymap '%s'!\n", strerror(errno));
  175. exit(1);
  176. }
  177. key = 1;
  178. value = 1234;
  179. /* Insert key=1 element. */
  180. assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
  181. value = 0;
  182. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
  183. errno == EEXIST);
  184. /* Check that key=1 can be found. */
  185. assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
  186. key = 0;
  187. /* Check that key=0 is also found and zero initialized. */
  188. assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
  189. /* key=0 and key=1 were inserted, check that key=2 cannot be inserted
  190. * due to max_entries limit.
  191. */
  192. key = 2;
  193. assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 &&
  194. errno == E2BIG);
  195. /* Check that key = 2 doesn't exist. */
  196. assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
  197. /* Iterate over two elements. */
  198. assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
  199. next_key == 0);
  200. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
  201. next_key == 1);
  202. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
  203. errno == ENOENT);
  204. /* Delete shouldn't succeed. */
  205. key = 1;
  206. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL);
  207. close(fd);
  208. }
  209. static void test_arraymap_percpu(int task, void *data)
  210. {
  211. unsigned int nr_cpus = bpf_num_possible_cpus();
  212. int key, next_key, fd, i;
  213. long values[nr_cpus];
  214. fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
  215. sizeof(values[0]), 2, 0);
  216. if (fd < 0) {
  217. printf("Failed to create arraymap '%s'!\n", strerror(errno));
  218. exit(1);
  219. }
  220. for (i = 0; i < nr_cpus; i++)
  221. values[i] = i + 100;
  222. key = 1;
  223. /* Insert key=1 element. */
  224. assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
  225. values[0] = 0;
  226. assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) == -1 &&
  227. errno == EEXIST);
  228. /* Check that key=1 can be found. */
  229. assert(bpf_map_lookup_elem(fd, &key, values) == 0 && values[0] == 100);
  230. key = 0;
  231. /* Check that key=0 is also found and zero initialized. */
  232. assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&
  233. values[0] == 0 && values[nr_cpus - 1] == 0);
  234. /* Check that key=2 cannot be inserted due to max_entries limit. */
  235. key = 2;
  236. assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) == -1 &&
  237. errno == E2BIG);
  238. /* Check that key = 2 doesn't exist. */
  239. assert(bpf_map_lookup_elem(fd, &key, values) == -1 && errno == ENOENT);
  240. /* Iterate over two elements. */
  241. assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
  242. next_key == 0);
  243. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
  244. next_key == 1);
  245. assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 &&
  246. errno == ENOENT);
  247. /* Delete shouldn't succeed. */
  248. key = 1;
  249. assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL);
  250. close(fd);
  251. }
  252. static void test_arraymap_percpu_many_keys(void)
  253. {
  254. unsigned int nr_cpus = bpf_num_possible_cpus();
  255. unsigned int nr_keys = 20000;
  256. long values[nr_cpus];
  257. int key, fd, i;
  258. fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
  259. sizeof(values[0]), nr_keys, 0);
  260. if (fd < 0) {
  261. printf("Failed to create per-cpu arraymap '%s'!\n",
  262. strerror(errno));
  263. exit(1);
  264. }
  265. for (i = 0; i < nr_cpus; i++)
  266. values[i] = i + 10;
  267. for (key = 0; key < nr_keys; key++)
  268. assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
  269. for (key = 0; key < nr_keys; key++) {
  270. for (i = 0; i < nr_cpus; i++)
  271. values[i] = 0;
  272. assert(bpf_map_lookup_elem(fd, &key, values) == 0);
  273. for (i = 0; i < nr_cpus; i++)
  274. assert(values[i] == i + 10);
  275. }
  276. close(fd);
  277. }
  278. #define MAP_SIZE (32 * 1024)
  279. static void test_map_large(void)
  280. {
  281. struct bigkey {
  282. int a;
  283. char b[116];
  284. long long c;
  285. } key;
  286. int fd, i, value;
  287. fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  288. MAP_SIZE, map_flags);
  289. if (fd < 0) {
  290. printf("Failed to create large map '%s'!\n", strerror(errno));
  291. exit(1);
  292. }
  293. for (i = 0; i < MAP_SIZE; i++) {
  294. key = (struct bigkey) { .c = i };
  295. value = i;
  296. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
  297. }
  298. key.c = -1;
  299. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
  300. errno == E2BIG);
  301. /* Iterate through all elements. */
  302. for (i = 0; i < MAP_SIZE; i++)
  303. assert(bpf_map_get_next_key(fd, &key, &key) == 0);
  304. assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  305. key.c = 0;
  306. assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
  307. key.a = 1;
  308. assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
  309. close(fd);
  310. }
  311. static void run_parallel(int tasks, void (*fn)(int task, void *data),
  312. void *data)
  313. {
  314. pid_t pid[tasks];
  315. int i;
  316. for (i = 0; i < tasks; i++) {
  317. pid[i] = fork();
  318. if (pid[i] == 0) {
  319. fn(i, data);
  320. exit(0);
  321. } else if (pid[i] == -1) {
  322. printf("Couldn't spawn #%d process!\n", i);
  323. exit(1);
  324. }
  325. }
  326. for (i = 0; i < tasks; i++) {
  327. int status;
  328. assert(waitpid(pid[i], &status, 0) == pid[i]);
  329. assert(status == 0);
  330. }
  331. }
  332. static void test_map_stress(void)
  333. {
  334. run_parallel(100, test_hashmap, NULL);
  335. run_parallel(100, test_hashmap_percpu, NULL);
  336. run_parallel(100, test_arraymap, NULL);
  337. run_parallel(100, test_arraymap_percpu, NULL);
  338. }
  339. #define TASKS 1024
  340. #define DO_UPDATE 1
  341. #define DO_DELETE 0
  342. static void do_work(int fn, void *data)
  343. {
  344. int do_update = ((int *)data)[1];
  345. int fd = ((int *)data)[0];
  346. int i, key, value;
  347. for (i = fn; i < MAP_SIZE; i += TASKS) {
  348. key = value = i;
  349. if (do_update) {
  350. assert(bpf_map_update_elem(fd, &key, &value,
  351. BPF_NOEXIST) == 0);
  352. assert(bpf_map_update_elem(fd, &key, &value,
  353. BPF_EXIST) == 0);
  354. } else {
  355. assert(bpf_map_delete_elem(fd, &key) == 0);
  356. }
  357. }
  358. }
  359. static void test_map_parallel(void)
  360. {
  361. int i, fd, key = 0, value = 0;
  362. int data[2];
  363. fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  364. MAP_SIZE, map_flags);
  365. if (fd < 0) {
  366. printf("Failed to create map for parallel test '%s'!\n",
  367. strerror(errno));
  368. exit(1);
  369. }
  370. /* Use the same fd in children to add elements to this map:
  371. * child_0 adds key=0, key=1024, key=2048, ...
  372. * child_1 adds key=1, key=1025, key=2049, ...
  373. * child_1023 adds key=1023, ...
  374. */
  375. data[0] = fd;
  376. data[1] = DO_UPDATE;
  377. run_parallel(TASKS, do_work, data);
  378. /* Check that key=0 is already there. */
  379. assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
  380. errno == EEXIST);
  381. /* Check that all elements were inserted. */
  382. key = -1;
  383. for (i = 0; i < MAP_SIZE; i++)
  384. assert(bpf_map_get_next_key(fd, &key, &key) == 0);
  385. assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  386. /* Another check for all elements */
  387. for (i = 0; i < MAP_SIZE; i++) {
  388. key = MAP_SIZE - i - 1;
  389. assert(bpf_map_lookup_elem(fd, &key, &value) == 0 &&
  390. value == key);
  391. }
  392. /* Now let's delete all elemenets in parallel. */
  393. data[1] = DO_DELETE;
  394. run_parallel(TASKS, do_work, data);
  395. /* Nothing should be left. */
  396. key = -1;
  397. assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  398. }
  399. static void run_all_tests(void)
  400. {
  401. test_hashmap(0, NULL);
  402. test_hashmap_percpu(0, NULL);
  403. test_arraymap(0, NULL);
  404. test_arraymap_percpu(0, NULL);
  405. test_arraymap_percpu_many_keys();
  406. test_map_large();
  407. test_map_parallel();
  408. test_map_stress();
  409. }
  410. int main(void)
  411. {
  412. struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
  413. setrlimit(RLIMIT_MEMLOCK, &rinf);
  414. map_flags = 0;
  415. run_all_tests();
  416. map_flags = BPF_F_NO_PREALLOC;
  417. run_all_tests();
  418. printf("test_maps: OK\n");
  419. return 0;
  420. }