test_maps.c 13 KB

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