test_maps.c 13 KB

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