xdp_hw_metadata.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Reference program for verifying XDP metadata on real HW. Functional test
  3. * only, doesn't test the performance.
  4. *
  5. * RX:
  6. * - UDP 9091 packets are diverted into AF_XDP
  7. * - Metadata verified:
  8. * - rx_timestamp
  9. * - rx_hash
  10. *
  11. * TX:
  12. * - UDP 9091 packets trigger TX reply
  13. * - TX HW timestamp is requested and reported back upon completion
  14. * - TX checksum is requested
  15. */
  16. #include <test_progs.h>
  17. #include <network_helpers.h>
  18. #include "xdp_hw_metadata.skel.h"
  19. #include "xsk.h"
  20. #include <error.h>
  21. #include <linux/kernel.h>
  22. #include <linux/bits.h>
  23. #include <linux/bitfield.h>
  24. #include <linux/errqueue.h>
  25. #include <linux/if_link.h>
  26. #include <linux/net_tstamp.h>
  27. #include <linux/udp.h>
  28. #include <linux/sockios.h>
  29. #include <linux/if_xdp.h>
  30. #include <sys/mman.h>
  31. #include <net/if.h>
  32. #include <ctype.h>
  33. #include <poll.h>
  34. #include <time.h>
  35. #include <unistd.h>
  36. #include <libgen.h>
  37. #include "xdp_metadata.h"
  38. #define UMEM_NUM 256
  39. #define UMEM_FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
  40. #define UMEM_SIZE (UMEM_FRAME_SIZE * UMEM_NUM)
  41. #define XDP_FLAGS (XDP_FLAGS_DRV_MODE | XDP_FLAGS_REPLACE)
  42. struct xsk {
  43. void *umem_area;
  44. struct xsk_umem *umem;
  45. struct xsk_ring_prod fill;
  46. struct xsk_ring_cons comp;
  47. struct xsk_ring_prod tx;
  48. struct xsk_ring_cons rx;
  49. struct xsk_socket *socket;
  50. };
  51. struct xdp_hw_metadata *bpf_obj;
  52. __u16 bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY;
  53. struct xsk *rx_xsk;
  54. const char *ifname;
  55. int ifindex;
  56. int rxq;
  57. bool skip_tx;
  58. __u64 last_hw_rx_timestamp;
  59. __u64 last_xdp_rx_timestamp;
  60. void test__fail(void) { /* for network_helpers.c */ }
  61. static int open_xsk(int ifindex, struct xsk *xsk, __u32 queue_id)
  62. {
  63. int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
  64. const struct xsk_socket_config socket_config = {
  65. .rx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
  66. .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
  67. .bind_flags = bind_flags,
  68. };
  69. const struct xsk_umem_config umem_config = {
  70. .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
  71. .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
  72. .frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE,
  73. .flags = XDP_UMEM_TX_METADATA_LEN,
  74. .tx_metadata_len = sizeof(struct xsk_tx_metadata),
  75. };
  76. __u32 idx = 0;
  77. u64 addr;
  78. int ret;
  79. int i;
  80. xsk->umem_area = mmap(NULL, UMEM_SIZE, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
  81. if (xsk->umem_area == MAP_FAILED)
  82. return -ENOMEM;
  83. ret = xsk_umem__create(&xsk->umem,
  84. xsk->umem_area, UMEM_SIZE,
  85. &xsk->fill,
  86. &xsk->comp,
  87. &umem_config);
  88. if (ret)
  89. return ret;
  90. ret = xsk_socket__create(&xsk->socket, ifindex, queue_id,
  91. xsk->umem,
  92. &xsk->rx,
  93. &xsk->tx,
  94. &socket_config);
  95. if (ret)
  96. return ret;
  97. /* First half of umem is for TX. This way address matches 1-to-1
  98. * to the completion queue index.
  99. */
  100. for (i = 0; i < UMEM_NUM / 2; i++) {
  101. addr = i * UMEM_FRAME_SIZE;
  102. printf("%p: tx_desc[%d] -> %lx\n", xsk, i, addr);
  103. }
  104. /* Second half of umem is for RX. */
  105. ret = xsk_ring_prod__reserve(&xsk->fill, UMEM_NUM / 2, &idx);
  106. for (i = 0; i < UMEM_NUM / 2; i++) {
  107. addr = (UMEM_NUM / 2 + i) * UMEM_FRAME_SIZE;
  108. printf("%p: rx_desc[%d] -> %lx\n", xsk, i, addr);
  109. *xsk_ring_prod__fill_addr(&xsk->fill, idx + i) = addr;
  110. }
  111. xsk_ring_prod__submit(&xsk->fill, ret);
  112. return 0;
  113. }
  114. static void close_xsk(struct xsk *xsk)
  115. {
  116. if (xsk->umem)
  117. xsk_umem__delete(xsk->umem);
  118. if (xsk->socket)
  119. xsk_socket__delete(xsk->socket);
  120. munmap(xsk->umem_area, UMEM_SIZE);
  121. }
  122. static void refill_rx(struct xsk *xsk, __u64 addr)
  123. {
  124. __u32 idx;
  125. if (xsk_ring_prod__reserve(&xsk->fill, 1, &idx) == 1) {
  126. printf("%p: complete rx idx=%u addr=%llx\n", xsk, idx, addr);
  127. *xsk_ring_prod__fill_addr(&xsk->fill, idx) = addr;
  128. xsk_ring_prod__submit(&xsk->fill, 1);
  129. }
  130. }
  131. static int kick_tx(struct xsk *xsk)
  132. {
  133. return sendto(xsk_socket__fd(xsk->socket), NULL, 0, MSG_DONTWAIT, NULL, 0);
  134. }
  135. static int kick_rx(struct xsk *xsk)
  136. {
  137. return recvfrom(xsk_socket__fd(xsk->socket), NULL, 0, MSG_DONTWAIT, NULL, NULL);
  138. }
  139. #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
  140. static __u64 gettime(clockid_t clock_id)
  141. {
  142. struct timespec t;
  143. int res;
  144. /* See man clock_gettime(2) for type of clock_id's */
  145. res = clock_gettime(clock_id, &t);
  146. if (res < 0)
  147. error(res, errno, "Error with clock_gettime()");
  148. return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
  149. }
  150. static void print_tstamp_delta(const char *name, const char *refname,
  151. __u64 tstamp, __u64 reference)
  152. {
  153. __s64 delta = (__s64)reference - (__s64)tstamp;
  154. printf("%s: %llu (sec:%0.4f) delta to %s sec:%0.4f (%0.3f usec)\n",
  155. name, tstamp, (double)tstamp / NANOSEC_PER_SEC, refname,
  156. (double)delta / NANOSEC_PER_SEC,
  157. (double)delta / 1000);
  158. }
  159. #define VLAN_PRIO_MASK GENMASK(15, 13) /* Priority Code Point */
  160. #define VLAN_DEI_MASK GENMASK(12, 12) /* Drop Eligible Indicator */
  161. #define VLAN_VID_MASK GENMASK(11, 0) /* VLAN Identifier */
  162. static void print_vlan_tci(__u16 tag)
  163. {
  164. __u16 vlan_id = FIELD_GET(VLAN_VID_MASK, tag);
  165. __u8 pcp = FIELD_GET(VLAN_PRIO_MASK, tag);
  166. bool dei = FIELD_GET(VLAN_DEI_MASK, tag);
  167. printf("PCP=%u, DEI=%d, VID=0x%X\n", pcp, dei, vlan_id);
  168. }
  169. static void verify_xdp_metadata(void *data, clockid_t clock_id)
  170. {
  171. struct xdp_meta *meta;
  172. meta = data - sizeof(*meta);
  173. if (meta->hint_valid & XDP_META_FIELD_RSS)
  174. printf("rx_hash: 0x%X with RSS type:0x%X\n",
  175. meta->rx_hash, meta->rx_hash_type);
  176. else
  177. printf("No rx_hash, err=%d\n", meta->rx_hash_err);
  178. if (meta->hint_valid & XDP_META_FIELD_TS) {
  179. __u64 ref_tstamp = gettime(clock_id);
  180. /* store received timestamps to calculate a delta at tx */
  181. last_hw_rx_timestamp = meta->rx_timestamp;
  182. last_xdp_rx_timestamp = meta->xdp_timestamp;
  183. print_tstamp_delta("HW RX-time", "User RX-time",
  184. meta->rx_timestamp, ref_tstamp);
  185. print_tstamp_delta("XDP RX-time", "User RX-time",
  186. meta->xdp_timestamp, ref_tstamp);
  187. } else {
  188. printf("No rx_timestamp, err=%d\n", meta->rx_timestamp_err);
  189. }
  190. if (meta->hint_valid & XDP_META_FIELD_VLAN_TAG) {
  191. printf("rx_vlan_proto: 0x%X\n", ntohs(meta->rx_vlan_proto));
  192. printf("rx_vlan_tci: ");
  193. print_vlan_tci(meta->rx_vlan_tci);
  194. } else {
  195. printf("No rx_vlan_tci or rx_vlan_proto, err=%d\n",
  196. meta->rx_vlan_tag_err);
  197. }
  198. }
  199. static void verify_skb_metadata(int fd)
  200. {
  201. char cmsg_buf[1024];
  202. char packet_buf[128];
  203. struct scm_timestamping *ts;
  204. struct iovec packet_iov;
  205. struct cmsghdr *cmsg;
  206. struct msghdr hdr;
  207. memset(&hdr, 0, sizeof(hdr));
  208. hdr.msg_iov = &packet_iov;
  209. hdr.msg_iovlen = 1;
  210. packet_iov.iov_base = packet_buf;
  211. packet_iov.iov_len = sizeof(packet_buf);
  212. hdr.msg_control = cmsg_buf;
  213. hdr.msg_controllen = sizeof(cmsg_buf);
  214. if (recvmsg(fd, &hdr, 0) < 0)
  215. error(1, errno, "recvmsg");
  216. for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
  217. cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
  218. if (cmsg->cmsg_level != SOL_SOCKET)
  219. continue;
  220. switch (cmsg->cmsg_type) {
  221. case SCM_TIMESTAMPING:
  222. ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
  223. if (ts->ts[2].tv_sec || ts->ts[2].tv_nsec) {
  224. printf("found skb hwtstamp = %lu.%lu\n",
  225. ts->ts[2].tv_sec, ts->ts[2].tv_nsec);
  226. return;
  227. }
  228. break;
  229. default:
  230. break;
  231. }
  232. }
  233. printf("skb hwtstamp is not found!\n");
  234. }
  235. static bool complete_tx(struct xsk *xsk, clockid_t clock_id)
  236. {
  237. struct xsk_tx_metadata *meta;
  238. __u64 addr;
  239. void *data;
  240. __u32 idx;
  241. if (!xsk_ring_cons__peek(&xsk->comp, 1, &idx))
  242. return false;
  243. addr = *xsk_ring_cons__comp_addr(&xsk->comp, idx);
  244. data = xsk_umem__get_data(xsk->umem_area, addr);
  245. meta = data - sizeof(struct xsk_tx_metadata);
  246. printf("%p: complete tx idx=%u addr=%llx\n", xsk, idx, addr);
  247. if (meta->completion.tx_timestamp) {
  248. __u64 ref_tstamp = gettime(clock_id);
  249. print_tstamp_delta("HW TX-complete-time", "User TX-complete-time",
  250. meta->completion.tx_timestamp, ref_tstamp);
  251. print_tstamp_delta("XDP RX-time", "User TX-complete-time",
  252. last_xdp_rx_timestamp, ref_tstamp);
  253. print_tstamp_delta("HW RX-time", "HW TX-complete-time",
  254. last_hw_rx_timestamp, meta->completion.tx_timestamp);
  255. } else {
  256. printf("No tx_timestamp\n");
  257. }
  258. xsk_ring_cons__release(&xsk->comp, 1);
  259. return true;
  260. }
  261. #define swap(a, b, len) do { \
  262. for (int i = 0; i < len; i++) { \
  263. __u8 tmp = ((__u8 *)a)[i]; \
  264. ((__u8 *)a)[i] = ((__u8 *)b)[i]; \
  265. ((__u8 *)b)[i] = tmp; \
  266. } \
  267. } while (0)
  268. static void ping_pong(struct xsk *xsk, void *rx_packet, clockid_t clock_id)
  269. {
  270. struct xsk_tx_metadata *meta;
  271. struct ipv6hdr *ip6h = NULL;
  272. struct iphdr *iph = NULL;
  273. struct xdp_desc *tx_desc;
  274. struct udphdr *udph;
  275. struct ethhdr *eth;
  276. __sum16 want_csum;
  277. void *data;
  278. __u32 idx;
  279. int ret;
  280. int len;
  281. ret = xsk_ring_prod__reserve(&xsk->tx, 1, &idx);
  282. if (ret != 1) {
  283. printf("%p: failed to reserve tx slot\n", xsk);
  284. return;
  285. }
  286. tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx);
  287. tx_desc->addr = idx % (UMEM_NUM / 2) * UMEM_FRAME_SIZE + sizeof(struct xsk_tx_metadata);
  288. data = xsk_umem__get_data(xsk->umem_area, tx_desc->addr);
  289. meta = data - sizeof(struct xsk_tx_metadata);
  290. memset(meta, 0, sizeof(*meta));
  291. meta->flags = XDP_TXMD_FLAGS_TIMESTAMP;
  292. eth = rx_packet;
  293. if (eth->h_proto == htons(ETH_P_IP)) {
  294. iph = (void *)(eth + 1);
  295. udph = (void *)(iph + 1);
  296. } else if (eth->h_proto == htons(ETH_P_IPV6)) {
  297. ip6h = (void *)(eth + 1);
  298. udph = (void *)(ip6h + 1);
  299. } else {
  300. printf("%p: failed to detect IP version for ping pong %04x\n", xsk, eth->h_proto);
  301. xsk_ring_prod__cancel(&xsk->tx, 1);
  302. return;
  303. }
  304. len = ETH_HLEN;
  305. if (ip6h)
  306. len += sizeof(*ip6h) + ntohs(ip6h->payload_len);
  307. if (iph)
  308. len += ntohs(iph->tot_len);
  309. swap(eth->h_dest, eth->h_source, ETH_ALEN);
  310. if (iph)
  311. swap(&iph->saddr, &iph->daddr, 4);
  312. else
  313. swap(&ip6h->saddr, &ip6h->daddr, 16);
  314. swap(&udph->source, &udph->dest, 2);
  315. want_csum = udph->check;
  316. if (ip6h)
  317. udph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
  318. ntohs(udph->len), IPPROTO_UDP, 0);
  319. else
  320. udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
  321. ntohs(udph->len), IPPROTO_UDP, 0);
  322. meta->flags |= XDP_TXMD_FLAGS_CHECKSUM;
  323. if (iph)
  324. meta->request.csum_start = sizeof(*eth) + sizeof(*iph);
  325. else
  326. meta->request.csum_start = sizeof(*eth) + sizeof(*ip6h);
  327. meta->request.csum_offset = offsetof(struct udphdr, check);
  328. printf("%p: ping-pong with csum=%04x (want %04x) csum_start=%d csum_offset=%d\n",
  329. xsk, ntohs(udph->check), ntohs(want_csum),
  330. meta->request.csum_start, meta->request.csum_offset);
  331. memcpy(data, rx_packet, len); /* don't share umem chunk for simplicity */
  332. tx_desc->options |= XDP_TX_METADATA;
  333. tx_desc->len = len;
  334. xsk_ring_prod__submit(&xsk->tx, 1);
  335. }
  336. static int verify_metadata(struct xsk *rx_xsk, int rxq, int server_fd, clockid_t clock_id)
  337. {
  338. const struct xdp_desc *rx_desc;
  339. struct pollfd fds[rxq + 1];
  340. __u64 comp_addr;
  341. __u64 addr;
  342. __u32 idx = 0;
  343. int ret;
  344. int i;
  345. for (i = 0; i < rxq; i++) {
  346. fds[i].fd = xsk_socket__fd(rx_xsk[i].socket);
  347. fds[i].events = POLLIN;
  348. fds[i].revents = 0;
  349. }
  350. fds[rxq].fd = server_fd;
  351. fds[rxq].events = POLLIN;
  352. fds[rxq].revents = 0;
  353. while (true) {
  354. errno = 0;
  355. for (i = 0; i < rxq; i++) {
  356. ret = kick_rx(&rx_xsk[i]);
  357. if (ret)
  358. printf("kick_rx ret=%d\n", ret);
  359. }
  360. ret = poll(fds, rxq + 1, 1000);
  361. printf("poll: %d (%d) skip=%llu fail=%llu redir=%llu\n",
  362. ret, errno, bpf_obj->bss->pkts_skip,
  363. bpf_obj->bss->pkts_fail, bpf_obj->bss->pkts_redir);
  364. if (ret < 0)
  365. break;
  366. if (ret == 0)
  367. continue;
  368. if (fds[rxq].revents)
  369. verify_skb_metadata(server_fd);
  370. for (i = 0; i < rxq; i++) {
  371. bool first_seg = true;
  372. bool is_eop = true;
  373. if (fds[i].revents == 0)
  374. continue;
  375. struct xsk *xsk = &rx_xsk[i];
  376. peek:
  377. ret = xsk_ring_cons__peek(&xsk->rx, 1, &idx);
  378. printf("xsk_ring_cons__peek: %d\n", ret);
  379. if (ret != 1)
  380. continue;
  381. rx_desc = xsk_ring_cons__rx_desc(&xsk->rx, idx);
  382. comp_addr = xsk_umem__extract_addr(rx_desc->addr);
  383. addr = xsk_umem__add_offset_to_addr(rx_desc->addr);
  384. is_eop = !(rx_desc->options & XDP_PKT_CONTD);
  385. printf("%p: rx_desc[%u]->addr=%llx addr=%llx comp_addr=%llx%s\n",
  386. xsk, idx, rx_desc->addr, addr, comp_addr, is_eop ? " EoP" : "");
  387. if (first_seg) {
  388. verify_xdp_metadata(xsk_umem__get_data(xsk->umem_area, addr),
  389. clock_id);
  390. first_seg = false;
  391. if (!skip_tx) {
  392. /* mirror first chunk back */
  393. ping_pong(xsk, xsk_umem__get_data(xsk->umem_area, addr),
  394. clock_id);
  395. ret = kick_tx(xsk);
  396. if (ret)
  397. printf("kick_tx ret=%d\n", ret);
  398. for (int j = 0; j < 500; j++) {
  399. if (complete_tx(xsk, clock_id))
  400. break;
  401. usleep(10);
  402. }
  403. }
  404. }
  405. xsk_ring_cons__release(&xsk->rx, 1);
  406. refill_rx(xsk, comp_addr);
  407. if (!is_eop)
  408. goto peek;
  409. }
  410. }
  411. return 0;
  412. }
  413. static int rxq_num(const char *ifname)
  414. {
  415. struct ethtool_channels ch = {
  416. .cmd = ETHTOOL_GCHANNELS,
  417. };
  418. struct ifreq ifr = {
  419. .ifr_data = (void *)&ch,
  420. };
  421. strncpy(ifr.ifr_name, ifname, IF_NAMESIZE - 1);
  422. int fd, ret;
  423. fd = socket(AF_UNIX, SOCK_DGRAM, 0);
  424. if (fd < 0)
  425. error(1, errno, "socket");
  426. ret = ioctl(fd, SIOCETHTOOL, &ifr);
  427. if (ret < 0)
  428. error(1, errno, "ioctl(SIOCETHTOOL)");
  429. close(fd);
  430. return ch.rx_count + ch.combined_count;
  431. }
  432. static void hwtstamp_ioctl(int op, const char *ifname, struct hwtstamp_config *cfg)
  433. {
  434. struct ifreq ifr = {
  435. .ifr_data = (void *)cfg,
  436. };
  437. strncpy(ifr.ifr_name, ifname, IF_NAMESIZE - 1);
  438. int fd, ret;
  439. fd = socket(AF_UNIX, SOCK_DGRAM, 0);
  440. if (fd < 0)
  441. error(1, errno, "socket");
  442. ret = ioctl(fd, op, &ifr);
  443. if (ret < 0)
  444. error(1, errno, "ioctl(%d)", op);
  445. close(fd);
  446. }
  447. static struct hwtstamp_config saved_hwtstamp_cfg;
  448. static const char *saved_hwtstamp_ifname;
  449. static void hwtstamp_restore(void)
  450. {
  451. hwtstamp_ioctl(SIOCSHWTSTAMP, saved_hwtstamp_ifname, &saved_hwtstamp_cfg);
  452. }
  453. static void hwtstamp_enable(const char *ifname)
  454. {
  455. struct hwtstamp_config cfg = {
  456. .rx_filter = HWTSTAMP_FILTER_ALL,
  457. };
  458. hwtstamp_ioctl(SIOCGHWTSTAMP, ifname, &saved_hwtstamp_cfg);
  459. saved_hwtstamp_ifname = strdup(ifname);
  460. atexit(hwtstamp_restore);
  461. hwtstamp_ioctl(SIOCSHWTSTAMP, ifname, &cfg);
  462. }
  463. static void cleanup(void)
  464. {
  465. LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
  466. int ret;
  467. int i;
  468. if (bpf_obj) {
  469. opts.old_prog_fd = bpf_program__fd(bpf_obj->progs.rx);
  470. if (opts.old_prog_fd >= 0) {
  471. printf("detaching bpf program....\n");
  472. ret = bpf_xdp_detach(ifindex, XDP_FLAGS, &opts);
  473. if (ret)
  474. printf("failed to detach XDP program: %d\n", ret);
  475. }
  476. }
  477. for (i = 0; i < rxq; i++)
  478. close_xsk(&rx_xsk[i]);
  479. if (bpf_obj)
  480. xdp_hw_metadata__destroy(bpf_obj);
  481. free((void *)saved_hwtstamp_ifname);
  482. }
  483. static void handle_signal(int sig)
  484. {
  485. /* interrupting poll() is all we need */
  486. }
  487. static void timestamping_enable(int fd, int val)
  488. {
  489. int ret;
  490. ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
  491. if (ret < 0)
  492. error(1, errno, "setsockopt(SO_TIMESTAMPING)");
  493. }
  494. static void print_usage(void)
  495. {
  496. const char *usage =
  497. "Usage: xdp_hw_metadata [OPTIONS] [IFNAME]\n"
  498. " -c Run in copy mode (zerocopy is default)\n"
  499. " -h Display this help and exit\n\n"
  500. " -m Enable multi-buffer XDP for larger MTU\n"
  501. " -r Don't generate AF_XDP reply (rx metadata only)\n"
  502. "Generate test packets on the other machine with:\n"
  503. " echo -n xdp | nc -u -q1 <dst_ip> 9091\n";
  504. printf("%s", usage);
  505. }
  506. static void read_args(int argc, char *argv[])
  507. {
  508. int opt;
  509. while ((opt = getopt(argc, argv, "chmr")) != -1) {
  510. switch (opt) {
  511. case 'c':
  512. bind_flags &= ~XDP_USE_NEED_WAKEUP;
  513. bind_flags &= ~XDP_ZEROCOPY;
  514. bind_flags |= XDP_COPY;
  515. break;
  516. case 'h':
  517. print_usage();
  518. exit(0);
  519. case 'm':
  520. bind_flags |= XDP_USE_SG;
  521. break;
  522. case 'r':
  523. skip_tx = true;
  524. break;
  525. case '?':
  526. if (isprint(optopt))
  527. fprintf(stderr, "Unknown option: -%c\n", optopt);
  528. fallthrough;
  529. default:
  530. print_usage();
  531. error(-1, opterr, "Command line options error");
  532. }
  533. }
  534. if (optind >= argc) {
  535. fprintf(stderr, "No device name provided\n");
  536. print_usage();
  537. exit(-1);
  538. }
  539. ifname = argv[optind];
  540. ifindex = if_nametoindex(ifname);
  541. if (!ifname)
  542. error(-1, errno, "Invalid interface name");
  543. }
  544. int main(int argc, char *argv[])
  545. {
  546. clockid_t clock_id = CLOCK_TAI;
  547. int server_fd = -1;
  548. int ret;
  549. int i;
  550. struct bpf_program *prog;
  551. read_args(argc, argv);
  552. rxq = rxq_num(ifname);
  553. printf("rxq: %d\n", rxq);
  554. hwtstamp_enable(ifname);
  555. rx_xsk = malloc(sizeof(struct xsk) * rxq);
  556. if (!rx_xsk)
  557. error(1, ENOMEM, "malloc");
  558. for (i = 0; i < rxq; i++) {
  559. printf("open_xsk(%s, %p, %d)\n", ifname, &rx_xsk[i], i);
  560. ret = open_xsk(ifindex, &rx_xsk[i], i);
  561. if (ret)
  562. error(1, -ret, "open_xsk");
  563. printf("xsk_socket__fd() -> %d\n", xsk_socket__fd(rx_xsk[i].socket));
  564. }
  565. printf("open bpf program...\n");
  566. bpf_obj = xdp_hw_metadata__open();
  567. if (libbpf_get_error(bpf_obj))
  568. error(1, libbpf_get_error(bpf_obj), "xdp_hw_metadata__open");
  569. prog = bpf_object__find_program_by_name(bpf_obj->obj, "rx");
  570. bpf_program__set_ifindex(prog, ifindex);
  571. bpf_program__set_flags(prog, BPF_F_XDP_DEV_BOUND_ONLY);
  572. printf("load bpf program...\n");
  573. ret = xdp_hw_metadata__load(bpf_obj);
  574. if (ret)
  575. error(1, -ret, "xdp_hw_metadata__load");
  576. printf("prepare skb endpoint...\n");
  577. server_fd = start_server(AF_INET6, SOCK_DGRAM, NULL, 9092, 1000);
  578. if (server_fd < 0)
  579. error(1, errno, "start_server");
  580. timestamping_enable(server_fd,
  581. SOF_TIMESTAMPING_SOFTWARE |
  582. SOF_TIMESTAMPING_RAW_HARDWARE);
  583. printf("prepare xsk map...\n");
  584. for (i = 0; i < rxq; i++) {
  585. int sock_fd = xsk_socket__fd(rx_xsk[i].socket);
  586. __u32 queue_id = i;
  587. printf("map[%d] = %d\n", queue_id, sock_fd);
  588. ret = bpf_map_update_elem(bpf_map__fd(bpf_obj->maps.xsk), &queue_id, &sock_fd, 0);
  589. if (ret)
  590. error(1, -ret, "bpf_map_update_elem");
  591. }
  592. printf("attach bpf program...\n");
  593. ret = bpf_xdp_attach(ifindex,
  594. bpf_program__fd(bpf_obj->progs.rx),
  595. XDP_FLAGS, NULL);
  596. if (ret)
  597. error(1, -ret, "bpf_xdp_attach");
  598. signal(SIGINT, handle_signal);
  599. ret = verify_metadata(rx_xsk, rxq, server_fd, clock_id);
  600. close(server_fd);
  601. cleanup();
  602. if (ret)
  603. error(1, -ret, "verify_metadata");
  604. }