bench-lookups.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Author: Dmitry Safonov <dima@arista.com> */
  3. #include <arpa/inet.h>
  4. #include <inttypes.h>
  5. #include <math.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <time.h>
  9. #include "../../../../include/linux/bits.h"
  10. #include "../../../../include/linux/kernel.h"
  11. #include "aolib.h"
  12. #define BENCH_NR_ITERS 100 /* number of times to run gathering statistics */
  13. static void gen_test_ips(union tcp_addr *ips, size_t ips_nr, bool use_rand)
  14. {
  15. union tcp_addr net = {};
  16. size_t i, j;
  17. if (inet_pton(TEST_FAMILY, TEST_NETWORK, &net) != 1)
  18. test_error("Can't convert ip address %s", TEST_NETWORK);
  19. if (!use_rand) {
  20. for (i = 0; i < ips_nr; i++)
  21. ips[i] = gen_tcp_addr(net, 2 * i + 1);
  22. return;
  23. }
  24. for (i = 0; i < ips_nr; i++) {
  25. size_t r = (size_t)random() | 0x1;
  26. ips[i] = gen_tcp_addr(net, r);
  27. for (j = i - 1; j > 0 && i > 0; j--) {
  28. if (!memcmp(&ips[i], &ips[j], sizeof(union tcp_addr))) {
  29. i--; /* collision */
  30. break;
  31. }
  32. }
  33. }
  34. }
  35. static void test_add_routes(union tcp_addr *ips, size_t ips_nr)
  36. {
  37. size_t i;
  38. for (i = 0; i < ips_nr; i++) {
  39. union tcp_addr *p = (union tcp_addr *)&ips[i];
  40. int err;
  41. err = ip_route_add(veth_name, TEST_FAMILY, this_ip_addr, *p);
  42. if (err && err != -EEXIST)
  43. test_error("Failed to add route");
  44. }
  45. }
  46. static void server_apply_keys(int lsk, union tcp_addr *ips, size_t ips_nr)
  47. {
  48. size_t i;
  49. for (i = 0; i < ips_nr; i++) {
  50. union tcp_addr *p = (union tcp_addr *)&ips[i];
  51. if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, *p, -1, 100, 100))
  52. test_error("setsockopt(TCP_AO)");
  53. }
  54. }
  55. static const size_t nr_keys[] = { 512, 1024, 2048, 4096, 8192 };
  56. static union tcp_addr *test_ips;
  57. struct bench_stats {
  58. uint64_t min;
  59. uint64_t max;
  60. uint64_t nr;
  61. double mean;
  62. double s2;
  63. };
  64. static struct bench_tests {
  65. struct bench_stats delete_last_key;
  66. struct bench_stats add_key;
  67. struct bench_stats delete_rand_key;
  68. struct bench_stats connect_last_key;
  69. struct bench_stats connect_rand_key;
  70. struct bench_stats delete_async;
  71. } bench_results[ARRAY_SIZE(nr_keys)];
  72. #define NSEC_PER_SEC 1000000000ULL
  73. static void measure_call(struct bench_stats *st,
  74. void (*f)(int, void *), int sk, void *arg)
  75. {
  76. struct timespec start = {}, end = {};
  77. double delta;
  78. uint64_t nsec;
  79. if (clock_gettime(CLOCK_MONOTONIC, &start))
  80. test_error("clock_gettime()");
  81. f(sk, arg);
  82. if (clock_gettime(CLOCK_MONOTONIC, &end))
  83. test_error("clock_gettime()");
  84. nsec = (end.tv_sec - start.tv_sec) * NSEC_PER_SEC;
  85. if (end.tv_nsec >= start.tv_nsec)
  86. nsec += end.tv_nsec - start.tv_nsec;
  87. else
  88. nsec -= start.tv_nsec - end.tv_nsec;
  89. if (st->nr == 0) {
  90. st->min = st->max = nsec;
  91. } else {
  92. if (st->min > nsec)
  93. st->min = nsec;
  94. if (st->max < nsec)
  95. st->max = nsec;
  96. }
  97. /* Welford-Knuth algorithm */
  98. st->nr++;
  99. delta = (double)nsec - st->mean;
  100. st->mean += delta / st->nr;
  101. st->s2 += delta * ((double)nsec - st->mean);
  102. }
  103. static void delete_mkt(int sk, void *arg)
  104. {
  105. struct tcp_ao_del *ao = arg;
  106. if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL_KEY, ao, sizeof(*ao)))
  107. test_error("setsockopt(TCP_AO_DEL_KEY)");
  108. }
  109. static void add_back_mkt(int sk, void *arg)
  110. {
  111. union tcp_addr *p = arg;
  112. if (test_add_key(sk, DEFAULT_TEST_PASSWORD, *p, -1, 100, 100))
  113. test_error("setsockopt(TCP_AO)");
  114. }
  115. static void bench_delete(int lsk, struct bench_stats *add,
  116. struct bench_stats *del,
  117. union tcp_addr *ips, size_t ips_nr,
  118. bool rand_order, bool async)
  119. {
  120. struct tcp_ao_del ao_del = {};
  121. union tcp_addr *p;
  122. size_t i;
  123. ao_del.sndid = 100;
  124. ao_del.rcvid = 100;
  125. ao_del.del_async = !!async;
  126. ao_del.prefix = DEFAULT_TEST_PREFIX;
  127. /* Remove the first added */
  128. p = (union tcp_addr *)&ips[0];
  129. tcp_addr_to_sockaddr_in(&ao_del.addr, p, 0);
  130. for (i = 0; i < BENCH_NR_ITERS; i++) {
  131. measure_call(del, delete_mkt, lsk, (void *)&ao_del);
  132. /* Restore it back */
  133. measure_call(add, add_back_mkt, lsk, (void *)p);
  134. /*
  135. * Slowest for FILO-linked-list:
  136. * on (i) iteration removing ips[i] element. When it gets
  137. * added to the list back - it becomes first to fetch, so
  138. * on (i + 1) iteration go to ips[i + 1] element.
  139. */
  140. if (rand_order)
  141. p = (union tcp_addr *)&ips[rand() % ips_nr];
  142. else
  143. p = (union tcp_addr *)&ips[i % ips_nr];
  144. tcp_addr_to_sockaddr_in(&ao_del.addr, p, 0);
  145. }
  146. }
  147. static void bench_connect_srv(int lsk, union tcp_addr *ips, size_t ips_nr)
  148. {
  149. size_t i;
  150. for (i = 0; i < BENCH_NR_ITERS; i++) {
  151. int sk;
  152. synchronize_threads();
  153. if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))
  154. test_error("test_wait_fd()");
  155. sk = accept(lsk, NULL, NULL);
  156. if (sk < 0)
  157. test_error("accept()");
  158. close(sk);
  159. }
  160. }
  161. static void test_print_stats(const char *desc, size_t nr, struct bench_stats *bs)
  162. {
  163. test_ok("%-20s\t%zu keys: min=%" PRIu64 "ms max=%" PRIu64 "ms mean=%gms stddev=%g",
  164. desc, nr, bs->min / 1000000, bs->max / 1000000,
  165. bs->mean / 1000000, sqrt((bs->mean / 1000000) / bs->nr));
  166. }
  167. static void *server_fn(void *arg)
  168. {
  169. size_t i;
  170. for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
  171. struct bench_tests *bt = &bench_results[i];
  172. int lsk;
  173. test_ips = malloc(nr_keys[i] * sizeof(union tcp_addr));
  174. if (!test_ips)
  175. test_error("malloc()");
  176. lsk = test_listen_socket(this_ip_addr, test_server_port + i, 1);
  177. gen_test_ips(test_ips, nr_keys[i], false);
  178. test_add_routes(test_ips, nr_keys[i]);
  179. test_set_optmem(KERNEL_TCP_AO_KEY_SZ_ROUND_UP * nr_keys[i]);
  180. server_apply_keys(lsk, test_ips, nr_keys[i]);
  181. synchronize_threads();
  182. bench_connect_srv(lsk, test_ips, nr_keys[i]);
  183. bench_connect_srv(lsk, test_ips, nr_keys[i]);
  184. /* The worst case for FILO-list */
  185. bench_delete(lsk, &bt->add_key, &bt->delete_last_key,
  186. test_ips, nr_keys[i], false, false);
  187. test_print_stats("Add a new key",
  188. nr_keys[i], &bt->add_key);
  189. test_print_stats("Delete: worst case",
  190. nr_keys[i], &bt->delete_last_key);
  191. bench_delete(lsk, &bt->add_key, &bt->delete_rand_key,
  192. test_ips, nr_keys[i], true, false);
  193. test_print_stats("Delete: random-search",
  194. nr_keys[i], &bt->delete_rand_key);
  195. bench_delete(lsk, &bt->add_key, &bt->delete_async,
  196. test_ips, nr_keys[i], false, true);
  197. test_print_stats("Delete: async", nr_keys[i], &bt->delete_async);
  198. free(test_ips);
  199. close(lsk);
  200. }
  201. return NULL;
  202. }
  203. static void connect_client(int sk, void *arg)
  204. {
  205. size_t *p = arg;
  206. if (test_connect_socket(sk, this_ip_dest, test_server_port + *p) <= 0)
  207. test_error("failed to connect()");
  208. }
  209. static void client_addr_setup(int sk, union tcp_addr taddr)
  210. {
  211. #ifdef IPV6_TEST
  212. struct sockaddr_in6 addr = {
  213. .sin6_family = AF_INET6,
  214. .sin6_port = 0,
  215. .sin6_addr = taddr.a6,
  216. };
  217. #else
  218. struct sockaddr_in addr = {
  219. .sin_family = AF_INET,
  220. .sin_port = 0,
  221. .sin_addr = taddr.a4,
  222. };
  223. #endif
  224. int ret;
  225. ret = ip_addr_add(veth_name, TEST_FAMILY, taddr, TEST_PREFIX);
  226. if (ret && ret != -EEXIST)
  227. test_error("Failed to add ip address");
  228. ret = ip_route_add(veth_name, TEST_FAMILY, taddr, this_ip_dest);
  229. if (ret && ret != -EEXIST)
  230. test_error("Failed to add route");
  231. if (bind(sk, &addr, sizeof(addr)))
  232. test_error("bind()");
  233. }
  234. static void bench_connect_client(size_t port_off, struct bench_tests *bt,
  235. union tcp_addr *ips, size_t ips_nr, bool rand_order)
  236. {
  237. struct bench_stats *con;
  238. union tcp_addr *p;
  239. size_t i;
  240. if (rand_order)
  241. con = &bt->connect_rand_key;
  242. else
  243. con = &bt->connect_last_key;
  244. p = (union tcp_addr *)&ips[0];
  245. for (i = 0; i < BENCH_NR_ITERS; i++) {
  246. int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
  247. if (sk < 0)
  248. test_error("socket()");
  249. client_addr_setup(sk, *p);
  250. if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest,
  251. -1, 100, 100))
  252. test_error("setsockopt(TCP_AO_ADD_KEY)");
  253. synchronize_threads();
  254. measure_call(con, connect_client, sk, (void *)&port_off);
  255. close(sk);
  256. /*
  257. * Slowest for FILO-linked-list:
  258. * on (i) iteration removing ips[i] element. When it gets
  259. * added to the list back - it becomes first to fetch, so
  260. * on (i + 1) iteration go to ips[i + 1] element.
  261. */
  262. if (rand_order)
  263. p = (union tcp_addr *)&ips[rand() % ips_nr];
  264. else
  265. p = (union tcp_addr *)&ips[i % ips_nr];
  266. }
  267. }
  268. static void *client_fn(void *arg)
  269. {
  270. size_t i;
  271. for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
  272. struct bench_tests *bt = &bench_results[i];
  273. synchronize_threads();
  274. bench_connect_client(i, bt, test_ips, nr_keys[i], false);
  275. test_print_stats("Connect: worst case",
  276. nr_keys[i], &bt->connect_last_key);
  277. bench_connect_client(i, bt, test_ips, nr_keys[i], false);
  278. test_print_stats("Connect: random-search",
  279. nr_keys[i], &bt->connect_last_key);
  280. }
  281. synchronize_threads();
  282. return NULL;
  283. }
  284. int main(int argc, char *argv[])
  285. {
  286. test_init(31, server_fn, client_fn);
  287. return 0;
  288. }