Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/xsk: add basic multi-buffer test

Add the first basic multi-buffer test that sends a stream of 9K
packets and validates that they are received at the other end. In
order to enable sending and receiving multi-buffer packets, code that
sets the MTU is introduced as well as modifications to the XDP
programs so that they signal that they are multi-buffer enabled.

Signed-off-by: Magnus Karlsson <magnus.karlsson@intel.com>
Link: https://lore.kernel.org/r/20230719132421.584801-20-maciej.fijalkowski@intel.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Magnus Karlsson and committed by
Alexei Starovoitov
f540d44e 17f1034d

+219 -3
+6
tools/include/uapi/linux/if_xdp.h
··· 25 25 * application. 26 26 */ 27 27 #define XDP_USE_NEED_WAKEUP (1 << 3) 28 + /* By setting this option, userspace application indicates that it can 29 + * handle multiple descriptors per packet thus enabling xsk core to split 30 + * multi-buffer XDP frames into multiple Rx descriptors. Without this set 31 + * such frames will be dropped by xsk. 32 + */ 33 + #define XDP_USE_SG (1 << 4) 28 34 29 35 /* Flags for xsk_umem_config flags */ 30 36 #define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
+2 -2
tools/testing/selftests/bpf/progs/xsk_xdp_progs.c
··· 15 15 static unsigned int idx; 16 16 int count = 0; 17 17 18 - SEC("xdp") int xsk_def_prog(struct xdp_md *xdp) 18 + SEC("xdp.frags") int xsk_def_prog(struct xdp_md *xdp) 19 19 { 20 20 return bpf_redirect_map(&xsk, 0, XDP_DROP); 21 21 } 22 22 23 - SEC("xdp") int xsk_xdp_drop(struct xdp_md *xdp) 23 + SEC("xdp.frags") int xsk_xdp_drop(struct xdp_md *xdp) 24 24 { 25 25 /* Drop every other packet */ 26 26 if (idx++ % 2)
+135 -1
tools/testing/selftests/bpf/xsk.c
··· 18 18 #include <linux/ethtool.h> 19 19 #include <linux/filter.h> 20 20 #include <linux/if_ether.h> 21 + #include <linux/if_link.h> 21 22 #include <linux/if_packet.h> 22 23 #include <linux/if_xdp.h> 23 24 #include <linux/kernel.h> 24 25 #include <linux/list.h> 26 + #include <linux/netlink.h> 27 + #include <linux/rtnetlink.h> 25 28 #include <linux/sockios.h> 26 29 #include <net/if.h> 27 30 #include <sys/ioctl.h> 28 31 #include <sys/mman.h> 29 32 #include <sys/socket.h> 30 33 #include <sys/types.h> 31 - #include <linux/if_link.h> 32 34 33 35 #include <bpf/bpf.h> 34 36 #include <bpf/libbpf.h> ··· 81 79 struct xsk_ctx *ctx; 82 80 struct xsk_socket_config config; 83 81 int fd; 82 + }; 83 + 84 + struct nl_mtu_req { 85 + struct nlmsghdr nh; 86 + struct ifinfomsg msg; 87 + char buf[512]; 84 88 }; 85 89 86 90 int xsk_umem__fd(const struct xsk_umem *umem) ··· 292 284 return opts.attach_mode == XDP_ATTACHED_SKB; 293 285 294 286 return false; 287 + } 288 + 289 + /* Lifted from netlink.c in tools/lib/bpf */ 290 + static int netlink_recvmsg(int sock, struct msghdr *mhdr, int flags) 291 + { 292 + int len; 293 + 294 + do { 295 + len = recvmsg(sock, mhdr, flags); 296 + } while (len < 0 && (errno == EINTR || errno == EAGAIN)); 297 + 298 + if (len < 0) 299 + return -errno; 300 + return len; 301 + } 302 + 303 + /* Lifted from netlink.c in tools/lib/bpf */ 304 + static int alloc_iov(struct iovec *iov, int len) 305 + { 306 + void *nbuf; 307 + 308 + nbuf = realloc(iov->iov_base, len); 309 + if (!nbuf) 310 + return -ENOMEM; 311 + 312 + iov->iov_base = nbuf; 313 + iov->iov_len = len; 314 + return 0; 315 + } 316 + 317 + /* Original version lifted from netlink.c in tools/lib/bpf */ 318 + static int netlink_recv(int sock) 319 + { 320 + struct iovec iov = {}; 321 + struct msghdr mhdr = { 322 + .msg_iov = &iov, 323 + .msg_iovlen = 1, 324 + }; 325 + bool multipart = true; 326 + struct nlmsgerr *err; 327 + struct nlmsghdr *nh; 328 + int len, ret; 329 + 330 + ret = alloc_iov(&iov, 4096); 331 + if (ret) 332 + goto done; 333 + 334 + while (multipart) { 335 + multipart = false; 336 + len = netlink_recvmsg(sock, &mhdr, MSG_PEEK | MSG_TRUNC); 337 + if (len < 0) { 338 + ret = len; 339 + goto done; 340 + } 341 + 342 + if (len > iov.iov_len) { 343 + ret = alloc_iov(&iov, len); 344 + if (ret) 345 + goto done; 346 + } 347 + 348 + len = netlink_recvmsg(sock, &mhdr, 0); 349 + if (len < 0) { 350 + ret = len; 351 + goto done; 352 + } 353 + 354 + if (len == 0) 355 + break; 356 + 357 + for (nh = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(nh, len); 358 + nh = NLMSG_NEXT(nh, len)) { 359 + if (nh->nlmsg_flags & NLM_F_MULTI) 360 + multipart = true; 361 + switch (nh->nlmsg_type) { 362 + case NLMSG_ERROR: 363 + err = (struct nlmsgerr *)NLMSG_DATA(nh); 364 + if (!err->error) 365 + continue; 366 + ret = err->error; 367 + goto done; 368 + case NLMSG_DONE: 369 + ret = 0; 370 + goto done; 371 + default: 372 + break; 373 + } 374 + } 375 + } 376 + ret = 0; 377 + done: 378 + free(iov.iov_base); 379 + return ret; 380 + } 381 + 382 + int xsk_set_mtu(int ifindex, int mtu) 383 + { 384 + struct nl_mtu_req req; 385 + struct rtattr *rta; 386 + int fd, ret; 387 + 388 + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); 389 + if (fd < 0) 390 + return fd; 391 + 392 + memset(&req, 0, sizeof(req)); 393 + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 394 + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 395 + req.nh.nlmsg_type = RTM_NEWLINK; 396 + req.msg.ifi_family = AF_UNSPEC; 397 + req.msg.ifi_index = ifindex; 398 + rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len)); 399 + rta->rta_type = IFLA_MTU; 400 + rta->rta_len = RTA_LENGTH(sizeof(unsigned int)); 401 + req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + RTA_LENGTH(sizeof(mtu)); 402 + memcpy(RTA_DATA(rta), &mtu, sizeof(mtu)); 403 + 404 + ret = send(fd, &req, req.nh.nlmsg_len, 0); 405 + if (ret < 0) { 406 + close(fd); 407 + return errno; 408 + } 409 + 410 + ret = netlink_recv(fd); 411 + close(fd); 412 + return ret; 295 413 } 296 414 297 415 int xsk_attach_xdp_program(struct bpf_program *prog, int ifindex, u32 xdp_flags)
+2
tools/testing/selftests/bpf/xsk.h
··· 239 239 int xsk_umem__delete(struct xsk_umem *umem); 240 240 void xsk_socket__delete(struct xsk_socket *xsk); 241 241 242 + int xsk_set_mtu(int ifindex, int mtu); 243 + 242 244 #ifdef __cplusplus 243 245 } /* extern "C" */ 244 246 #endif
+68
tools/testing/selftests/bpf/xskxceiver.c
··· 49 49 * h. tests for invalid and corner case Tx descriptors so that the correct ones 50 50 * are discarded and let through, respectively. 51 51 * i. 2K frame size tests 52 + * j. If multi-buffer is supported, send 9k packets divided into 3 frames 52 53 * 53 54 * Total tests: 12 54 55 * ··· 78 77 #include <linux/if_link.h> 79 78 #include <linux/if_ether.h> 80 79 #include <linux/mman.h> 80 + #include <linux/netdev.h> 81 81 #include <arpa/inet.h> 82 82 #include <net/if.h> 83 83 #include <locale.h> ··· 255 253 cfg.bind_flags = ifobject->bind_flags; 256 254 if (shared) 257 255 cfg.bind_flags |= XDP_SHARED_UMEM; 256 + if (ifobject->pkt_stream && ifobject->mtu > MAX_ETH_PKT_SIZE) 257 + cfg.bind_flags |= XDP_USE_SG; 258 258 259 259 txr = ifobject->tx_on ? &xsk->tx : NULL; 260 260 rxr = ifobject->rx_on ? &xsk->rx : NULL; ··· 419 415 test->total_steps = 1; 420 416 test->nb_sockets = 1; 421 417 test->fail = false; 418 + test->mtu = MAX_ETH_PKT_SIZE; 422 419 test->xdp_prog_rx = ifobj_rx->xdp_progs->progs.xsk_def_prog; 423 420 test->xskmap_rx = ifobj_rx->xdp_progs->maps.xsk; 424 421 test->xdp_prog_tx = ifobj_tx->xdp_progs->progs.xsk_def_prog; ··· 471 466 test->xdp_prog_tx = xdp_prog_tx; 472 467 test->xskmap_rx = xskmap_rx; 473 468 test->xskmap_tx = xskmap_tx; 469 + } 470 + 471 + static int test_spec_set_mtu(struct test_spec *test, int mtu) 472 + { 473 + int err; 474 + 475 + if (test->ifobj_rx->mtu != mtu) { 476 + err = xsk_set_mtu(test->ifobj_rx->ifindex, mtu); 477 + if (err) 478 + return err; 479 + test->ifobj_rx->mtu = mtu; 480 + } 481 + if (test->ifobj_tx->mtu != mtu) { 482 + err = xsk_set_mtu(test->ifobj_tx->ifindex, mtu); 483 + if (err) 484 + return err; 485 + test->ifobj_tx->mtu = mtu; 486 + } 487 + 488 + return 0; 474 489 } 475 490 476 491 static void pkt_stream_reset(struct pkt_stream *pkt_stream) ··· 1541 1516 struct ifobject *ifobj2) 1542 1517 { 1543 1518 pthread_t t0, t1; 1519 + int err; 1520 + 1521 + if (test->mtu > MAX_ETH_PKT_SIZE) { 1522 + if (test->mode == TEST_MODE_ZC && (!ifobj1->multi_buff_zc_supp || 1523 + (ifobj2 && !ifobj2->multi_buff_zc_supp))) { 1524 + ksft_test_result_skip("Multi buffer for zero-copy not supported.\n"); 1525 + return TEST_SKIP; 1526 + } 1527 + if (test->mode != TEST_MODE_ZC && (!ifobj1->multi_buff_supp || 1528 + (ifobj2 && !ifobj2->multi_buff_supp))) { 1529 + ksft_test_result_skip("Multi buffer not supported.\n"); 1530 + return TEST_SKIP; 1531 + } 1532 + } 1533 + err = test_spec_set_mtu(test, test->mtu); 1534 + if (err) { 1535 + ksft_print_msg("Error, could not set mtu.\n"); 1536 + exit_with_error(err); 1537 + } 1544 1538 1545 1539 if (ifobj2) { 1546 1540 if (pthread_barrier_init(&barr, NULL, 2)) ··· 1769 1725 return testapp_validate_traffic(test); 1770 1726 } 1771 1727 1728 + static int testapp_multi_buffer(struct test_spec *test) 1729 + { 1730 + test_spec_set_name(test, "RUN_TO_COMPLETION_9K_PACKETS"); 1731 + test->mtu = MAX_ETH_JUMBO_SIZE; 1732 + pkt_stream_replace(test, DEFAULT_PKT_CNT, MAX_ETH_JUMBO_SIZE); 1733 + 1734 + return testapp_validate_traffic(test); 1735 + } 1736 + 1772 1737 static int testapp_invalid_desc(struct test_spec *test) 1773 1738 { 1774 1739 struct xsk_umem_info *umem = test->ifobj_tx->umem; ··· 1911 1858 static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *src_mac, 1912 1859 thread_func_t func_ptr) 1913 1860 { 1861 + LIBBPF_OPTS(bpf_xdp_query_opts, query_opts); 1914 1862 int err; 1915 1863 1916 1864 memcpy(ifobj->dst_mac, dst_mac, ETH_ALEN); ··· 1927 1873 1928 1874 if (hugepages_present()) 1929 1875 ifobj->unaligned_supp = true; 1876 + 1877 + err = bpf_xdp_query(ifobj->ifindex, XDP_FLAGS_DRV_MODE, &query_opts); 1878 + if (err) { 1879 + ksft_print_msg("Error querrying XDP capabilities\n"); 1880 + exit_with_error(-err); 1881 + } 1882 + if (query_opts.feature_flags & NETDEV_XDP_ACT_RX_SG) 1883 + ifobj->multi_buff_supp = true; 1884 + if (query_opts.feature_flags & NETDEV_XDP_ACT_XSK_ZEROCOPY) 1885 + if (query_opts.xdp_zc_max_segs > 1) 1886 + ifobj->multi_buff_zc_supp = true; 1930 1887 } 1931 1888 1932 1889 static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type) ··· 1969 1904 case TEST_TYPE_RUN_TO_COMPLETION: 1970 1905 test_spec_set_name(test, "RUN_TO_COMPLETION"); 1971 1906 ret = testapp_validate_traffic(test); 1907 + break; 1908 + case TEST_TYPE_RUN_TO_COMPLETION_MB: 1909 + ret = testapp_multi_buffer(test); 1972 1910 break; 1973 1911 case TEST_TYPE_RUN_TO_COMPLETION_SINGLE_PKT: 1974 1912 test_spec_set_name(test, "RUN_TO_COMPLETION_SINGLE_PKT");
+6
tools/testing/selftests/bpf/xskxceiver.h
··· 38 38 #define MAX_TEARDOWN_ITER 10 39 39 #define PKT_HDR_SIZE (sizeof(struct ethhdr) + 2) /* Just to align the data in the packet */ 40 40 #define MIN_PKT_SIZE 64 41 + #define MAX_ETH_PKT_SIZE 1518 41 42 #define MAX_ETH_JUMBO_SIZE 9000 42 43 #define USLEEP_MAX 10000 43 44 #define SOCK_RECONF_CTR 10 ··· 85 84 TEST_TYPE_BPF_RES, 86 85 TEST_TYPE_XDP_DROP_HALF, 87 86 TEST_TYPE_XDP_METADATA_COUNT, 87 + TEST_TYPE_RUN_TO_COMPLETION_MB, 88 88 TEST_TYPE_MAX 89 89 }; 90 90 ··· 144 142 struct bpf_program *xdp_prog; 145 143 enum test_mode mode; 146 144 int ifindex; 145 + int mtu; 147 146 u32 bind_flags; 148 147 bool tx_on; 149 148 bool rx_on; ··· 155 152 bool shared_umem; 156 153 bool use_metadata; 157 154 bool unaligned_supp; 155 + bool multi_buff_supp; 156 + bool multi_buff_zc_supp; 158 157 u8 dst_mac[ETH_ALEN]; 159 158 u8 src_mac[ETH_ALEN]; 160 159 }; ··· 170 165 struct bpf_program *xdp_prog_tx; 171 166 struct bpf_map *xskmap_rx; 172 167 struct bpf_map *xskmap_tx; 168 + int mtu; 173 169 u16 total_steps; 174 170 u16 current_step; 175 171 u16 nb_sockets;