test_maps.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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_sys.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_map_create(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(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(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(fd, &key, &value, -1) == -1 && errno == EINVAL);
  44. /* Check that key=1 can be found. */
  45. assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234);
  46. key = 2;
  47. /* Check that key=2 is not found. */
  48. assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
  49. /* BPF_EXIST means update existing element. */
  50. assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 &&
  51. /* key=2 is not there. */
  52. errno == ENOENT);
  53. /* Insert key=2 element. */
  54. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
  55. /* key=1 and key=2 were inserted, check that key=0 cannot be
  56. * inserted due to max_entries limit.
  57. */
  58. key = 0;
  59. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
  60. errno == E2BIG);
  61. /* Update existing element, though the map is full. */
  62. key = 1;
  63. assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0);
  64. key = 2;
  65. assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
  66. key = 1;
  67. assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0);
  68. /* Check that key = 0 doesn't exist. */
  69. key = 0;
  70. assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
  71. /* Iterate over two elements. */
  72. assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
  73. (next_key == 1 || next_key == 2));
  74. assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
  75. (next_key == 1 || next_key == 2));
  76. assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
  77. errno == ENOENT);
  78. /* Delete both elements. */
  79. key = 1;
  80. assert(bpf_map_delete(fd, &key) == 0);
  81. key = 2;
  82. assert(bpf_map_delete(fd, &key) == 0);
  83. assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
  84. key = 0;
  85. /* Check that map is empty. */
  86. assert(bpf_map_next_key(fd, &key, &next_key) == -1 &&
  87. errno == ENOENT);
  88. close(fd);
  89. }
  90. static void test_hashmap_percpu(int task, void *data)
  91. {
  92. unsigned int nr_cpus = bpf_num_possible_cpus();
  93. long long value[nr_cpus];
  94. long long key, next_key;
  95. int expected_key_mask = 0;
  96. int fd, i;
  97. fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key),
  98. sizeof(value[0]), 2, map_flags);
  99. if (fd < 0) {
  100. printf("Failed to create hashmap '%s'!\n", strerror(errno));
  101. exit(1);
  102. }
  103. for (i = 0; i < nr_cpus; i++)
  104. value[i] = i + 100;
  105. key = 1;
  106. /* Insert key=1 element. */
  107. assert(!(expected_key_mask & key));
  108. assert(bpf_map_update(fd, &key, value, BPF_ANY) == 0);
  109. expected_key_mask |= key;
  110. /* BPF_NOEXIST means add new element if it doesn't exist. */
  111. assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 &&
  112. /* key=1 already exists. */
  113. errno == EEXIST);
  114. /* -1 is an invalid flag. */
  115. assert(bpf_map_update(fd, &key, value, -1) == -1 && 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_map_lookup(fd, &key, value) == 0 && value[0] == 100);
  121. key = 2;
  122. /* Check that key=2 is not found. */
  123. assert(bpf_map_lookup(fd, &key, value) == -1 && errno == ENOENT);
  124. /* BPF_EXIST means update existing element. */
  125. assert(bpf_map_update(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_map_update(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
  133. * inserted due to max_entries limit.
  134. */
  135. key = 0;
  136. assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 &&
  137. errno == E2BIG);
  138. /* Check that key = 0 doesn't exist. */
  139. assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
  140. /* Iterate over two elements. */
  141. while (!bpf_map_next_key(fd, &key, &next_key)) {
  142. assert((expected_key_mask & next_key) == next_key);
  143. expected_key_mask &= ~next_key;
  144. assert(bpf_map_lookup(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_map_update(fd, &key, value, BPF_EXIST) == 0);
  153. /* Delete both elements. */
  154. key = 1;
  155. assert(bpf_map_delete(fd, &key) == 0);
  156. key = 2;
  157. assert(bpf_map_delete(fd, &key) == 0);
  158. assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT);
  159. key = 0;
  160. /* Check that map is empty. */
  161. assert(bpf_map_next_key(fd, &key, &next_key) == -1 &&
  162. errno == ENOENT);
  163. close(fd);
  164. }
  165. static void test_arraymap(int task, void *data)
  166. {
  167. int key, next_key, fd;
  168. long long value;
  169. fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value),
  170. 2, 0);
  171. if (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_map_update(fd, &key, &value, BPF_ANY) == 0);
  179. value = 0;
  180. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
  181. errno == EEXIST);
  182. /* Check that key=1 can be found. */
  183. assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234);
  184. key = 0;
  185. /* Check that key=0 is also found and zero initialized. */
  186. assert(bpf_map_lookup(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_map_update(fd, &key, &value, BPF_EXIST) == -1 &&
  192. errno == E2BIG);
  193. /* Check that key = 2 doesn't exist. */
  194. assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
  195. /* Iterate over two elements. */
  196. assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
  197. next_key == 0);
  198. assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
  199. next_key == 1);
  200. assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
  201. errno == ENOENT);
  202. /* Delete shouldn't succeed. */
  203. key = 1;
  204. assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL);
  205. close(fd);
  206. }
  207. static void test_arraymap_percpu(int task, void *data)
  208. {
  209. unsigned int nr_cpus = bpf_num_possible_cpus();
  210. int key, next_key, fd, i;
  211. long values[nr_cpus];
  212. fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
  213. sizeof(values[0]), 2, 0);
  214. if (fd < 0) {
  215. printf("Failed to create arraymap '%s'!\n", strerror(errno));
  216. exit(1);
  217. }
  218. for (i = 0; i < nr_cpus; i++)
  219. values[i] = i + 100;
  220. key = 1;
  221. /* Insert key=1 element. */
  222. assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0);
  223. values[0] = 0;
  224. assert(bpf_map_update(fd, &key, values, BPF_NOEXIST) == -1 &&
  225. errno == EEXIST);
  226. /* Check that key=1 can be found. */
  227. assert(bpf_map_lookup(fd, &key, values) == 0 && values[0] == 100);
  228. key = 0;
  229. /* Check that key=0 is also found and zero initialized. */
  230. assert(bpf_map_lookup(fd, &key, values) == 0 &&
  231. values[0] == 0 && values[nr_cpus - 1] == 0);
  232. /* Check that key=2 cannot be inserted due to max_entries limit. */
  233. key = 2;
  234. assert(bpf_map_update(fd, &key, values, BPF_EXIST) == -1 &&
  235. errno == E2BIG);
  236. /* Check that key = 2 doesn't exist. */
  237. assert(bpf_map_lookup(fd, &key, values) == -1 && errno == ENOENT);
  238. /* Iterate over two elements. */
  239. assert(bpf_map_next_key(fd, &key, &next_key) == 0 &&
  240. next_key == 0);
  241. assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 &&
  242. next_key == 1);
  243. assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 &&
  244. errno == ENOENT);
  245. /* Delete shouldn't succeed. */
  246. key = 1;
  247. assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL);
  248. close(fd);
  249. }
  250. static void test_arraymap_percpu_many_keys(void)
  251. {
  252. unsigned int nr_cpus = bpf_num_possible_cpus();
  253. unsigned int nr_keys = 20000;
  254. long values[nr_cpus];
  255. int key, fd, i;
  256. fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key),
  257. sizeof(values[0]), nr_keys, 0);
  258. if (fd < 0) {
  259. printf("Failed to create per-cpu arraymap '%s'!\n",
  260. strerror(errno));
  261. exit(1);
  262. }
  263. for (i = 0; i < nr_cpus; i++)
  264. values[i] = i + 10;
  265. for (key = 0; key < nr_keys; key++)
  266. assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0);
  267. for (key = 0; key < nr_keys; key++) {
  268. for (i = 0; i < nr_cpus; i++)
  269. values[i] = 0;
  270. assert(bpf_map_lookup(fd, &key, values) == 0);
  271. for (i = 0; i < nr_cpus; i++)
  272. assert(values[i] == i + 10);
  273. }
  274. close(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 fd, i, value;
  285. fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  286. MAP_SIZE, map_flags);
  287. if (fd < 0) {
  288. printf("Failed to create large map '%s'!\n", strerror(errno));
  289. exit(1);
  290. }
  291. for (i = 0; i < MAP_SIZE; i++) {
  292. key = (struct bigkey) { .c = i };
  293. value = i;
  294. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
  295. }
  296. key.c = -1;
  297. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
  298. errno == E2BIG);
  299. /* Iterate through all elements. */
  300. for (i = 0; i < MAP_SIZE; i++)
  301. assert(bpf_map_next_key(fd, &key, &key) == 0);
  302. assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  303. key.c = 0;
  304. assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0);
  305. key.a = 1;
  306. assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT);
  307. close(fd);
  308. }
  309. static void run_parallel(int tasks, void (*fn)(int task, void *data),
  310. void *data)
  311. {
  312. pid_t pid[tasks];
  313. int i;
  314. for (i = 0; i < tasks; i++) {
  315. pid[i] = fork();
  316. if (pid[i] == 0) {
  317. fn(i, data);
  318. exit(0);
  319. } else if (pid[i] == -1) {
  320. printf("Couldn't spawn #%d process!\n", i);
  321. exit(1);
  322. }
  323. }
  324. for (i = 0; i < tasks; i++) {
  325. int status;
  326. assert(waitpid(pid[i], &status, 0) == pid[i]);
  327. assert(status == 0);
  328. }
  329. }
  330. static void test_map_stress(void)
  331. {
  332. run_parallel(100, test_hashmap, NULL);
  333. run_parallel(100, test_hashmap_percpu, NULL);
  334. run_parallel(100, test_arraymap, NULL);
  335. run_parallel(100, test_arraymap_percpu, NULL);
  336. }
  337. #define TASKS 1024
  338. #define DO_UPDATE 1
  339. #define DO_DELETE 0
  340. static void do_work(int fn, void *data)
  341. {
  342. int do_update = ((int *)data)[1];
  343. int fd = ((int *)data)[0];
  344. int i, key, value;
  345. for (i = fn; i < MAP_SIZE; i += TASKS) {
  346. key = value = i;
  347. if (do_update) {
  348. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0);
  349. assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0);
  350. } else {
  351. assert(bpf_map_delete(fd, &key) == 0);
  352. }
  353. }
  354. }
  355. static void test_map_parallel(void)
  356. {
  357. int i, fd, key = 0, value = 0;
  358. int data[2];
  359. fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
  360. MAP_SIZE, map_flags);
  361. if (fd < 0) {
  362. printf("Failed to create map for parallel test '%s'!\n",
  363. strerror(errno));
  364. exit(1);
  365. }
  366. /* Use the same fd in children to add elements to this map:
  367. * child_0 adds key=0, key=1024, key=2048, ...
  368. * child_1 adds key=1, key=1025, key=2049, ...
  369. * child_1023 adds key=1023, ...
  370. */
  371. data[0] = fd;
  372. data[1] = DO_UPDATE;
  373. run_parallel(TASKS, do_work, data);
  374. /* Check that key=0 is already there. */
  375. assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 &&
  376. errno == EEXIST);
  377. /* Check that all elements were inserted. */
  378. key = -1;
  379. for (i = 0; i < MAP_SIZE; i++)
  380. assert(bpf_map_next_key(fd, &key, &key) == 0);
  381. assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  382. /* Another check for all elements */
  383. for (i = 0; i < MAP_SIZE; i++) {
  384. key = MAP_SIZE - i - 1;
  385. assert(bpf_map_lookup(fd, &key, &value) == 0 &&
  386. value == key);
  387. }
  388. /* Now let's delete all elemenets in parallel. */
  389. data[1] = DO_DELETE;
  390. run_parallel(TASKS, do_work, data);
  391. /* Nothing should be left. */
  392. key = -1;
  393. assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT);
  394. }
  395. static void run_all_tests(void)
  396. {
  397. test_hashmap(0, NULL);
  398. test_hashmap_percpu(0, NULL);
  399. test_arraymap(0, NULL);
  400. test_arraymap_percpu(0, NULL);
  401. test_arraymap_percpu_many_keys();
  402. test_map_large();
  403. test_map_parallel();
  404. test_map_stress();
  405. }
  406. int main(void)
  407. {
  408. struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
  409. setrlimit(RLIMIT_MEMLOCK, &rinf);
  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. }