diff options
Diffstat (limited to 'tools/testing/selftests/net/toeplitz.c')
| -rw-r--r-- | tools/testing/selftests/net/toeplitz.c | 589 |
1 files changed, 0 insertions, 589 deletions
diff --git a/tools/testing/selftests/net/toeplitz.c b/tools/testing/selftests/net/toeplitz.c deleted file mode 100644 index 9ba03164d73a..000000000000 --- a/tools/testing/selftests/net/toeplitz.c +++ /dev/null @@ -1,589 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Toeplitz test - * - * 1. Read packets and their rx_hash using PF_PACKET/TPACKET_V3 - * 2. Compute the rx_hash in software based on the packet contents - * 3. Compare the two - * - * Optionally, either '-C $rx_irq_cpu_list' or '-r $rps_bitmap' may be given. - * - * If '-C $rx_irq_cpu_list' is given, also - * - * 4. Identify the cpu on which the packet arrived with PACKET_FANOUT_CPU - * 5. Compute the rxqueue that RSS would select based on this rx_hash - * 6. Using the $rx_irq_cpu_list map, identify the arriving cpu based on rxq irq - * 7. Compare the cpus from 4 and 6 - * - * Else if '-r $rps_bitmap' is given, also - * - * 4. Identify the cpu on which the packet arrived with PACKET_FANOUT_CPU - * 5. Compute the cpu that RPS should select based on rx_hash and $rps_bitmap - * 6. Compare the cpus from 4 and 5 - */ - -#define _GNU_SOURCE - -#include <arpa/inet.h> -#include <errno.h> -#include <error.h> -#include <fcntl.h> -#include <getopt.h> -#include <linux/filter.h> -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <net/if.h> -#include <netdb.h> -#include <netinet/ip.h> -#include <netinet/ip6.h> -#include <netinet/tcp.h> -#include <netinet/udp.h> -#include <poll.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/sysinfo.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -#include "../kselftest.h" - -#define TOEPLITZ_KEY_MIN_LEN 40 -#define TOEPLITZ_KEY_MAX_LEN 60 - -#define TOEPLITZ_STR_LEN(K) (((K) * 3) - 1) /* hex encoded: AA:BB:CC:...:ZZ */ -#define TOEPLITZ_STR_MIN_LEN TOEPLITZ_STR_LEN(TOEPLITZ_KEY_MIN_LEN) -#define TOEPLITZ_STR_MAX_LEN TOEPLITZ_STR_LEN(TOEPLITZ_KEY_MAX_LEN) - -#define FOUR_TUPLE_MAX_LEN ((sizeof(struct in6_addr) * 2) + (sizeof(uint16_t) * 2)) - -#define RSS_MAX_CPUS (1 << 16) /* real constraint is PACKET_FANOUT_MAX */ - -#define RPS_MAX_CPUS 16UL /* must be a power of 2 */ - -/* configuration options (cmdline arguments) */ -static uint16_t cfg_dport = 8000; -static int cfg_family = AF_INET6; -static char *cfg_ifname = "eth0"; -static int cfg_num_queues; -static int cfg_num_rps_cpus; -static bool cfg_sink; -static int cfg_type = SOCK_STREAM; -static int cfg_timeout_msec = 1000; -static bool cfg_verbose; - -/* global vars */ -static int num_cpus; -static int ring_block_nr; -static int ring_block_sz; - -/* stats */ -static int frames_received; -static int frames_nohash; -static int frames_error; - -#define log_verbose(args...) do { if (cfg_verbose) fprintf(stderr, args); } while (0) - -/* tpacket ring */ -struct ring_state { - int fd; - char *mmap; - int idx; - int cpu; -}; - -static unsigned int rx_irq_cpus[RSS_MAX_CPUS]; /* map from rxq to cpu */ -static int rps_silo_to_cpu[RPS_MAX_CPUS]; -static unsigned char toeplitz_key[TOEPLITZ_KEY_MAX_LEN]; -static struct ring_state rings[RSS_MAX_CPUS]; - -static inline uint32_t toeplitz(const unsigned char *four_tuple, - const unsigned char *key) -{ - int i, bit, ret = 0; - uint32_t key32; - - key32 = ntohl(*((uint32_t *)key)); - key += 4; - - for (i = 0; i < FOUR_TUPLE_MAX_LEN; i++) { - for (bit = 7; bit >= 0; bit--) { - if (four_tuple[i] & (1 << bit)) - ret ^= key32; - - key32 <<= 1; - key32 |= !!(key[0] & (1 << bit)); - } - key++; - } - - return ret; -} - -/* Compare computed cpu with arrival cpu from packet_fanout_cpu */ -static void verify_rss(uint32_t rx_hash, int cpu) -{ - int queue = rx_hash % cfg_num_queues; - - log_verbose(" rxq %d (cpu %d)", queue, rx_irq_cpus[queue]); - if (rx_irq_cpus[queue] != cpu) { - log_verbose(". error: rss cpu mismatch (%d)", cpu); - frames_error++; - } -} - -static void verify_rps(uint64_t rx_hash, int cpu) -{ - int silo = (rx_hash * cfg_num_rps_cpus) >> 32; - - log_verbose(" silo %d (cpu %d)", silo, rps_silo_to_cpu[silo]); - if (rps_silo_to_cpu[silo] != cpu) { - log_verbose(". error: rps cpu mismatch (%d)", cpu); - frames_error++; - } -} - -static void log_rxhash(int cpu, uint32_t rx_hash, - const char *addrs, int addr_len) -{ - char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN]; - uint16_t *ports; - - if (!inet_ntop(cfg_family, addrs, saddr, sizeof(saddr)) || - !inet_ntop(cfg_family, addrs + addr_len, daddr, sizeof(daddr))) - error(1, 0, "address parse error"); - - ports = (void *)addrs + (addr_len * 2); - log_verbose("cpu %d: rx_hash 0x%08x [saddr %s daddr %s sport %02hu dport %02hu]", - cpu, rx_hash, saddr, daddr, - ntohs(ports[0]), ntohs(ports[1])); -} - -/* Compare computed rxhash with rxhash received from tpacket_v3 */ -static void verify_rxhash(const char *pkt, uint32_t rx_hash, int cpu) -{ - unsigned char four_tuple[FOUR_TUPLE_MAX_LEN] = {0}; - uint32_t rx_hash_sw; - const char *addrs; - int addr_len; - - if (cfg_family == AF_INET) { - addr_len = sizeof(struct in_addr); - addrs = pkt + offsetof(struct iphdr, saddr); - } else { - addr_len = sizeof(struct in6_addr); - addrs = pkt + offsetof(struct ip6_hdr, ip6_src); - } - - memcpy(four_tuple, addrs, (addr_len * 2) + (sizeof(uint16_t) * 2)); - rx_hash_sw = toeplitz(four_tuple, toeplitz_key); - - if (cfg_verbose) - log_rxhash(cpu, rx_hash, addrs, addr_len); - - if (rx_hash != rx_hash_sw) { - log_verbose(" != expected 0x%x\n", rx_hash_sw); - frames_error++; - return; - } - - log_verbose(" OK"); - if (cfg_num_queues) - verify_rss(rx_hash, cpu); - else if (cfg_num_rps_cpus) - verify_rps(rx_hash, cpu); - log_verbose("\n"); -} - -static char *recv_frame(const struct ring_state *ring, char *frame) -{ - struct tpacket3_hdr *hdr = (void *)frame; - - if (hdr->hv1.tp_rxhash) - verify_rxhash(frame + hdr->tp_net, hdr->hv1.tp_rxhash, - ring->cpu); - else - frames_nohash++; - - return frame + hdr->tp_next_offset; -} - -/* A single TPACKET_V3 block can hold multiple frames */ -static bool recv_block(struct ring_state *ring) -{ - struct tpacket_block_desc *block; - char *frame; - int i; - - block = (void *)(ring->mmap + ring->idx * ring_block_sz); - if (!(block->hdr.bh1.block_status & TP_STATUS_USER)) - return false; - - frame = (char *)block; - frame += block->hdr.bh1.offset_to_first_pkt; - - for (i = 0; i < block->hdr.bh1.num_pkts; i++) { - frame = recv_frame(ring, frame); - frames_received++; - } - - block->hdr.bh1.block_status = TP_STATUS_KERNEL; - ring->idx = (ring->idx + 1) % ring_block_nr; - - return true; -} - -/* simple test: sleep once unconditionally and then process all rings */ -static void process_rings(void) -{ - int i; - - usleep(1000 * cfg_timeout_msec); - - for (i = 0; i < num_cpus; i++) - do {} while (recv_block(&rings[i])); - - fprintf(stderr, "count: pass=%u nohash=%u fail=%u\n", - frames_received - frames_nohash - frames_error, - frames_nohash, frames_error); -} - -static char *setup_ring(int fd) -{ - struct tpacket_req3 req3 = {0}; - void *ring; - - req3.tp_retire_blk_tov = cfg_timeout_msec / 8; - req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; - - req3.tp_frame_size = 2048; - req3.tp_frame_nr = 1 << 10; - req3.tp_block_nr = 16; - - req3.tp_block_size = req3.tp_frame_size * req3.tp_frame_nr; - req3.tp_block_size /= req3.tp_block_nr; - - if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req3, sizeof(req3))) - error(1, errno, "setsockopt PACKET_RX_RING"); - - ring_block_sz = req3.tp_block_size; - ring_block_nr = req3.tp_block_nr; - - ring = mmap(0, req3.tp_block_size * req3.tp_block_nr, - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, 0); - if (ring == MAP_FAILED) - error(1, 0, "mmap failed"); - - return ring; -} - -static void __set_filter(int fd, int off_proto, uint8_t proto, int off_dport) -{ - struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PACKET_HOST, 0, 4), - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, off_proto), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, proto, 0, 2), - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, off_dport), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_dport, 1, 0), - BPF_STMT(BPF_RET + BPF_K, 0), - BPF_STMT(BPF_RET + BPF_K, 0xFFFF), - }; - struct sock_fprog prog = {}; - - prog.filter = filter; - prog.len = ARRAY_SIZE(filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) - error(1, errno, "setsockopt filter"); -} - -/* filter on transport protocol and destination port */ -static void set_filter(int fd) -{ - const int off_dport = offsetof(struct tcphdr, dest); /* same for udp */ - uint8_t proto; - - proto = cfg_type == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP; - if (cfg_family == AF_INET) - __set_filter(fd, offsetof(struct iphdr, protocol), proto, - sizeof(struct iphdr) + off_dport); - else - __set_filter(fd, offsetof(struct ip6_hdr, ip6_nxt), proto, - sizeof(struct ip6_hdr) + off_dport); -} - -/* drop everything: used temporarily during setup */ -static void set_filter_null(int fd) -{ - struct sock_filter filter[] = { - BPF_STMT(BPF_RET + BPF_K, 0), - }; - struct sock_fprog prog = {}; - - prog.filter = filter; - prog.len = ARRAY_SIZE(filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) - error(1, errno, "setsockopt filter"); -} - -static int create_ring(char **ring) -{ - struct fanout_args args = { - .id = 1, - .type_flags = PACKET_FANOUT_CPU, - .max_num_members = RSS_MAX_CPUS - }; - struct sockaddr_ll ll = { 0 }; - int fd, val; - - fd = socket(PF_PACKET, SOCK_DGRAM, 0); - if (fd == -1) - error(1, errno, "socket creation failed"); - - val = TPACKET_V3; - if (setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val))) - error(1, errno, "setsockopt PACKET_VERSION"); - *ring = setup_ring(fd); - - /* block packets until all rings are added to the fanout group: - * else packets can arrive during setup and get misclassified - */ - set_filter_null(fd); - - ll.sll_family = AF_PACKET; - ll.sll_ifindex = if_nametoindex(cfg_ifname); - ll.sll_protocol = cfg_family == AF_INET ? htons(ETH_P_IP) : - htons(ETH_P_IPV6); - if (bind(fd, (void *)&ll, sizeof(ll))) - error(1, errno, "bind"); - - /* must come after bind: verifies all programs in group match */ - if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &args, sizeof(args))) { - /* on failure, retry using old API if that is sufficient: - * it has a hard limit of 256 sockets, so only try if - * (a) only testing rxhash, not RSS or (b) <= 256 cpus. - * in this API, the third argument is left implicit. - */ - if (cfg_num_queues || num_cpus > 256 || - setsockopt(fd, SOL_PACKET, PACKET_FANOUT, - &args, sizeof(uint32_t))) - error(1, errno, "setsockopt PACKET_FANOUT cpu"); - } - - return fd; -} - -/* setup inet(6) socket to blackhole the test traffic, if arg '-s' */ -static int setup_sink(void) -{ - int fd, val; - - fd = socket(cfg_family, cfg_type, 0); - if (fd == -1) - error(1, errno, "socket %d.%d", cfg_family, cfg_type); - - val = 1 << 20; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &val, sizeof(val))) - error(1, errno, "setsockopt rcvbuf"); - - return fd; -} - -static void setup_rings(void) -{ - int i; - - for (i = 0; i < num_cpus; i++) { - rings[i].cpu = i; - rings[i].fd = create_ring(&rings[i].mmap); - } - - /* accept packets once all rings in the fanout group are up */ - for (i = 0; i < num_cpus; i++) - set_filter(rings[i].fd); -} - -static void cleanup_rings(void) -{ - int i; - - for (i = 0; i < num_cpus; i++) { - if (munmap(rings[i].mmap, ring_block_nr * ring_block_sz)) - error(1, errno, "munmap"); - if (close(rings[i].fd)) - error(1, errno, "close"); - } -} - -static void parse_cpulist(const char *arg) -{ - do { - rx_irq_cpus[cfg_num_queues++] = strtol(arg, NULL, 10); - - arg = strchr(arg, ','); - if (!arg) - break; - arg++; // skip ',' - } while (1); -} - -static void show_cpulist(void) -{ - int i; - - for (i = 0; i < cfg_num_queues; i++) - fprintf(stderr, "rxq %d: cpu %d\n", i, rx_irq_cpus[i]); -} - -static void show_silos(void) -{ - int i; - - for (i = 0; i < cfg_num_rps_cpus; i++) - fprintf(stderr, "silo %d: cpu %d\n", i, rps_silo_to_cpu[i]); -} - -static void parse_toeplitz_key(const char *str, int slen, unsigned char *key) -{ - int i, ret, off; - - if (slen < TOEPLITZ_STR_MIN_LEN || - slen > TOEPLITZ_STR_MAX_LEN + 1) - error(1, 0, "invalid toeplitz key"); - - for (i = 0, off = 0; off < slen; i++, off += 3) { - ret = sscanf(str + off, "%hhx", &key[i]); - if (ret != 1) - error(1, 0, "key parse error at %d off %d len %d", - i, off, slen); - } -} - -static void parse_rps_bitmap(const char *arg) -{ - unsigned long bitmap; - int i; - - bitmap = strtoul(arg, NULL, 0); - - if (bitmap & ~(RPS_MAX_CPUS - 1)) - error(1, 0, "rps bitmap 0x%lx out of bounds 0..%lu", - bitmap, RPS_MAX_CPUS - 1); - - for (i = 0; i < RPS_MAX_CPUS; i++) - if (bitmap & 1UL << i) - rps_silo_to_cpu[cfg_num_rps_cpus++] = i; -} - -static void parse_opts(int argc, char **argv) -{ - static struct option long_options[] = { - {"dport", required_argument, 0, 'd'}, - {"cpus", required_argument, 0, 'C'}, - {"key", required_argument, 0, 'k'}, - {"iface", required_argument, 0, 'i'}, - {"ipv4", no_argument, 0, '4'}, - {"ipv6", no_argument, 0, '6'}, - {"sink", no_argument, 0, 's'}, - {"tcp", no_argument, 0, 't'}, - {"timeout", required_argument, 0, 'T'}, - {"udp", no_argument, 0, 'u'}, - {"verbose", no_argument, 0, 'v'}, - {"rps", required_argument, 0, 'r'}, - {0, 0, 0, 0} - }; - bool have_toeplitz = false; - int index, c; - - while ((c = getopt_long(argc, argv, "46C:d:i:k:r:stT:uv", long_options, &index)) != -1) { - switch (c) { - case '4': - cfg_family = AF_INET; - break; - case '6': - cfg_family = AF_INET6; - break; - case 'C': - parse_cpulist(optarg); - break; - case 'd': - cfg_dport = strtol(optarg, NULL, 0); - break; - case 'i': - cfg_ifname = optarg; - break; - case 'k': - parse_toeplitz_key(optarg, strlen(optarg), - toeplitz_key); - have_toeplitz = true; - break; - case 'r': - parse_rps_bitmap(optarg); - break; - case 's': - cfg_sink = true; - break; - case 't': - cfg_type = SOCK_STREAM; - break; - case 'T': - cfg_timeout_msec = strtol(optarg, NULL, 0); - break; - case 'u': - cfg_type = SOCK_DGRAM; - break; - case 'v': - cfg_verbose = true; - break; - - default: - error(1, 0, "unknown option %c", optopt); - break; - } - } - - if (!have_toeplitz) - error(1, 0, "Must supply rss key ('-k')"); - - num_cpus = get_nprocs(); - if (num_cpus > RSS_MAX_CPUS) - error(1, 0, "increase RSS_MAX_CPUS"); - - if (cfg_num_queues && cfg_num_rps_cpus) - error(1, 0, - "Can't supply both RSS cpus ('-C') and RPS map ('-r')"); - if (cfg_verbose) { - show_cpulist(); - show_silos(); - } -} - -int main(int argc, char **argv) -{ - const int min_tests = 10; - int fd_sink = -1; - - parse_opts(argc, argv); - - if (cfg_sink) - fd_sink = setup_sink(); - - setup_rings(); - process_rings(); - cleanup_rings(); - - if (cfg_sink && close(fd_sink)) - error(1, errno, "close sink"); - - if (frames_received - frames_nohash < min_tests) - error(1, 0, "too few frames for verification"); - - return frames_error; -} |
