bench_sockmap.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <error.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <sys/sendfile.h>
  7. #include <arpa/inet.h>
  8. #include <fcntl.h>
  9. #include <argp.h>
  10. #include "bench.h"
  11. #include "bench_sockmap_prog.skel.h"
  12. #include "bpf_util.h"
  13. #define FILE_SIZE (128 * 1024)
  14. #define DATA_REPEAT_SIZE 10
  15. static const char snd_data[DATA_REPEAT_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  16. /* c1 <-> [p1, p2] <-> c2
  17. * RX bench(BPF_SK_SKB_STREAM_VERDICT):
  18. * ARG_FW_RX_PASS:
  19. * send(p2) -> recv(c2) -> bpf skb passthrough -> recv(c2)
  20. * ARG_FW_RX_VERDICT_EGRESS:
  21. * send(c1) -> verdict skb to tx queuec of p2 -> recv(c2)
  22. * ARG_FW_RX_VERDICT_INGRESS:
  23. * send(c1) -> verdict skb to rx queuec of c2 -> recv(c2)
  24. *
  25. * TX bench(BPF_SK_MSG_VERDIC):
  26. * ARG_FW_TX_PASS:
  27. * send(p2) -> bpf msg passthrough -> send(p2) -> recv(c2)
  28. * ARG_FW_TX_VERDICT_INGRESS:
  29. * send(p2) -> verdict msg to rx queue of c2 -> recv(c2)
  30. * ARG_FW_TX_VERDICT_EGRESS:
  31. * send(p1) -> verdict msg to tx queue of p2 -> recv(c2)
  32. */
  33. enum SOCKMAP_ARG_FLAG {
  34. ARG_FW_RX_NORMAL = 11000,
  35. ARG_FW_RX_PASS,
  36. ARG_FW_RX_VERDICT_EGRESS,
  37. ARG_FW_RX_VERDICT_INGRESS,
  38. ARG_FW_TX_NORMAL,
  39. ARG_FW_TX_PASS,
  40. ARG_FW_TX_VERDICT_INGRESS,
  41. ARG_FW_TX_VERDICT_EGRESS,
  42. ARG_CTL_RX_STRP,
  43. ARG_CONSUMER_DELAY_TIME,
  44. ARG_PRODUCER_DURATION,
  45. };
  46. #define TXMODE_NORMAL() \
  47. ((ctx.mode) == ARG_FW_TX_NORMAL)
  48. #define TXMODE_BPF_INGRESS() \
  49. ((ctx.mode) == ARG_FW_TX_VERDICT_INGRESS)
  50. #define TXMODE_BPF_EGRESS() \
  51. ((ctx.mode) == ARG_FW_TX_VERDICT_EGRESS)
  52. #define TXMODE_BPF_PASS() \
  53. ((ctx.mode) == ARG_FW_TX_PASS)
  54. #define TXMODE_BPF() ( \
  55. TXMODE_BPF_PASS() || \
  56. TXMODE_BPF_INGRESS() || \
  57. TXMODE_BPF_EGRESS())
  58. #define TXMODE() ( \
  59. TXMODE_NORMAL() || \
  60. TXMODE_BPF())
  61. #define RXMODE_NORMAL() \
  62. ((ctx.mode) == ARG_FW_RX_NORMAL)
  63. #define RXMODE_BPF_PASS() \
  64. ((ctx.mode) == ARG_FW_RX_PASS)
  65. #define RXMODE_BPF_VERDICT_EGRESS() \
  66. ((ctx.mode) == ARG_FW_RX_VERDICT_EGRESS)
  67. #define RXMODE_BPF_VERDICT_INGRESS() \
  68. ((ctx.mode) == ARG_FW_RX_VERDICT_INGRESS)
  69. #define RXMODE_BPF_VERDICT() ( \
  70. RXMODE_BPF_VERDICT_INGRESS() || \
  71. RXMODE_BPF_VERDICT_EGRESS())
  72. #define RXMODE_BPF() ( \
  73. RXMODE_BPF_PASS() || \
  74. RXMODE_BPF_VERDICT())
  75. #define RXMODE() ( \
  76. RXMODE_NORMAL() || \
  77. RXMODE_BPF())
  78. static struct socmap_ctx {
  79. struct bench_sockmap_prog *skel;
  80. enum SOCKMAP_ARG_FLAG mode;
  81. #define c1 fds[0]
  82. #define p1 fds[1]
  83. #define c2 fds[2]
  84. #define p2 fds[3]
  85. #define sfd fds[4]
  86. int fds[5];
  87. long send_calls;
  88. long read_calls;
  89. long prod_send;
  90. long user_read;
  91. int file_size;
  92. int delay_consumer;
  93. int prod_run_time;
  94. int strp_size;
  95. } ctx = {
  96. .prod_send = 0,
  97. .user_read = 0,
  98. .file_size = FILE_SIZE,
  99. .mode = ARG_FW_RX_VERDICT_EGRESS,
  100. .fds = {0},
  101. .delay_consumer = 0,
  102. .prod_run_time = 0,
  103. .strp_size = 0,
  104. };
  105. static void bench_sockmap_prog_destroy(void)
  106. {
  107. int i;
  108. for (i = 0; i < ARRAY_SIZE(ctx.fds); i++) {
  109. if (ctx.fds[i] > 0)
  110. close(ctx.fds[i]);
  111. }
  112. bench_sockmap_prog__destroy(ctx.skel);
  113. }
  114. static void init_addr(struct sockaddr_storage *ss,
  115. socklen_t *len)
  116. {
  117. struct sockaddr_in *addr4 = memset(ss, 0, sizeof(*ss));
  118. addr4->sin_family = AF_INET;
  119. addr4->sin_port = 0;
  120. addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  121. *len = sizeof(*addr4);
  122. }
  123. static bool set_non_block(int fd, bool blocking)
  124. {
  125. int flags = fcntl(fd, F_GETFL, 0);
  126. if (flags == -1)
  127. return false;
  128. flags = blocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK);
  129. return (fcntl(fd, F_SETFL, flags) == 0);
  130. }
  131. static int create_pair(int *c, int *p, int type)
  132. {
  133. struct sockaddr_storage addr;
  134. int err, cfd, pfd;
  135. socklen_t addr_len = sizeof(struct sockaddr_storage);
  136. err = getsockname(ctx.sfd, (struct sockaddr *)&addr, &addr_len);
  137. if (err) {
  138. fprintf(stderr, "getsockname error %d\n", errno);
  139. return err;
  140. }
  141. cfd = socket(AF_INET, type, 0);
  142. if (cfd < 0) {
  143. fprintf(stderr, "socket error %d\n", errno);
  144. return err;
  145. }
  146. err = connect(cfd, (struct sockaddr *)&addr, addr_len);
  147. if (err && errno != EINPROGRESS) {
  148. fprintf(stderr, "connect error %d\n", errno);
  149. return err;
  150. }
  151. pfd = accept(ctx.sfd, NULL, NULL);
  152. if (pfd < 0) {
  153. fprintf(stderr, "accept error %d\n", errno);
  154. return err;
  155. }
  156. *c = cfd;
  157. *p = pfd;
  158. return 0;
  159. }
  160. static int create_sockets(void)
  161. {
  162. struct sockaddr_storage addr;
  163. int err, one = 1;
  164. socklen_t addr_len;
  165. init_addr(&addr, &addr_len);
  166. ctx.sfd = socket(AF_INET, SOCK_STREAM, 0);
  167. if (ctx.sfd < 0) {
  168. fprintf(stderr, "socket error:%d\n", errno);
  169. return ctx.sfd;
  170. }
  171. err = setsockopt(ctx.sfd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
  172. if (err) {
  173. fprintf(stderr, "setsockopt error:%d\n", errno);
  174. return err;
  175. }
  176. err = bind(ctx.sfd, (struct sockaddr *)&addr, addr_len);
  177. if (err) {
  178. fprintf(stderr, "bind error:%d\n", errno);
  179. return err;
  180. }
  181. err = listen(ctx.sfd, SOMAXCONN);
  182. if (err) {
  183. fprintf(stderr, "listen error:%d\n", errno);
  184. return err;
  185. }
  186. err = create_pair(&ctx.c1, &ctx.p1, SOCK_STREAM);
  187. if (err) {
  188. fprintf(stderr, "create_pair 1 error\n");
  189. return err;
  190. }
  191. err = create_pair(&ctx.c2, &ctx.p2, SOCK_STREAM);
  192. if (err) {
  193. fprintf(stderr, "create_pair 2 error\n");
  194. return err;
  195. }
  196. printf("create socket fd c1:%d p1:%d c2:%d p2:%d\n",
  197. ctx.c1, ctx.p1, ctx.c2, ctx.p2);
  198. return 0;
  199. }
  200. static void validate(void)
  201. {
  202. if (env.consumer_cnt != 2 || env.producer_cnt != 1 ||
  203. !env.affinity)
  204. goto err;
  205. return;
  206. err:
  207. fprintf(stderr, "argument '-c 2 -p 1 -a' is necessary");
  208. exit(1);
  209. }
  210. static int setup_rx_sockmap(void)
  211. {
  212. int verdict, pass, parser, map;
  213. int zero = 0, one = 1;
  214. int err;
  215. parser = bpf_program__fd(ctx.skel->progs.prog_skb_parser);
  216. verdict = bpf_program__fd(ctx.skel->progs.prog_skb_verdict);
  217. pass = bpf_program__fd(ctx.skel->progs.prog_skb_pass);
  218. map = bpf_map__fd(ctx.skel->maps.sock_map_rx);
  219. if (ctx.strp_size != 0) {
  220. ctx.skel->bss->pkt_size = ctx.strp_size;
  221. err = bpf_prog_attach(parser, map, BPF_SK_SKB_STREAM_PARSER, 0);
  222. if (err)
  223. return err;
  224. }
  225. if (RXMODE_BPF_VERDICT())
  226. err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0);
  227. else if (RXMODE_BPF_PASS())
  228. err = bpf_prog_attach(pass, map, BPF_SK_SKB_STREAM_VERDICT, 0);
  229. if (err)
  230. return err;
  231. if (RXMODE_BPF_PASS())
  232. return bpf_map_update_elem(map, &zero, &ctx.c2, BPF_NOEXIST);
  233. err = bpf_map_update_elem(map, &zero, &ctx.p1, BPF_NOEXIST);
  234. if (err < 0)
  235. return err;
  236. if (RXMODE_BPF_VERDICT_INGRESS()) {
  237. ctx.skel->bss->verdict_dir = BPF_F_INGRESS;
  238. err = bpf_map_update_elem(map, &one, &ctx.c2, BPF_NOEXIST);
  239. } else {
  240. err = bpf_map_update_elem(map, &one, &ctx.p2, BPF_NOEXIST);
  241. }
  242. if (err < 0)
  243. return err;
  244. return 0;
  245. }
  246. static int setup_tx_sockmap(void)
  247. {
  248. int zero = 0, one = 1;
  249. int prog, map;
  250. int err;
  251. map = bpf_map__fd(ctx.skel->maps.sock_map_tx);
  252. prog = TXMODE_BPF_PASS() ?
  253. bpf_program__fd(ctx.skel->progs.prog_skmsg_pass) :
  254. bpf_program__fd(ctx.skel->progs.prog_skmsg_verdict);
  255. err = bpf_prog_attach(prog, map, BPF_SK_MSG_VERDICT, 0);
  256. if (err)
  257. return err;
  258. if (TXMODE_BPF_EGRESS()) {
  259. err = bpf_map_update_elem(map, &zero, &ctx.p1, BPF_NOEXIST);
  260. err |= bpf_map_update_elem(map, &one, &ctx.p2, BPF_NOEXIST);
  261. } else {
  262. ctx.skel->bss->verdict_dir = BPF_F_INGRESS;
  263. err = bpf_map_update_elem(map, &zero, &ctx.p2, BPF_NOEXIST);
  264. err |= bpf_map_update_elem(map, &one, &ctx.c2, BPF_NOEXIST);
  265. }
  266. if (err < 0)
  267. return err;
  268. return 0;
  269. }
  270. static void setup(void)
  271. {
  272. int err;
  273. ctx.skel = bench_sockmap_prog__open_and_load();
  274. if (!ctx.skel) {
  275. fprintf(stderr, "error loading skel\n");
  276. exit(1);
  277. }
  278. if (create_sockets()) {
  279. fprintf(stderr, "create_net_mode error\n");
  280. goto err;
  281. }
  282. if (RXMODE_BPF()) {
  283. err = setup_rx_sockmap();
  284. if (err) {
  285. fprintf(stderr, "setup_rx_sockmap error:%d\n", err);
  286. goto err;
  287. }
  288. } else if (TXMODE_BPF()) {
  289. err = setup_tx_sockmap();
  290. if (err) {
  291. fprintf(stderr, "setup_tx_sockmap error:%d\n", err);
  292. goto err;
  293. }
  294. } else {
  295. fprintf(stderr, "unknown sockmap bench mode: %d\n", ctx.mode);
  296. goto err;
  297. }
  298. return;
  299. err:
  300. bench_sockmap_prog_destroy();
  301. exit(1);
  302. }
  303. static void measure(struct bench_res *res)
  304. {
  305. res->drops = atomic_swap(&ctx.prod_send, 0);
  306. res->hits = atomic_swap(&ctx.skel->bss->process_byte, 0);
  307. res->false_hits = atomic_swap(&ctx.user_read, 0);
  308. res->important_hits = atomic_swap(&ctx.send_calls, 0);
  309. res->important_hits |= atomic_swap(&ctx.read_calls, 0) << 32;
  310. }
  311. static void verify_data(int *check_pos, char *buf, int rcv)
  312. {
  313. for (int i = 0 ; i < rcv; i++) {
  314. if (buf[i] != snd_data[(*check_pos) % DATA_REPEAT_SIZE]) {
  315. fprintf(stderr, "verify data fail");
  316. exit(1);
  317. }
  318. (*check_pos)++;
  319. if (*check_pos >= FILE_SIZE)
  320. *check_pos = 0;
  321. }
  322. }
  323. static void *consumer(void *input)
  324. {
  325. int rcv, sent;
  326. int check_pos = 0;
  327. int tid = (long)input;
  328. int recv_buf_size = FILE_SIZE;
  329. char *buf = malloc(recv_buf_size);
  330. int delay_read = ctx.delay_consumer;
  331. if (!buf) {
  332. fprintf(stderr, "fail to init read buffer");
  333. return NULL;
  334. }
  335. while (true) {
  336. if (tid == 1) {
  337. /* consumer 1 is unused for tx test and stream verdict test */
  338. if (RXMODE_BPF() || TXMODE())
  339. return NULL;
  340. /* it's only for RX_NORMAL which service as reserve-proxy mode */
  341. rcv = read(ctx.p1, buf, recv_buf_size);
  342. if (rcv < 0) {
  343. fprintf(stderr, "fail to read p1");
  344. return NULL;
  345. }
  346. sent = send(ctx.p2, buf, recv_buf_size, 0);
  347. if (sent < 0) {
  348. fprintf(stderr, "fail to send p2");
  349. return NULL;
  350. }
  351. } else {
  352. if (delay_read != 0) {
  353. if (delay_read < 0)
  354. return NULL;
  355. sleep(delay_read);
  356. delay_read = 0;
  357. }
  358. /* read real endpoint by consumer 0 */
  359. atomic_inc(&ctx.read_calls);
  360. rcv = read(ctx.c2, buf, recv_buf_size);
  361. if (rcv < 0 && errno != EAGAIN) {
  362. fprintf(stderr, "%s fail to read c2 %d\n", __func__, errno);
  363. return NULL;
  364. }
  365. verify_data(&check_pos, buf, rcv);
  366. atomic_add(&ctx.user_read, rcv);
  367. }
  368. }
  369. return NULL;
  370. }
  371. static void *producer(void *input)
  372. {
  373. int off = 0, fp, need_sent, sent;
  374. int file_size = ctx.file_size;
  375. struct timespec ts1, ts2;
  376. int target;
  377. FILE *file;
  378. file = tmpfile();
  379. if (!file) {
  380. fprintf(stderr, "create file for sendfile");
  381. return NULL;
  382. }
  383. /* we need simple verify */
  384. for (int i = 0; i < file_size; i++) {
  385. if (fwrite(&snd_data[off], sizeof(char), 1, file) != 1) {
  386. fprintf(stderr, "init tmpfile error");
  387. return NULL;
  388. }
  389. if (++off >= sizeof(snd_data))
  390. off = 0;
  391. }
  392. fflush(file);
  393. fseek(file, 0, SEEK_SET);
  394. fp = fileno(file);
  395. need_sent = file_size;
  396. clock_gettime(CLOCK_MONOTONIC, &ts1);
  397. if (RXMODE_BPF_VERDICT())
  398. target = ctx.c1;
  399. else if (TXMODE_BPF_EGRESS())
  400. target = ctx.p1;
  401. else
  402. target = ctx.p2;
  403. set_non_block(target, true);
  404. while (true) {
  405. if (ctx.prod_run_time) {
  406. clock_gettime(CLOCK_MONOTONIC, &ts2);
  407. if (ts2.tv_sec - ts1.tv_sec > ctx.prod_run_time)
  408. return NULL;
  409. }
  410. errno = 0;
  411. atomic_inc(&ctx.send_calls);
  412. sent = sendfile(target, fp, NULL, need_sent);
  413. if (sent < 0) {
  414. if (errno != EAGAIN && errno != ENOMEM && errno != ENOBUFS) {
  415. fprintf(stderr, "sendfile return %d, errorno %d:%s\n",
  416. sent, errno, strerror(errno));
  417. return NULL;
  418. }
  419. continue;
  420. } else if (sent < need_sent) {
  421. need_sent -= sent;
  422. atomic_add(&ctx.prod_send, sent);
  423. continue;
  424. }
  425. atomic_add(&ctx.prod_send, need_sent);
  426. need_sent = file_size;
  427. lseek(fp, 0, SEEK_SET);
  428. }
  429. return NULL;
  430. }
  431. static void report_progress(int iter, struct bench_res *res, long delta_ns)
  432. {
  433. double speed_mbs, prod_mbs, bpf_mbs, send_hz, read_hz;
  434. prod_mbs = res->drops / 1000000.0 / (delta_ns / 1000000000.0);
  435. speed_mbs = res->false_hits / 1000000.0 / (delta_ns / 1000000000.0);
  436. bpf_mbs = res->hits / 1000000.0 / (delta_ns / 1000000000.0);
  437. send_hz = (res->important_hits & 0xFFFFFFFF) / (delta_ns / 1000000000.0);
  438. read_hz = (res->important_hits >> 32) / (delta_ns / 1000000000.0);
  439. printf("Iter %3d (%7.3lfus): ",
  440. iter, (delta_ns - 1000000000) / 1000.0);
  441. printf("Send Speed %8.3lf MB/s (%8.3lf calls/s), BPF Speed %8.3lf MB/s, "
  442. "Rcv Speed %8.3lf MB/s (%8.3lf calls/s)\n",
  443. prod_mbs, send_hz, bpf_mbs, speed_mbs, read_hz);
  444. }
  445. static void report_final(struct bench_res res[], int res_cnt)
  446. {
  447. double verdict_mbs_mean = 0.0;
  448. long verdict_total = 0;
  449. int i;
  450. for (i = 0; i < res_cnt; i++) {
  451. verdict_mbs_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
  452. verdict_total += res[i].hits / 1000000.0;
  453. }
  454. printf("Summary: total trans %8.3lu MB \u00B1 %5.3lf MB/s\n",
  455. verdict_total, verdict_mbs_mean);
  456. }
  457. static const struct argp_option opts[] = {
  458. { "rx-normal", ARG_FW_RX_NORMAL, NULL, 0,
  459. "simple reserve-proxy mode, no bfp enabled"},
  460. { "rx-pass", ARG_FW_RX_PASS, NULL, 0,
  461. "run bpf prog but no redir applied"},
  462. { "rx-strp", ARG_CTL_RX_STRP, "Byte", 0,
  463. "enable strparser and set the encapsulation size"},
  464. { "rx-verdict-egress", ARG_FW_RX_VERDICT_EGRESS, NULL, 0,
  465. "forward data with bpf(stream verdict)"},
  466. { "rx-verdict-ingress", ARG_FW_RX_VERDICT_INGRESS, NULL, 0,
  467. "forward data with bpf(stream verdict)"},
  468. { "tx-normal", ARG_FW_TX_NORMAL, NULL, 0,
  469. "simple c-s mode, no bfp enabled"},
  470. { "tx-pass", ARG_FW_TX_PASS, NULL, 0,
  471. "run bpf prog but no redir applied"},
  472. { "tx-verdict-ingress", ARG_FW_TX_VERDICT_INGRESS, NULL, 0,
  473. "forward msg to ingress queue of another socket"},
  474. { "tx-verdict-egress", ARG_FW_TX_VERDICT_EGRESS, NULL, 0,
  475. "forward msg to egress queue of another socket"},
  476. { "delay-consumer", ARG_CONSUMER_DELAY_TIME, "SEC", 0,
  477. "delay consumer start"},
  478. { "producer-duration", ARG_PRODUCER_DURATION, "SEC", 0,
  479. "producer duration"},
  480. {},
  481. };
  482. static error_t parse_arg(int key, char *arg, struct argp_state *state)
  483. {
  484. switch (key) {
  485. case ARG_FW_RX_NORMAL...ARG_FW_TX_VERDICT_EGRESS:
  486. ctx.mode = key;
  487. break;
  488. case ARG_CONSUMER_DELAY_TIME:
  489. ctx.delay_consumer = strtol(arg, NULL, 10);
  490. break;
  491. case ARG_PRODUCER_DURATION:
  492. ctx.prod_run_time = strtol(arg, NULL, 10);
  493. break;
  494. case ARG_CTL_RX_STRP:
  495. ctx.strp_size = strtol(arg, NULL, 10);
  496. break;
  497. default:
  498. return ARGP_ERR_UNKNOWN;
  499. }
  500. return 0;
  501. }
  502. /* exported into benchmark runner */
  503. const struct argp bench_sockmap_argp = {
  504. .options = opts,
  505. .parser = parse_arg,
  506. };
  507. /* Benchmark performance of creating bpf local storage */
  508. const struct bench bench_sockmap = {
  509. .name = "sockmap",
  510. .argp = &bench_sockmap_argp,
  511. .validate = validate,
  512. .setup = setup,
  513. .producer_thread = producer,
  514. .consumer_thread = consumer,
  515. .measure = measure,
  516. .report_progress = report_progress,
  517. .report_final = report_final,
  518. };