PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/samples/bpf/test_lru_dist.c

https://github.com/gby/linux
C | 543 lines | 421 code | 110 blank | 12 comment | 65 complexity | 63030a6fba585edce7dac15379cb3011 MD5 | raw file
  1. /*
  2. * Copyright (c) 2016 Facebook
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of version 2 of the GNU General Public
  6. * License as published by the Free Software Foundation.
  7. */
  8. #define _GNU_SOURCE
  9. #include <linux/types.h>
  10. #include <stdio.h>
  11. #include <unistd.h>
  12. #include <linux/bpf.h>
  13. #include <errno.h>
  14. #include <string.h>
  15. #include <assert.h>
  16. #include <sched.h>
  17. #include <sys/wait.h>
  18. #include <sys/stat.h>
  19. #include <sys/resource.h>
  20. #include <fcntl.h>
  21. #include <stdlib.h>
  22. #include <time.h>
  23. #include "libbpf.h"
  24. #include "bpf_util.h"
  25. #define min(a, b) ((a) < (b) ? (a) : (b))
  26. #ifndef offsetof
  27. # define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
  28. #endif
  29. #define container_of(ptr, type, member) ({ \
  30. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
  31. (type *)( (char *)__mptr - offsetof(type,member) );})
  32. static int nr_cpus;
  33. static unsigned long long *dist_keys;
  34. static unsigned int dist_key_counts;
  35. struct list_head {
  36. struct list_head *next, *prev;
  37. };
  38. static inline void INIT_LIST_HEAD(struct list_head *list)
  39. {
  40. list->next = list;
  41. list->prev = list;
  42. }
  43. static inline int list_empty(const struct list_head *head)
  44. {
  45. return head->next == head;
  46. }
  47. static inline void __list_add(struct list_head *new,
  48. struct list_head *prev,
  49. struct list_head *next)
  50. {
  51. next->prev = new;
  52. new->next = next;
  53. new->prev = prev;
  54. prev->next = new;
  55. }
  56. static inline void list_add(struct list_head *new, struct list_head *head)
  57. {
  58. __list_add(new, head, head->next);
  59. }
  60. static inline void __list_del(struct list_head *prev, struct list_head *next)
  61. {
  62. next->prev = prev;
  63. prev->next = next;
  64. }
  65. static inline void __list_del_entry(struct list_head *entry)
  66. {
  67. __list_del(entry->prev, entry->next);
  68. }
  69. static inline void list_move(struct list_head *list, struct list_head *head)
  70. {
  71. __list_del_entry(list);
  72. list_add(list, head);
  73. }
  74. #define list_entry(ptr, type, member) \
  75. container_of(ptr, type, member)
  76. #define list_last_entry(ptr, type, member) \
  77. list_entry((ptr)->prev, type, member)
  78. struct pfect_lru_node {
  79. struct list_head list;
  80. unsigned long long key;
  81. };
  82. struct pfect_lru {
  83. struct list_head list;
  84. struct pfect_lru_node *free_nodes;
  85. unsigned int cur_size;
  86. unsigned int lru_size;
  87. unsigned int nr_unique;
  88. unsigned int nr_misses;
  89. unsigned int total;
  90. int map_fd;
  91. };
  92. static void pfect_lru_init(struct pfect_lru *lru, unsigned int lru_size,
  93. unsigned int nr_possible_elems)
  94. {
  95. lru->map_fd = bpf_create_map(BPF_MAP_TYPE_HASH,
  96. sizeof(unsigned long long),
  97. sizeof(struct pfect_lru_node *),
  98. nr_possible_elems, 0);
  99. assert(lru->map_fd != -1);
  100. lru->free_nodes = malloc(lru_size * sizeof(struct pfect_lru_node));
  101. assert(lru->free_nodes);
  102. INIT_LIST_HEAD(&lru->list);
  103. lru->cur_size = 0;
  104. lru->lru_size = lru_size;
  105. lru->nr_unique = lru->nr_misses = lru->total = 0;
  106. }
  107. static void pfect_lru_destroy(struct pfect_lru *lru)
  108. {
  109. close(lru->map_fd);
  110. free(lru->free_nodes);
  111. }
  112. static int pfect_lru_lookup_or_insert(struct pfect_lru *lru,
  113. unsigned long long key)
  114. {
  115. struct pfect_lru_node *node = NULL;
  116. int seen = 0;
  117. lru->total++;
  118. if (!bpf_map_lookup_elem(lru->map_fd, &key, &node)) {
  119. if (node) {
  120. list_move(&node->list, &lru->list);
  121. return 1;
  122. }
  123. seen = 1;
  124. }
  125. if (lru->cur_size < lru->lru_size) {
  126. node = &lru->free_nodes[lru->cur_size++];
  127. INIT_LIST_HEAD(&node->list);
  128. } else {
  129. struct pfect_lru_node *null_node = NULL;
  130. node = list_last_entry(&lru->list,
  131. struct pfect_lru_node,
  132. list);
  133. bpf_map_update_elem(lru->map_fd, &node->key, &null_node, BPF_EXIST);
  134. }
  135. node->key = key;
  136. list_move(&node->list, &lru->list);
  137. lru->nr_misses++;
  138. if (seen) {
  139. assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_EXIST));
  140. } else {
  141. lru->nr_unique++;
  142. assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_NOEXIST));
  143. }
  144. return seen;
  145. }
  146. static unsigned int read_keys(const char *dist_file,
  147. unsigned long long **keys)
  148. {
  149. struct stat fst;
  150. unsigned long long *retkeys;
  151. unsigned int counts = 0;
  152. int dist_fd;
  153. char *b, *l;
  154. int i;
  155. dist_fd = open(dist_file, 0);
  156. assert(dist_fd != -1);
  157. assert(fstat(dist_fd, &fst) == 0);
  158. b = malloc(fst.st_size);
  159. assert(b);
  160. assert(read(dist_fd, b, fst.st_size) == fst.st_size);
  161. close(dist_fd);
  162. for (i = 0; i < fst.st_size; i++) {
  163. if (b[i] == '\n')
  164. counts++;
  165. }
  166. counts++; /* in case the last line has no \n */
  167. retkeys = malloc(counts * sizeof(unsigned long long));
  168. assert(retkeys);
  169. counts = 0;
  170. for (l = strtok(b, "\n"); l; l = strtok(NULL, "\n"))
  171. retkeys[counts++] = strtoull(l, NULL, 10);
  172. free(b);
  173. *keys = retkeys;
  174. return counts;
  175. }
  176. static int create_map(int map_type, int map_flags, unsigned int size)
  177. {
  178. int map_fd;
  179. map_fd = bpf_create_map(map_type, sizeof(unsigned long long),
  180. sizeof(unsigned long long), size, map_flags);
  181. if (map_fd == -1)
  182. perror("bpf_create_map");
  183. return map_fd;
  184. }
  185. static int sched_next_online(int pid, int next_to_try)
  186. {
  187. cpu_set_t cpuset;
  188. if (next_to_try == nr_cpus)
  189. return -1;
  190. while (next_to_try < nr_cpus) {
  191. CPU_ZERO(&cpuset);
  192. CPU_SET(next_to_try++, &cpuset);
  193. if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset))
  194. break;
  195. }
  196. return next_to_try;
  197. }
  198. static void run_parallel(unsigned int tasks, void (*fn)(int i, void *data),
  199. void *data)
  200. {
  201. int next_sched_cpu = 0;
  202. pid_t pid[tasks];
  203. int i;
  204. for (i = 0; i < tasks; i++) {
  205. pid[i] = fork();
  206. if (pid[i] == 0) {
  207. next_sched_cpu = sched_next_online(0, next_sched_cpu);
  208. fn(i, data);
  209. exit(0);
  210. } else if (pid[i] == -1) {
  211. printf("couldn't spawn #%d process\n", i);
  212. exit(1);
  213. }
  214. /* It is mostly redundant and just allow the parent
  215. * process to update next_shced_cpu for the next child
  216. * process
  217. */
  218. next_sched_cpu = sched_next_online(pid[i], next_sched_cpu);
  219. }
  220. for (i = 0; i < tasks; i++) {
  221. int status;
  222. assert(waitpid(pid[i], &status, 0) == pid[i]);
  223. assert(status == 0);
  224. }
  225. }
  226. static void do_test_lru_dist(int task, void *data)
  227. {
  228. unsigned int nr_misses = 0;
  229. struct pfect_lru pfect_lru;
  230. unsigned long long key, value = 1234;
  231. unsigned int i;
  232. unsigned int lru_map_fd = ((unsigned int *)data)[0];
  233. unsigned int lru_size = ((unsigned int *)data)[1];
  234. unsigned long long key_offset = task * dist_key_counts;
  235. pfect_lru_init(&pfect_lru, lru_size, dist_key_counts);
  236. for (i = 0; i < dist_key_counts; i++) {
  237. key = dist_keys[i] + key_offset;
  238. pfect_lru_lookup_or_insert(&pfect_lru, key);
  239. if (!bpf_map_lookup_elem(lru_map_fd, &key, &value))
  240. continue;
  241. if (bpf_map_update_elem(lru_map_fd, &key, &value, BPF_NOEXIST)) {
  242. printf("bpf_map_update_elem(lru_map_fd, %llu): errno:%d\n",
  243. key, errno);
  244. assert(0);
  245. }
  246. nr_misses++;
  247. }
  248. printf(" task:%d BPF LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n",
  249. task, pfect_lru.nr_unique, dist_key_counts, nr_misses,
  250. dist_key_counts);
  251. printf(" task:%d Perfect LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n",
  252. task, pfect_lru.nr_unique, pfect_lru.total,
  253. pfect_lru.nr_misses, pfect_lru.total);
  254. pfect_lru_destroy(&pfect_lru);
  255. close(lru_map_fd);
  256. }
  257. static void test_parallel_lru_dist(int map_type, int map_flags,
  258. int nr_tasks, unsigned int lru_size)
  259. {
  260. int child_data[2];
  261. int lru_map_fd;
  262. printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type,
  263. map_flags);
  264. if (map_flags & BPF_F_NO_COMMON_LRU)
  265. lru_map_fd = create_map(map_type, map_flags,
  266. nr_cpus * lru_size);
  267. else
  268. lru_map_fd = create_map(map_type, map_flags,
  269. nr_tasks * lru_size);
  270. assert(lru_map_fd != -1);
  271. child_data[0] = lru_map_fd;
  272. child_data[1] = lru_size;
  273. run_parallel(nr_tasks, do_test_lru_dist, child_data);
  274. close(lru_map_fd);
  275. }
  276. static void test_lru_loss0(int map_type, int map_flags)
  277. {
  278. unsigned long long key, value[nr_cpus];
  279. unsigned int old_unused_losses = 0;
  280. unsigned int new_unused_losses = 0;
  281. unsigned int used_losses = 0;
  282. int map_fd;
  283. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  284. map_flags);
  285. assert(sched_next_online(0, 0) != -1);
  286. if (map_flags & BPF_F_NO_COMMON_LRU)
  287. map_fd = create_map(map_type, map_flags, 900 * nr_cpus);
  288. else
  289. map_fd = create_map(map_type, map_flags, 900);
  290. assert(map_fd != -1);
  291. value[0] = 1234;
  292. for (key = 1; key <= 1000; key++) {
  293. int start_key, end_key;
  294. assert(bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0);
  295. start_key = 101;
  296. end_key = min(key, 900);
  297. while (start_key <= end_key) {
  298. bpf_map_lookup_elem(map_fd, &start_key, value);
  299. start_key++;
  300. }
  301. }
  302. for (key = 1; key <= 1000; key++) {
  303. if (bpf_map_lookup_elem(map_fd, &key, value)) {
  304. if (key <= 100)
  305. old_unused_losses++;
  306. else if (key <= 900)
  307. used_losses++;
  308. else
  309. new_unused_losses++;
  310. }
  311. }
  312. close(map_fd);
  313. printf("older-elem-losses:%d(/100) active-elem-losses:%d(/800) "
  314. "newer-elem-losses:%d(/100)\n",
  315. old_unused_losses, used_losses, new_unused_losses);
  316. }
  317. static void test_lru_loss1(int map_type, int map_flags)
  318. {
  319. unsigned long long key, value[nr_cpus];
  320. int map_fd;
  321. unsigned int nr_losses = 0;
  322. printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
  323. map_flags);
  324. assert(sched_next_online(0, 0) != -1);
  325. if (map_flags & BPF_F_NO_COMMON_LRU)
  326. map_fd = create_map(map_type, map_flags, 1000 * nr_cpus);
  327. else
  328. map_fd = create_map(map_type, map_flags, 1000);
  329. assert(map_fd != -1);
  330. value[0] = 1234;
  331. for (key = 1; key <= 1000; key++)
  332. assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
  333. for (key = 1; key <= 1000; key++) {
  334. if (bpf_map_lookup_elem(map_fd, &key, value))
  335. nr_losses++;
  336. }
  337. close(map_fd);
  338. printf("nr_losses:%d(/1000)\n", nr_losses);
  339. }
  340. static void do_test_parallel_lru_loss(int task, void *data)
  341. {
  342. const unsigned int nr_stable_elems = 1000;
  343. const unsigned int nr_repeats = 100000;
  344. int map_fd = *(int *)data;
  345. unsigned long long stable_base;
  346. unsigned long long key, value[nr_cpus];
  347. unsigned long long next_ins_key;
  348. unsigned int nr_losses = 0;
  349. unsigned int i;
  350. stable_base = task * nr_repeats * 2 + 1;
  351. next_ins_key = stable_base;
  352. value[0] = 1234;
  353. for (i = 0; i < nr_stable_elems; i++) {
  354. assert(bpf_map_update_elem(map_fd, &next_ins_key, value,
  355. BPF_NOEXIST) == 0);
  356. next_ins_key++;
  357. }
  358. for (i = 0; i < nr_repeats; i++) {
  359. int rn;
  360. rn = rand();
  361. if (rn % 10) {
  362. key = rn % nr_stable_elems + stable_base;
  363. bpf_map_lookup_elem(map_fd, &key, value);
  364. } else {
  365. bpf_map_update_elem(map_fd, &next_ins_key, value,
  366. BPF_NOEXIST);
  367. next_ins_key++;
  368. }
  369. }
  370. key = stable_base;
  371. for (i = 0; i < nr_stable_elems; i++) {
  372. if (bpf_map_lookup_elem(map_fd, &key, value))
  373. nr_losses++;
  374. key++;
  375. }
  376. printf(" task:%d nr_losses:%u\n", task, nr_losses);
  377. }
  378. static void test_parallel_lru_loss(int map_type, int map_flags, int nr_tasks)
  379. {
  380. int map_fd;
  381. printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type,
  382. map_flags);
  383. /* Give 20% more than the active working set */
  384. if (map_flags & BPF_F_NO_COMMON_LRU)
  385. map_fd = create_map(map_type, map_flags,
  386. nr_cpus * (1000 + 200));
  387. else
  388. map_fd = create_map(map_type, map_flags,
  389. nr_tasks * (1000 + 200));
  390. assert(map_fd != -1);
  391. run_parallel(nr_tasks, do_test_parallel_lru_loss, &map_fd);
  392. close(map_fd);
  393. }
  394. int main(int argc, char **argv)
  395. {
  396. struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
  397. int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
  398. const char *dist_file;
  399. int nr_tasks = 1;
  400. int lru_size;
  401. int f;
  402. if (argc < 4) {
  403. printf("Usage: %s <dist-file> <lru-size> <nr-tasks>\n",
  404. argv[0]);
  405. return -1;
  406. }
  407. dist_file = argv[1];
  408. lru_size = atoi(argv[2]);
  409. nr_tasks = atoi(argv[3]);
  410. setbuf(stdout, NULL);
  411. assert(!setrlimit(RLIMIT_MEMLOCK, &r));
  412. srand(time(NULL));
  413. nr_cpus = bpf_num_possible_cpus();
  414. assert(nr_cpus != -1);
  415. printf("nr_cpus:%d\n\n", nr_cpus);
  416. nr_tasks = min(nr_tasks, nr_cpus);
  417. dist_key_counts = read_keys(dist_file, &dist_keys);
  418. if (!dist_key_counts) {
  419. printf("%s has no key\n", dist_file);
  420. return -1;
  421. }
  422. for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) {
  423. test_lru_loss0(BPF_MAP_TYPE_LRU_HASH, map_flags[f]);
  424. test_lru_loss1(BPF_MAP_TYPE_LRU_HASH, map_flags[f]);
  425. test_parallel_lru_loss(BPF_MAP_TYPE_LRU_HASH, map_flags[f],
  426. nr_tasks);
  427. test_parallel_lru_dist(BPF_MAP_TYPE_LRU_HASH, map_flags[f],
  428. nr_tasks, lru_size);
  429. printf("\n");
  430. }
  431. free(dist_keys);
  432. return 0;
  433. }