18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2020 Intel Corporation. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#define _GNU_SOURCE 58c2ecf20Sopenharmony_ci#include <poll.h> 68c2ecf20Sopenharmony_ci#include <pthread.h> 78c2ecf20Sopenharmony_ci#include <signal.h> 88c2ecf20Sopenharmony_ci#include <sched.h> 98c2ecf20Sopenharmony_ci#include <stdio.h> 108c2ecf20Sopenharmony_ci#include <stdlib.h> 118c2ecf20Sopenharmony_ci#include <string.h> 128c2ecf20Sopenharmony_ci#include <sys/mman.h> 138c2ecf20Sopenharmony_ci#include <sys/resource.h> 148c2ecf20Sopenharmony_ci#include <sys/socket.h> 158c2ecf20Sopenharmony_ci#include <sys/types.h> 168c2ecf20Sopenharmony_ci#include <time.h> 178c2ecf20Sopenharmony_ci#include <unistd.h> 188c2ecf20Sopenharmony_ci#include <getopt.h> 198c2ecf20Sopenharmony_ci#include <netinet/ether.h> 208c2ecf20Sopenharmony_ci#include <net/if.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/bpf.h> 238c2ecf20Sopenharmony_ci#include <linux/if_link.h> 248c2ecf20Sopenharmony_ci#include <linux/if_xdp.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <bpf/libbpf.h> 278c2ecf20Sopenharmony_ci#include <bpf/xsk.h> 288c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_citypedef __u64 u64; 338c2ecf20Sopenharmony_citypedef __u32 u32; 348c2ecf20Sopenharmony_citypedef __u16 u16; 358c2ecf20Sopenharmony_citypedef __u8 u8; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* This program illustrates the packet forwarding between multiple AF_XDP 388c2ecf20Sopenharmony_ci * sockets in multi-threaded environment. All threads are sharing a common 398c2ecf20Sopenharmony_ci * buffer pool, with each socket having its own private buffer cache. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Example 1: Single thread handling two sockets. The packets received by socket 428c2ecf20Sopenharmony_ci * A (interface IFA, queue QA) are forwarded to socket B (interface IFB, queue 438c2ecf20Sopenharmony_ci * QB), while the packets received by socket B are forwarded to socket A. The 448c2ecf20Sopenharmony_ci * thread is running on CPU core X: 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * ./xsk_fwd -i IFA -q QA -i IFB -q QB -c X 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Example 2: Two threads, each handling two sockets. The thread running on CPU 498c2ecf20Sopenharmony_ci * core X forwards all the packets received by socket A to socket B, and all the 508c2ecf20Sopenharmony_ci * packets received by socket B to socket A. The thread running on CPU core Y is 518c2ecf20Sopenharmony_ci * performing the same packet forwarding between sockets C and D: 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * ./xsk_fwd -i IFA -q QA -i IFB -q QB -i IFC -q QC -i IFD -q QD 548c2ecf20Sopenharmony_ci * -c CX -c CY 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Buffer pool and buffer cache 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * For packet forwarding, the packet buffers are typically allocated from the 618c2ecf20Sopenharmony_ci * pool for packet reception and freed back to the pool for further reuse once 628c2ecf20Sopenharmony_ci * the packet transmission is completed. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * The buffer pool is shared between multiple threads. In order to minimize the 658c2ecf20Sopenharmony_ci * access latency to the shared buffer pool, each thread creates one (or 668c2ecf20Sopenharmony_ci * several) buffer caches, which, unlike the buffer pool, are private to the 678c2ecf20Sopenharmony_ci * thread that creates them and therefore cannot be shared with other threads. 688c2ecf20Sopenharmony_ci * The access to the shared pool is only needed either (A) when the cache gets 698c2ecf20Sopenharmony_ci * empty due to repeated buffer allocations and it needs to be replenished from 708c2ecf20Sopenharmony_ci * the pool, or (B) when the cache gets full due to repeated buffer free and it 718c2ecf20Sopenharmony_ci * needs to be flushed back to the pull. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * In a packet forwarding system, a packet received on any input port can 748c2ecf20Sopenharmony_ci * potentially be transmitted on any output port, depending on the forwarding 758c2ecf20Sopenharmony_ci * configuration. For AF_XDP sockets, for this to work with zero-copy of the 768c2ecf20Sopenharmony_ci * packet buffers when, it is required that the buffer pool memory fits into the 778c2ecf20Sopenharmony_ci * UMEM area shared by all the sockets. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct bpool_params { 818c2ecf20Sopenharmony_ci u32 n_buffers; 828c2ecf20Sopenharmony_ci u32 buffer_size; 838c2ecf20Sopenharmony_ci int mmap_flags; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci u32 n_users_max; 868c2ecf20Sopenharmony_ci u32 n_buffers_per_slab; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* This buffer pool implementation organizes the buffers into equally sized 908c2ecf20Sopenharmony_ci * slabs of *n_buffers_per_slab*. Initially, there are *n_slabs* slabs in the 918c2ecf20Sopenharmony_ci * pool that are completely filled with buffer pointers (full slabs). 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * Each buffer cache has a slab for buffer allocation and a slab for buffer 948c2ecf20Sopenharmony_ci * free, with both of these slabs initially empty. When the cache's allocation 958c2ecf20Sopenharmony_ci * slab goes empty, it is swapped with one of the available full slabs from the 968c2ecf20Sopenharmony_ci * pool, if any is available. When the cache's free slab goes full, it is 978c2ecf20Sopenharmony_ci * swapped for one of the empty slabs from the pool, which is guaranteed to 988c2ecf20Sopenharmony_ci * succeed. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Partially filled slabs never get traded between the cache and the pool 1018c2ecf20Sopenharmony_ci * (except when the cache itself is destroyed), which enables fast operation 1028c2ecf20Sopenharmony_ci * through pointer swapping. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistruct bpool { 1058c2ecf20Sopenharmony_ci struct bpool_params params; 1068c2ecf20Sopenharmony_ci pthread_mutex_t lock; 1078c2ecf20Sopenharmony_ci void *addr; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci u64 **slabs; 1108c2ecf20Sopenharmony_ci u64 **slabs_reserved; 1118c2ecf20Sopenharmony_ci u64 *buffers; 1128c2ecf20Sopenharmony_ci u64 *buffers_reserved; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci u64 n_slabs; 1158c2ecf20Sopenharmony_ci u64 n_slabs_reserved; 1168c2ecf20Sopenharmony_ci u64 n_buffers; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci u64 n_slabs_available; 1198c2ecf20Sopenharmony_ci u64 n_slabs_reserved_available; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci struct xsk_umem_config umem_cfg; 1228c2ecf20Sopenharmony_ci struct xsk_ring_prod umem_fq; 1238c2ecf20Sopenharmony_ci struct xsk_ring_cons umem_cq; 1248c2ecf20Sopenharmony_ci struct xsk_umem *umem; 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct bpool * 1288c2ecf20Sopenharmony_cibpool_init(struct bpool_params *params, 1298c2ecf20Sopenharmony_ci struct xsk_umem_config *umem_cfg) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 1328c2ecf20Sopenharmony_ci u64 n_slabs, n_slabs_reserved, n_buffers, n_buffers_reserved; 1338c2ecf20Sopenharmony_ci u64 slabs_size, slabs_reserved_size; 1348c2ecf20Sopenharmony_ci u64 buffers_size, buffers_reserved_size; 1358c2ecf20Sopenharmony_ci u64 total_size, i; 1368c2ecf20Sopenharmony_ci struct bpool *bp; 1378c2ecf20Sopenharmony_ci u8 *p; 1388c2ecf20Sopenharmony_ci int status; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* mmap prep. */ 1418c2ecf20Sopenharmony_ci if (setrlimit(RLIMIT_MEMLOCK, &r)) 1428c2ecf20Sopenharmony_ci return NULL; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* bpool internals dimensioning. */ 1458c2ecf20Sopenharmony_ci n_slabs = (params->n_buffers + params->n_buffers_per_slab - 1) / 1468c2ecf20Sopenharmony_ci params->n_buffers_per_slab; 1478c2ecf20Sopenharmony_ci n_slabs_reserved = params->n_users_max * 2; 1488c2ecf20Sopenharmony_ci n_buffers = n_slabs * params->n_buffers_per_slab; 1498c2ecf20Sopenharmony_ci n_buffers_reserved = n_slabs_reserved * params->n_buffers_per_slab; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci slabs_size = n_slabs * sizeof(u64 *); 1528c2ecf20Sopenharmony_ci slabs_reserved_size = n_slabs_reserved * sizeof(u64 *); 1538c2ecf20Sopenharmony_ci buffers_size = n_buffers * sizeof(u64); 1548c2ecf20Sopenharmony_ci buffers_reserved_size = n_buffers_reserved * sizeof(u64); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci total_size = sizeof(struct bpool) + 1578c2ecf20Sopenharmony_ci slabs_size + slabs_reserved_size + 1588c2ecf20Sopenharmony_ci buffers_size + buffers_reserved_size; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* bpool memory allocation. */ 1618c2ecf20Sopenharmony_ci p = calloc(total_size, sizeof(u8)); 1628c2ecf20Sopenharmony_ci if (!p) 1638c2ecf20Sopenharmony_ci return NULL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* bpool memory initialization. */ 1668c2ecf20Sopenharmony_ci bp = (struct bpool *)p; 1678c2ecf20Sopenharmony_ci memcpy(&bp->params, params, sizeof(*params)); 1688c2ecf20Sopenharmony_ci bp->params.n_buffers = n_buffers; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci bp->slabs = (u64 **)&p[sizeof(struct bpool)]; 1718c2ecf20Sopenharmony_ci bp->slabs_reserved = (u64 **)&p[sizeof(struct bpool) + 1728c2ecf20Sopenharmony_ci slabs_size]; 1738c2ecf20Sopenharmony_ci bp->buffers = (u64 *)&p[sizeof(struct bpool) + 1748c2ecf20Sopenharmony_ci slabs_size + slabs_reserved_size]; 1758c2ecf20Sopenharmony_ci bp->buffers_reserved = (u64 *)&p[sizeof(struct bpool) + 1768c2ecf20Sopenharmony_ci slabs_size + slabs_reserved_size + buffers_size]; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci bp->n_slabs = n_slabs; 1798c2ecf20Sopenharmony_ci bp->n_slabs_reserved = n_slabs_reserved; 1808c2ecf20Sopenharmony_ci bp->n_buffers = n_buffers; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < n_slabs; i++) 1838c2ecf20Sopenharmony_ci bp->slabs[i] = &bp->buffers[i * params->n_buffers_per_slab]; 1848c2ecf20Sopenharmony_ci bp->n_slabs_available = n_slabs; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for (i = 0; i < n_slabs_reserved; i++) 1878c2ecf20Sopenharmony_ci bp->slabs_reserved[i] = &bp->buffers_reserved[i * 1888c2ecf20Sopenharmony_ci params->n_buffers_per_slab]; 1898c2ecf20Sopenharmony_ci bp->n_slabs_reserved_available = n_slabs_reserved; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (i = 0; i < n_buffers; i++) 1928c2ecf20Sopenharmony_ci bp->buffers[i] = i * params->buffer_size; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* lock. */ 1958c2ecf20Sopenharmony_ci status = pthread_mutex_init(&bp->lock, NULL); 1968c2ecf20Sopenharmony_ci if (status) { 1978c2ecf20Sopenharmony_ci free(p); 1988c2ecf20Sopenharmony_ci return NULL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* mmap. */ 2028c2ecf20Sopenharmony_ci bp->addr = mmap(NULL, 2038c2ecf20Sopenharmony_ci n_buffers * params->buffer_size, 2048c2ecf20Sopenharmony_ci PROT_READ | PROT_WRITE, 2058c2ecf20Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS | params->mmap_flags, 2068c2ecf20Sopenharmony_ci -1, 2078c2ecf20Sopenharmony_ci 0); 2088c2ecf20Sopenharmony_ci if (bp->addr == MAP_FAILED) { 2098c2ecf20Sopenharmony_ci pthread_mutex_destroy(&bp->lock); 2108c2ecf20Sopenharmony_ci free(p); 2118c2ecf20Sopenharmony_ci return NULL; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* umem. */ 2158c2ecf20Sopenharmony_ci status = xsk_umem__create(&bp->umem, 2168c2ecf20Sopenharmony_ci bp->addr, 2178c2ecf20Sopenharmony_ci bp->params.n_buffers * bp->params.buffer_size, 2188c2ecf20Sopenharmony_ci &bp->umem_fq, 2198c2ecf20Sopenharmony_ci &bp->umem_cq, 2208c2ecf20Sopenharmony_ci umem_cfg); 2218c2ecf20Sopenharmony_ci if (status) { 2228c2ecf20Sopenharmony_ci munmap(bp->addr, bp->params.n_buffers * bp->params.buffer_size); 2238c2ecf20Sopenharmony_ci pthread_mutex_destroy(&bp->lock); 2248c2ecf20Sopenharmony_ci free(p); 2258c2ecf20Sopenharmony_ci return NULL; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci memcpy(&bp->umem_cfg, umem_cfg, sizeof(*umem_cfg)); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return bp; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void 2338c2ecf20Sopenharmony_cibpool_free(struct bpool *bp) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci if (!bp) 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci xsk_umem__delete(bp->umem); 2398c2ecf20Sopenharmony_ci munmap(bp->addr, bp->params.n_buffers * bp->params.buffer_size); 2408c2ecf20Sopenharmony_ci pthread_mutex_destroy(&bp->lock); 2418c2ecf20Sopenharmony_ci free(bp); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistruct bcache { 2458c2ecf20Sopenharmony_ci struct bpool *bp; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci u64 *slab_cons; 2488c2ecf20Sopenharmony_ci u64 *slab_prod; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci u64 n_buffers_cons; 2518c2ecf20Sopenharmony_ci u64 n_buffers_prod; 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic u32 2558c2ecf20Sopenharmony_cibcache_slab_size(struct bcache *bc) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct bpool *bp = bc->bp; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return bp->params.n_buffers_per_slab; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic struct bcache * 2638c2ecf20Sopenharmony_cibcache_init(struct bpool *bp) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct bcache *bc; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci bc = calloc(1, sizeof(struct bcache)); 2688c2ecf20Sopenharmony_ci if (!bc) 2698c2ecf20Sopenharmony_ci return NULL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci bc->bp = bp; 2728c2ecf20Sopenharmony_ci bc->n_buffers_cons = 0; 2738c2ecf20Sopenharmony_ci bc->n_buffers_prod = 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pthread_mutex_lock(&bp->lock); 2768c2ecf20Sopenharmony_ci if (bp->n_slabs_reserved_available == 0) { 2778c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 2788c2ecf20Sopenharmony_ci free(bc); 2798c2ecf20Sopenharmony_ci return NULL; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci bc->slab_cons = bp->slabs_reserved[bp->n_slabs_reserved_available - 1]; 2838c2ecf20Sopenharmony_ci bc->slab_prod = bp->slabs_reserved[bp->n_slabs_reserved_available - 2]; 2848c2ecf20Sopenharmony_ci bp->n_slabs_reserved_available -= 2; 2858c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return bc; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void 2918c2ecf20Sopenharmony_cibcache_free(struct bcache *bc) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct bpool *bp; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!bc) 2968c2ecf20Sopenharmony_ci return; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* In order to keep this example simple, the case of freeing any 2998c2ecf20Sopenharmony_ci * existing buffers from the cache back to the pool is ignored. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci bp = bc->bp; 3038c2ecf20Sopenharmony_ci pthread_mutex_lock(&bp->lock); 3048c2ecf20Sopenharmony_ci bp->slabs_reserved[bp->n_slabs_reserved_available] = bc->slab_prod; 3058c2ecf20Sopenharmony_ci bp->slabs_reserved[bp->n_slabs_reserved_available + 1] = bc->slab_cons; 3068c2ecf20Sopenharmony_ci bp->n_slabs_reserved_available += 2; 3078c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci free(bc); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* To work correctly, the implementation requires that the *n_buffers* input 3138c2ecf20Sopenharmony_ci * argument is never greater than the buffer pool's *n_buffers_per_slab*. This 3148c2ecf20Sopenharmony_ci * is typically the case, with one exception taking place when large number of 3158c2ecf20Sopenharmony_ci * buffers are allocated at init time (e.g. for the UMEM fill queue setup). 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic inline u32 3188c2ecf20Sopenharmony_cibcache_cons_check(struct bcache *bc, u32 n_buffers) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct bpool *bp = bc->bp; 3218c2ecf20Sopenharmony_ci u64 n_buffers_per_slab = bp->params.n_buffers_per_slab; 3228c2ecf20Sopenharmony_ci u64 n_buffers_cons = bc->n_buffers_cons; 3238c2ecf20Sopenharmony_ci u64 n_slabs_available; 3248c2ecf20Sopenharmony_ci u64 *slab_full; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * Consumer slab is not empty: Use what's available locally. Do not 3288c2ecf20Sopenharmony_ci * look for more buffers from the pool when the ask can only be 3298c2ecf20Sopenharmony_ci * partially satisfied. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (n_buffers_cons) 3328c2ecf20Sopenharmony_ci return (n_buffers_cons < n_buffers) ? 3338c2ecf20Sopenharmony_ci n_buffers_cons : 3348c2ecf20Sopenharmony_ci n_buffers; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * Consumer slab is empty: look to trade the current consumer slab 3388c2ecf20Sopenharmony_ci * (full) for a full slab from the pool, if any is available. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci pthread_mutex_lock(&bp->lock); 3418c2ecf20Sopenharmony_ci n_slabs_available = bp->n_slabs_available; 3428c2ecf20Sopenharmony_ci if (!n_slabs_available) { 3438c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci n_slabs_available--; 3488c2ecf20Sopenharmony_ci slab_full = bp->slabs[n_slabs_available]; 3498c2ecf20Sopenharmony_ci bp->slabs[n_slabs_available] = bc->slab_cons; 3508c2ecf20Sopenharmony_ci bp->n_slabs_available = n_slabs_available; 3518c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci bc->slab_cons = slab_full; 3548c2ecf20Sopenharmony_ci bc->n_buffers_cons = n_buffers_per_slab; 3558c2ecf20Sopenharmony_ci return n_buffers; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline u64 3598c2ecf20Sopenharmony_cibcache_cons(struct bcache *bc) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci u64 n_buffers_cons = bc->n_buffers_cons - 1; 3628c2ecf20Sopenharmony_ci u64 buffer; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci buffer = bc->slab_cons[n_buffers_cons]; 3658c2ecf20Sopenharmony_ci bc->n_buffers_cons = n_buffers_cons; 3668c2ecf20Sopenharmony_ci return buffer; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic inline void 3708c2ecf20Sopenharmony_cibcache_prod(struct bcache *bc, u64 buffer) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct bpool *bp = bc->bp; 3738c2ecf20Sopenharmony_ci u64 n_buffers_per_slab = bp->params.n_buffers_per_slab; 3748c2ecf20Sopenharmony_ci u64 n_buffers_prod = bc->n_buffers_prod; 3758c2ecf20Sopenharmony_ci u64 n_slabs_available; 3768c2ecf20Sopenharmony_ci u64 *slab_empty; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Producer slab is not yet full: store the current buffer to it. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci if (n_buffers_prod < n_buffers_per_slab) { 3828c2ecf20Sopenharmony_ci bc->slab_prod[n_buffers_prod] = buffer; 3838c2ecf20Sopenharmony_ci bc->n_buffers_prod = n_buffers_prod + 1; 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * Producer slab is full: trade the cache's current producer slab 3898c2ecf20Sopenharmony_ci * (full) for an empty slab from the pool, then store the current 3908c2ecf20Sopenharmony_ci * buffer to the new producer slab. As one full slab exists in the 3918c2ecf20Sopenharmony_ci * cache, it is guaranteed that there is at least one empty slab 3928c2ecf20Sopenharmony_ci * available in the pool. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci pthread_mutex_lock(&bp->lock); 3958c2ecf20Sopenharmony_ci n_slabs_available = bp->n_slabs_available; 3968c2ecf20Sopenharmony_ci slab_empty = bp->slabs[n_slabs_available]; 3978c2ecf20Sopenharmony_ci bp->slabs[n_slabs_available] = bc->slab_prod; 3988c2ecf20Sopenharmony_ci bp->n_slabs_available = n_slabs_available + 1; 3998c2ecf20Sopenharmony_ci pthread_mutex_unlock(&bp->lock); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci slab_empty[0] = buffer; 4028c2ecf20Sopenharmony_ci bc->slab_prod = slab_empty; 4038c2ecf20Sopenharmony_ci bc->n_buffers_prod = 1; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * Port 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Each of the forwarding ports sits on top of an AF_XDP socket. In order for 4108c2ecf20Sopenharmony_ci * packet forwarding to happen with no packet buffer copy, all the sockets need 4118c2ecf20Sopenharmony_ci * to share the same UMEM area, which is used as the buffer pool memory. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci#ifndef MAX_BURST_RX 4148c2ecf20Sopenharmony_ci#define MAX_BURST_RX 64 4158c2ecf20Sopenharmony_ci#endif 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci#ifndef MAX_BURST_TX 4188c2ecf20Sopenharmony_ci#define MAX_BURST_TX 64 4198c2ecf20Sopenharmony_ci#endif 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistruct burst_rx { 4228c2ecf20Sopenharmony_ci u64 addr[MAX_BURST_RX]; 4238c2ecf20Sopenharmony_ci u32 len[MAX_BURST_RX]; 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistruct burst_tx { 4278c2ecf20Sopenharmony_ci u64 addr[MAX_BURST_TX]; 4288c2ecf20Sopenharmony_ci u32 len[MAX_BURST_TX]; 4298c2ecf20Sopenharmony_ci u32 n_pkts; 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistruct port_params { 4338c2ecf20Sopenharmony_ci struct xsk_socket_config xsk_cfg; 4348c2ecf20Sopenharmony_ci struct bpool *bp; 4358c2ecf20Sopenharmony_ci const char *iface; 4368c2ecf20Sopenharmony_ci u32 iface_queue; 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistruct port { 4408c2ecf20Sopenharmony_ci struct port_params params; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci struct bcache *bc; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci struct xsk_ring_cons rxq; 4458c2ecf20Sopenharmony_ci struct xsk_ring_prod txq; 4468c2ecf20Sopenharmony_ci struct xsk_ring_prod umem_fq; 4478c2ecf20Sopenharmony_ci struct xsk_ring_cons umem_cq; 4488c2ecf20Sopenharmony_ci struct xsk_socket *xsk; 4498c2ecf20Sopenharmony_ci int umem_fq_initialized; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci u64 n_pkts_rx; 4528c2ecf20Sopenharmony_ci u64 n_pkts_tx; 4538c2ecf20Sopenharmony_ci}; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void 4568c2ecf20Sopenharmony_ciport_free(struct port *p) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci if (!p) 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* To keep this example simple, the code to free the buffers from the 4628c2ecf20Sopenharmony_ci * socket's receive and transmit queues, as well as from the UMEM fill 4638c2ecf20Sopenharmony_ci * and completion queues, is not included. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (p->xsk) 4678c2ecf20Sopenharmony_ci xsk_socket__delete(p->xsk); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci bcache_free(p->bc); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci free(p); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic struct port * 4758c2ecf20Sopenharmony_ciport_init(struct port_params *params) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct port *p; 4788c2ecf20Sopenharmony_ci u32 umem_fq_size, pos = 0; 4798c2ecf20Sopenharmony_ci int status, i; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Memory allocation and initialization. */ 4828c2ecf20Sopenharmony_ci p = calloc(sizeof(struct port), 1); 4838c2ecf20Sopenharmony_ci if (!p) 4848c2ecf20Sopenharmony_ci return NULL; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci memcpy(&p->params, params, sizeof(p->params)); 4878c2ecf20Sopenharmony_ci umem_fq_size = params->bp->umem_cfg.fill_size; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* bcache. */ 4908c2ecf20Sopenharmony_ci p->bc = bcache_init(params->bp); 4918c2ecf20Sopenharmony_ci if (!p->bc || 4928c2ecf20Sopenharmony_ci (bcache_slab_size(p->bc) < umem_fq_size) || 4938c2ecf20Sopenharmony_ci (bcache_cons_check(p->bc, umem_fq_size) < umem_fq_size)) { 4948c2ecf20Sopenharmony_ci port_free(p); 4958c2ecf20Sopenharmony_ci return NULL; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* xsk socket. */ 4998c2ecf20Sopenharmony_ci status = xsk_socket__create_shared(&p->xsk, 5008c2ecf20Sopenharmony_ci params->iface, 5018c2ecf20Sopenharmony_ci params->iface_queue, 5028c2ecf20Sopenharmony_ci params->bp->umem, 5038c2ecf20Sopenharmony_ci &p->rxq, 5048c2ecf20Sopenharmony_ci &p->txq, 5058c2ecf20Sopenharmony_ci &p->umem_fq, 5068c2ecf20Sopenharmony_ci &p->umem_cq, 5078c2ecf20Sopenharmony_ci ¶ms->xsk_cfg); 5088c2ecf20Sopenharmony_ci if (status) { 5098c2ecf20Sopenharmony_ci port_free(p); 5108c2ecf20Sopenharmony_ci return NULL; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* umem fq. */ 5148c2ecf20Sopenharmony_ci xsk_ring_prod__reserve(&p->umem_fq, umem_fq_size, &pos); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci for (i = 0; i < umem_fq_size; i++) 5178c2ecf20Sopenharmony_ci *xsk_ring_prod__fill_addr(&p->umem_fq, pos + i) = 5188c2ecf20Sopenharmony_ci bcache_cons(p->bc); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci xsk_ring_prod__submit(&p->umem_fq, umem_fq_size); 5218c2ecf20Sopenharmony_ci p->umem_fq_initialized = 1; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return p; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic inline u32 5278c2ecf20Sopenharmony_ciport_rx_burst(struct port *p, struct burst_rx *b) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci u32 n_pkts, pos, i; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Free buffers for FQ replenish. */ 5328c2ecf20Sopenharmony_ci n_pkts = ARRAY_SIZE(b->addr); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci n_pkts = bcache_cons_check(p->bc, n_pkts); 5358c2ecf20Sopenharmony_ci if (!n_pkts) 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* RXQ. */ 5398c2ecf20Sopenharmony_ci n_pkts = xsk_ring_cons__peek(&p->rxq, n_pkts, &pos); 5408c2ecf20Sopenharmony_ci if (!n_pkts) { 5418c2ecf20Sopenharmony_ci if (xsk_ring_prod__needs_wakeup(&p->umem_fq)) { 5428c2ecf20Sopenharmony_ci struct pollfd pollfd = { 5438c2ecf20Sopenharmony_ci .fd = xsk_socket__fd(p->xsk), 5448c2ecf20Sopenharmony_ci .events = POLLIN, 5458c2ecf20Sopenharmony_ci }; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci poll(&pollfd, 1, 0); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci for (i = 0; i < n_pkts; i++) { 5538c2ecf20Sopenharmony_ci b->addr[i] = xsk_ring_cons__rx_desc(&p->rxq, pos + i)->addr; 5548c2ecf20Sopenharmony_ci b->len[i] = xsk_ring_cons__rx_desc(&p->rxq, pos + i)->len; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci xsk_ring_cons__release(&p->rxq, n_pkts); 5588c2ecf20Sopenharmony_ci p->n_pkts_rx += n_pkts; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* UMEM FQ. */ 5618c2ecf20Sopenharmony_ci for ( ; ; ) { 5628c2ecf20Sopenharmony_ci int status; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci status = xsk_ring_prod__reserve(&p->umem_fq, n_pkts, &pos); 5658c2ecf20Sopenharmony_ci if (status == n_pkts) 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (xsk_ring_prod__needs_wakeup(&p->umem_fq)) { 5698c2ecf20Sopenharmony_ci struct pollfd pollfd = { 5708c2ecf20Sopenharmony_ci .fd = xsk_socket__fd(p->xsk), 5718c2ecf20Sopenharmony_ci .events = POLLIN, 5728c2ecf20Sopenharmony_ci }; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci poll(&pollfd, 1, 0); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci for (i = 0; i < n_pkts; i++) 5798c2ecf20Sopenharmony_ci *xsk_ring_prod__fill_addr(&p->umem_fq, pos + i) = 5808c2ecf20Sopenharmony_ci bcache_cons(p->bc); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci xsk_ring_prod__submit(&p->umem_fq, n_pkts); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return n_pkts; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic inline void 5888c2ecf20Sopenharmony_ciport_tx_burst(struct port *p, struct burst_tx *b) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci u32 n_pkts, pos, i; 5918c2ecf20Sopenharmony_ci int status; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* UMEM CQ. */ 5948c2ecf20Sopenharmony_ci n_pkts = p->params.bp->umem_cfg.comp_size; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci n_pkts = xsk_ring_cons__peek(&p->umem_cq, n_pkts, &pos); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci for (i = 0; i < n_pkts; i++) { 5998c2ecf20Sopenharmony_ci u64 addr = *xsk_ring_cons__comp_addr(&p->umem_cq, pos + i); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci bcache_prod(p->bc, addr); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci xsk_ring_cons__release(&p->umem_cq, n_pkts); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* TXQ. */ 6078c2ecf20Sopenharmony_ci n_pkts = b->n_pkts; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci for ( ; ; ) { 6108c2ecf20Sopenharmony_ci status = xsk_ring_prod__reserve(&p->txq, n_pkts, &pos); 6118c2ecf20Sopenharmony_ci if (status == n_pkts) 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (xsk_ring_prod__needs_wakeup(&p->txq)) 6158c2ecf20Sopenharmony_ci sendto(xsk_socket__fd(p->xsk), NULL, 0, MSG_DONTWAIT, 6168c2ecf20Sopenharmony_ci NULL, 0); 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci for (i = 0; i < n_pkts; i++) { 6208c2ecf20Sopenharmony_ci xsk_ring_prod__tx_desc(&p->txq, pos + i)->addr = b->addr[i]; 6218c2ecf20Sopenharmony_ci xsk_ring_prod__tx_desc(&p->txq, pos + i)->len = b->len[i]; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci xsk_ring_prod__submit(&p->txq, n_pkts); 6258c2ecf20Sopenharmony_ci if (xsk_ring_prod__needs_wakeup(&p->txq)) 6268c2ecf20Sopenharmony_ci sendto(xsk_socket__fd(p->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); 6278c2ecf20Sopenharmony_ci p->n_pkts_tx += n_pkts; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* 6318c2ecf20Sopenharmony_ci * Thread 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * Packet forwarding threads. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci#ifndef MAX_PORTS_PER_THREAD 6368c2ecf20Sopenharmony_ci#define MAX_PORTS_PER_THREAD 16 6378c2ecf20Sopenharmony_ci#endif 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistruct thread_data { 6408c2ecf20Sopenharmony_ci struct port *ports_rx[MAX_PORTS_PER_THREAD]; 6418c2ecf20Sopenharmony_ci struct port *ports_tx[MAX_PORTS_PER_THREAD]; 6428c2ecf20Sopenharmony_ci u32 n_ports_rx; 6438c2ecf20Sopenharmony_ci struct burst_rx burst_rx; 6448c2ecf20Sopenharmony_ci struct burst_tx burst_tx[MAX_PORTS_PER_THREAD]; 6458c2ecf20Sopenharmony_ci u32 cpu_core_id; 6468c2ecf20Sopenharmony_ci int quit; 6478c2ecf20Sopenharmony_ci}; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic void swap_mac_addresses(void *data) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct ether_header *eth = (struct ether_header *)data; 6528c2ecf20Sopenharmony_ci struct ether_addr *src_addr = (struct ether_addr *)ð->ether_shost; 6538c2ecf20Sopenharmony_ci struct ether_addr *dst_addr = (struct ether_addr *)ð->ether_dhost; 6548c2ecf20Sopenharmony_ci struct ether_addr tmp; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci tmp = *src_addr; 6578c2ecf20Sopenharmony_ci *src_addr = *dst_addr; 6588c2ecf20Sopenharmony_ci *dst_addr = tmp; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void * 6628c2ecf20Sopenharmony_cithread_func(void *arg) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct thread_data *t = arg; 6658c2ecf20Sopenharmony_ci cpu_set_t cpu_cores; 6668c2ecf20Sopenharmony_ci u32 i; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci CPU_ZERO(&cpu_cores); 6698c2ecf20Sopenharmony_ci CPU_SET(t->cpu_core_id, &cpu_cores); 6708c2ecf20Sopenharmony_ci pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_cores); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci for (i = 0; !t->quit; i = (i + 1) & (t->n_ports_rx - 1)) { 6738c2ecf20Sopenharmony_ci struct port *port_rx = t->ports_rx[i]; 6748c2ecf20Sopenharmony_ci struct port *port_tx = t->ports_tx[i]; 6758c2ecf20Sopenharmony_ci struct burst_rx *brx = &t->burst_rx; 6768c2ecf20Sopenharmony_ci struct burst_tx *btx = &t->burst_tx[i]; 6778c2ecf20Sopenharmony_ci u32 n_pkts, j; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* RX. */ 6808c2ecf20Sopenharmony_ci n_pkts = port_rx_burst(port_rx, brx); 6818c2ecf20Sopenharmony_ci if (!n_pkts) 6828c2ecf20Sopenharmony_ci continue; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Process & TX. */ 6858c2ecf20Sopenharmony_ci for (j = 0; j < n_pkts; j++) { 6868c2ecf20Sopenharmony_ci u64 addr = xsk_umem__add_offset_to_addr(brx->addr[j]); 6878c2ecf20Sopenharmony_ci u8 *pkt = xsk_umem__get_data(port_rx->params.bp->addr, 6888c2ecf20Sopenharmony_ci addr); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci swap_mac_addresses(pkt); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci btx->addr[btx->n_pkts] = brx->addr[j]; 6938c2ecf20Sopenharmony_ci btx->len[btx->n_pkts] = brx->len[j]; 6948c2ecf20Sopenharmony_ci btx->n_pkts++; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (btx->n_pkts == MAX_BURST_TX) { 6978c2ecf20Sopenharmony_ci port_tx_burst(port_tx, btx); 6988c2ecf20Sopenharmony_ci btx->n_pkts = 0; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return NULL; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci/* 7078c2ecf20Sopenharmony_ci * Process 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_cistatic const struct bpool_params bpool_params_default = { 7108c2ecf20Sopenharmony_ci .n_buffers = 64 * 1024, 7118c2ecf20Sopenharmony_ci .buffer_size = XSK_UMEM__DEFAULT_FRAME_SIZE, 7128c2ecf20Sopenharmony_ci .mmap_flags = 0, 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci .n_users_max = 16, 7158c2ecf20Sopenharmony_ci .n_buffers_per_slab = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic const struct xsk_umem_config umem_cfg_default = { 7198c2ecf20Sopenharmony_ci .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, 7208c2ecf20Sopenharmony_ci .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, 7218c2ecf20Sopenharmony_ci .frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE, 7228c2ecf20Sopenharmony_ci .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, 7238c2ecf20Sopenharmony_ci .flags = 0, 7248c2ecf20Sopenharmony_ci}; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic const struct port_params port_params_default = { 7278c2ecf20Sopenharmony_ci .xsk_cfg = { 7288c2ecf20Sopenharmony_ci .rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, 7298c2ecf20Sopenharmony_ci .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, 7308c2ecf20Sopenharmony_ci .libbpf_flags = 0, 7318c2ecf20Sopenharmony_ci .xdp_flags = XDP_FLAGS_DRV_MODE, 7328c2ecf20Sopenharmony_ci .bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY, 7338c2ecf20Sopenharmony_ci }, 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci .bp = NULL, 7368c2ecf20Sopenharmony_ci .iface = NULL, 7378c2ecf20Sopenharmony_ci .iface_queue = 0, 7388c2ecf20Sopenharmony_ci}; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci#ifndef MAX_PORTS 7418c2ecf20Sopenharmony_ci#define MAX_PORTS 64 7428c2ecf20Sopenharmony_ci#endif 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci#ifndef MAX_THREADS 7458c2ecf20Sopenharmony_ci#define MAX_THREADS 64 7468c2ecf20Sopenharmony_ci#endif 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic struct bpool_params bpool_params; 7498c2ecf20Sopenharmony_cistatic struct xsk_umem_config umem_cfg; 7508c2ecf20Sopenharmony_cistatic struct bpool *bp; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic struct port_params port_params[MAX_PORTS]; 7538c2ecf20Sopenharmony_cistatic struct port *ports[MAX_PORTS]; 7548c2ecf20Sopenharmony_cistatic u64 n_pkts_rx[MAX_PORTS]; 7558c2ecf20Sopenharmony_cistatic u64 n_pkts_tx[MAX_PORTS]; 7568c2ecf20Sopenharmony_cistatic int n_ports; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic pthread_t threads[MAX_THREADS]; 7598c2ecf20Sopenharmony_cistatic struct thread_data thread_data[MAX_THREADS]; 7608c2ecf20Sopenharmony_cistatic int n_threads; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void 7638c2ecf20Sopenharmony_ciprint_usage(char *prog_name) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci const char *usage = 7668c2ecf20Sopenharmony_ci "Usage:\n" 7678c2ecf20Sopenharmony_ci "\t%s [ -b SIZE ] -c CORE -i INTERFACE [ -q QUEUE ]\n" 7688c2ecf20Sopenharmony_ci "\n" 7698c2ecf20Sopenharmony_ci "-c CORE CPU core to run a packet forwarding thread\n" 7708c2ecf20Sopenharmony_ci " on. May be invoked multiple times.\n" 7718c2ecf20Sopenharmony_ci "\n" 7728c2ecf20Sopenharmony_ci "-b SIZE Number of buffers in the buffer pool shared\n" 7738c2ecf20Sopenharmony_ci " by all the forwarding threads. Default: %u.\n" 7748c2ecf20Sopenharmony_ci "\n" 7758c2ecf20Sopenharmony_ci "-i INTERFACE Network interface. Each (INTERFACE, QUEUE)\n" 7768c2ecf20Sopenharmony_ci " pair specifies one forwarding port. May be\n" 7778c2ecf20Sopenharmony_ci " invoked multiple times.\n" 7788c2ecf20Sopenharmony_ci "\n" 7798c2ecf20Sopenharmony_ci "-q QUEUE Network interface queue for RX and TX. Each\n" 7808c2ecf20Sopenharmony_ci " (INTERFACE, QUEUE) pair specified one\n" 7818c2ecf20Sopenharmony_ci " forwarding port. Default: %u. May be invoked\n" 7828c2ecf20Sopenharmony_ci " multiple times.\n" 7838c2ecf20Sopenharmony_ci "\n"; 7848c2ecf20Sopenharmony_ci printf(usage, 7858c2ecf20Sopenharmony_ci prog_name, 7868c2ecf20Sopenharmony_ci bpool_params_default.n_buffers, 7878c2ecf20Sopenharmony_ci port_params_default.iface_queue); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int 7918c2ecf20Sopenharmony_ciparse_args(int argc, char **argv) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct option lgopts[] = { 7948c2ecf20Sopenharmony_ci { NULL, 0, 0, 0 } 7958c2ecf20Sopenharmony_ci }; 7968c2ecf20Sopenharmony_ci int opt, option_index; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Parse the input arguments. */ 7998c2ecf20Sopenharmony_ci for ( ; ;) { 8008c2ecf20Sopenharmony_ci opt = getopt_long(argc, argv, "c:i:q:", lgopts, &option_index); 8018c2ecf20Sopenharmony_ci if (opt == EOF) 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci switch (opt) { 8058c2ecf20Sopenharmony_ci case 'b': 8068c2ecf20Sopenharmony_ci bpool_params.n_buffers = atoi(optarg); 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci case 'c': 8108c2ecf20Sopenharmony_ci if (n_threads == MAX_THREADS) { 8118c2ecf20Sopenharmony_ci printf("Max number of threads (%d) reached.\n", 8128c2ecf20Sopenharmony_ci MAX_THREADS); 8138c2ecf20Sopenharmony_ci return -1; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci thread_data[n_threads].cpu_core_id = atoi(optarg); 8178c2ecf20Sopenharmony_ci n_threads++; 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci case 'i': 8218c2ecf20Sopenharmony_ci if (n_ports == MAX_PORTS) { 8228c2ecf20Sopenharmony_ci printf("Max number of ports (%d) reached.\n", 8238c2ecf20Sopenharmony_ci MAX_PORTS); 8248c2ecf20Sopenharmony_ci return -1; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci port_params[n_ports].iface = optarg; 8288c2ecf20Sopenharmony_ci port_params[n_ports].iface_queue = 0; 8298c2ecf20Sopenharmony_ci n_ports++; 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci case 'q': 8338c2ecf20Sopenharmony_ci if (n_ports == 0) { 8348c2ecf20Sopenharmony_ci printf("No port specified for queue.\n"); 8358c2ecf20Sopenharmony_ci return -1; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci port_params[n_ports - 1].iface_queue = atoi(optarg); 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci default: 8418c2ecf20Sopenharmony_ci printf("Illegal argument.\n"); 8428c2ecf20Sopenharmony_ci return -1; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci optind = 1; /* reset getopt lib */ 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Check the input arguments. */ 8498c2ecf20Sopenharmony_ci if (!n_ports) { 8508c2ecf20Sopenharmony_ci printf("No ports specified.\n"); 8518c2ecf20Sopenharmony_ci return -1; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (!n_threads) { 8558c2ecf20Sopenharmony_ci printf("No threads specified.\n"); 8568c2ecf20Sopenharmony_ci return -1; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (n_ports % n_threads) { 8608c2ecf20Sopenharmony_ci printf("Ports cannot be evenly distributed to threads.\n"); 8618c2ecf20Sopenharmony_ci return -1; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistatic void 8688c2ecf20Sopenharmony_ciprint_port(u32 port_id) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct port *port = ports[port_id]; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci printf("Port %u: interface = %s, queue = %u\n", 8738c2ecf20Sopenharmony_ci port_id, port->params.iface, port->params.iface_queue); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic void 8778c2ecf20Sopenharmony_ciprint_thread(u32 thread_id) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct thread_data *t = &thread_data[thread_id]; 8808c2ecf20Sopenharmony_ci u32 i; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci printf("Thread %u (CPU core %u): ", 8838c2ecf20Sopenharmony_ci thread_id, t->cpu_core_id); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci for (i = 0; i < t->n_ports_rx; i++) { 8868c2ecf20Sopenharmony_ci struct port *port_rx = t->ports_rx[i]; 8878c2ecf20Sopenharmony_ci struct port *port_tx = t->ports_tx[i]; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci printf("(%s, %u) -> (%s, %u), ", 8908c2ecf20Sopenharmony_ci port_rx->params.iface, 8918c2ecf20Sopenharmony_ci port_rx->params.iface_queue, 8928c2ecf20Sopenharmony_ci port_tx->params.iface, 8938c2ecf20Sopenharmony_ci port_tx->params.iface_queue); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci printf("\n"); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void 9008c2ecf20Sopenharmony_ciprint_port_stats_separator(void) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci printf("+-%4s-+-%12s-+-%13s-+-%12s-+-%13s-+\n", 9038c2ecf20Sopenharmony_ci "----", 9048c2ecf20Sopenharmony_ci "------------", 9058c2ecf20Sopenharmony_ci "-------------", 9068c2ecf20Sopenharmony_ci "------------", 9078c2ecf20Sopenharmony_ci "-------------"); 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic void 9118c2ecf20Sopenharmony_ciprint_port_stats_header(void) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci print_port_stats_separator(); 9148c2ecf20Sopenharmony_ci printf("| %4s | %12s | %13s | %12s | %13s |\n", 9158c2ecf20Sopenharmony_ci "Port", 9168c2ecf20Sopenharmony_ci "RX packets", 9178c2ecf20Sopenharmony_ci "RX rate (pps)", 9188c2ecf20Sopenharmony_ci "TX packets", 9198c2ecf20Sopenharmony_ci "TX_rate (pps)"); 9208c2ecf20Sopenharmony_ci print_port_stats_separator(); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic void 9248c2ecf20Sopenharmony_ciprint_port_stats_trailer(void) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci print_port_stats_separator(); 9278c2ecf20Sopenharmony_ci printf("\n"); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic void 9318c2ecf20Sopenharmony_ciprint_port_stats(int port_id, u64 ns_diff) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct port *p = ports[port_id]; 9348c2ecf20Sopenharmony_ci double rx_pps, tx_pps; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci rx_pps = (p->n_pkts_rx - n_pkts_rx[port_id]) * 1000000000. / ns_diff; 9378c2ecf20Sopenharmony_ci tx_pps = (p->n_pkts_tx - n_pkts_tx[port_id]) * 1000000000. / ns_diff; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci printf("| %4d | %12llu | %13.0f | %12llu | %13.0f |\n", 9408c2ecf20Sopenharmony_ci port_id, 9418c2ecf20Sopenharmony_ci p->n_pkts_rx, 9428c2ecf20Sopenharmony_ci rx_pps, 9438c2ecf20Sopenharmony_ci p->n_pkts_tx, 9448c2ecf20Sopenharmony_ci tx_pps); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci n_pkts_rx[port_id] = p->n_pkts_rx; 9478c2ecf20Sopenharmony_ci n_pkts_tx[port_id] = p->n_pkts_tx; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic void 9518c2ecf20Sopenharmony_ciprint_port_stats_all(u64 ns_diff) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int i; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci print_port_stats_header(); 9568c2ecf20Sopenharmony_ci for (i = 0; i < n_ports; i++) 9578c2ecf20Sopenharmony_ci print_port_stats(i, ns_diff); 9588c2ecf20Sopenharmony_ci print_port_stats_trailer(); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int quit; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void 9648c2ecf20Sopenharmony_cisignal_handler(int sig) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci quit = 1; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic void remove_xdp_program(void) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci int i; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (i = 0 ; i < n_ports; i++) 9748c2ecf20Sopenharmony_ci bpf_set_link_xdp_fd(if_nametoindex(port_params[i].iface), -1, 9758c2ecf20Sopenharmony_ci port_params[i].xsk_cfg.xdp_flags); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ciint main(int argc, char **argv) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct timespec time; 9818c2ecf20Sopenharmony_ci u64 ns0; 9828c2ecf20Sopenharmony_ci int i; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* Parse args. */ 9858c2ecf20Sopenharmony_ci memcpy(&bpool_params, &bpool_params_default, 9868c2ecf20Sopenharmony_ci sizeof(struct bpool_params)); 9878c2ecf20Sopenharmony_ci memcpy(&umem_cfg, &umem_cfg_default, 9888c2ecf20Sopenharmony_ci sizeof(struct xsk_umem_config)); 9898c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PORTS; i++) 9908c2ecf20Sopenharmony_ci memcpy(&port_params[i], &port_params_default, 9918c2ecf20Sopenharmony_ci sizeof(struct port_params)); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (parse_args(argc, argv)) { 9948c2ecf20Sopenharmony_ci print_usage(argv[0]); 9958c2ecf20Sopenharmony_ci return -1; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* Buffer pool initialization. */ 9998c2ecf20Sopenharmony_ci bp = bpool_init(&bpool_params, &umem_cfg); 10008c2ecf20Sopenharmony_ci if (!bp) { 10018c2ecf20Sopenharmony_ci printf("Buffer pool initialization failed.\n"); 10028c2ecf20Sopenharmony_ci return -1; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci printf("Buffer pool created successfully.\n"); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Ports initialization. */ 10078c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PORTS; i++) 10088c2ecf20Sopenharmony_ci port_params[i].bp = bp; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci for (i = 0; i < n_ports; i++) { 10118c2ecf20Sopenharmony_ci ports[i] = port_init(&port_params[i]); 10128c2ecf20Sopenharmony_ci if (!ports[i]) { 10138c2ecf20Sopenharmony_ci printf("Port %d initialization failed.\n", i); 10148c2ecf20Sopenharmony_ci return -1; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci print_port(i); 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci printf("All ports created successfully.\n"); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* Threads. */ 10218c2ecf20Sopenharmony_ci for (i = 0; i < n_threads; i++) { 10228c2ecf20Sopenharmony_ci struct thread_data *t = &thread_data[i]; 10238c2ecf20Sopenharmony_ci u32 n_ports_per_thread = n_ports / n_threads, j; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci for (j = 0; j < n_ports_per_thread; j++) { 10268c2ecf20Sopenharmony_ci t->ports_rx[j] = ports[i * n_ports_per_thread + j]; 10278c2ecf20Sopenharmony_ci t->ports_tx[j] = ports[i * n_ports_per_thread + 10288c2ecf20Sopenharmony_ci (j + 1) % n_ports_per_thread]; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci t->n_ports_rx = n_ports_per_thread; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci print_thread(i); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci for (i = 0; i < n_threads; i++) { 10378c2ecf20Sopenharmony_ci int status; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci status = pthread_create(&threads[i], 10408c2ecf20Sopenharmony_ci NULL, 10418c2ecf20Sopenharmony_ci thread_func, 10428c2ecf20Sopenharmony_ci &thread_data[i]); 10438c2ecf20Sopenharmony_ci if (status) { 10448c2ecf20Sopenharmony_ci printf("Thread %d creation failed.\n", i); 10458c2ecf20Sopenharmony_ci return -1; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci printf("All threads created successfully.\n"); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* Print statistics. */ 10518c2ecf20Sopenharmony_ci signal(SIGINT, signal_handler); 10528c2ecf20Sopenharmony_ci signal(SIGTERM, signal_handler); 10538c2ecf20Sopenharmony_ci signal(SIGABRT, signal_handler); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 10568c2ecf20Sopenharmony_ci ns0 = time.tv_sec * 1000000000UL + time.tv_nsec; 10578c2ecf20Sopenharmony_ci for ( ; !quit; ) { 10588c2ecf20Sopenharmony_ci u64 ns1, ns_diff; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci sleep(1); 10618c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 10628c2ecf20Sopenharmony_ci ns1 = time.tv_sec * 1000000000UL + time.tv_nsec; 10638c2ecf20Sopenharmony_ci ns_diff = ns1 - ns0; 10648c2ecf20Sopenharmony_ci ns0 = ns1; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci print_port_stats_all(ns_diff); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* Threads completion. */ 10708c2ecf20Sopenharmony_ci printf("Quit.\n"); 10718c2ecf20Sopenharmony_ci for (i = 0; i < n_threads; i++) 10728c2ecf20Sopenharmony_ci thread_data[i].quit = 1; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci for (i = 0; i < n_threads; i++) 10758c2ecf20Sopenharmony_ci pthread_join(threads[i], NULL); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci for (i = 0; i < n_ports; i++) 10788c2ecf20Sopenharmony_ci port_free(ports[i]); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci bpool_free(bp); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci remove_xdp_program(); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci} 1086