From b0aa73bf081da6810dacd750b9f8186640e172db Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 14:49:44 -0400 Subject: net: Add socket() system call self test. Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-socket/Makefile | 16 ++++ .../testing/selftests/net-socket/run_netsocktests | 12 +++ tools/testing/selftests/net-socket/socket.c | 92 ++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 tools/testing/selftests/net-socket/Makefile create mode 100644 tools/testing/selftests/net-socket/run_netsocktests create mode 100644 tools/testing/selftests/net-socket/socket.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3cc0ad7ae86..7c6280f1cf4 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,6 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs +TARGETS += net-socket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile new file mode 100644 index 00000000000..f27ee10da53 --- /dev/null +++ b/tools/testing/selftests/net-socket/Makefile @@ -0,0 +1,16 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +NET_SOCK_PROGS = socket + +all: $(NET_SOCK_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + +clean: + $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests new file mode 100644 index 00000000000..c09a682df56 --- /dev/null +++ b/tools/testing/selftests/net-socket/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c new file mode 100644 index 00000000000..0f227f2f9be --- /dev/null +++ b/tools/testing/selftests/net-socket/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3-70-g09d2 From 77f65ebdca506870d99bfabe52bde222511022ec Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 10:18:11 +0000 Subject: packet: packet fanout rollover during socket overload Changes: v3->v2: rebase (no other changes) passes selftest v2->v1: read f->num_members only once fix bug: test rollover mode + flag Minimize packet drop in a fanout group. If one socket is full, roll over packets to another from the group. Maintain flow affinity during normal load using an rxhash fanout policy, while dispersing unexpected traffic storms that hit a single cpu, such as spoofed-source DoS flows. Rollover breaks affinity for flows arriving at saturated sockets during those conditions. The patch adds a fanout policy ROLLOVER that rotates between sockets, filling each socket before moving to the next. It also adds a fanout flag ROLLOVER. If passed along with any other fanout policy, the primary policy is applied until the chosen socket is full. Then, rollover selects another socket, to delay packet drop until the entire system is saturated. Probing sockets is not free. Selecting the last used socket, as rollover does, is a greedy approach that maximizes chance of success, at the cost of extreme load imbalance. In practice, with sufficiently long queues to absorb bursts, sockets are drained in parallel and load balance looks uniform in `top`. To avoid contention, scales counters with number of sockets and accesses them lockfree. Values are bounds checked to ensure correctness. Tested using an application with 9 threads pinned to CPUs, one socket per thread and sufficient busywork per packet operation to limits each thread to handling 32 Kpps. When sent 500 Kpps single UDP stream packets, a FANOUT_CPU setup processes 32 Kpps in total without this patch, 270 Kpps with the patch. Tested with read() and with a packet ring (V1). Also, passes psock_fanout.c unit test added to selftests. Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/if_packet.h | 2 + net/packet/af_packet.c | 109 +++++-- net/packet/internal.h | 3 +- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-afpacket/Makefile | 18 ++ .../testing/selftests/net-afpacket/psock_fanout.c | 326 +++++++++++++++++++++ .../selftests/net-afpacket/run_afpackettests | 16 + 7 files changed, 451 insertions(+), 24 deletions(-) create mode 100644 tools/testing/selftests/net-afpacket/Makefile create mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c create mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests (limited to 'tools/testing') diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index f9a60375f0d..8136658ea47 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -55,6 +55,8 @@ struct sockaddr_ll { #define PACKET_FANOUT_HASH 0 #define PACKET_FANOUT_LB 1 #define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_ROLLOVER 3 +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1d6793dbfba..bd0d14c97d4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -181,6 +181,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, struct packet_sock; static int tpacket_snd(struct packet_sock *po, struct msghdr *msg); +static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); static void *packet_previous_frame(struct packet_sock *po, struct packet_ring_buffer *rb, @@ -973,11 +975,11 @@ static void *packet_current_rx_frame(struct packet_sock *po, static void *prb_lookup_block(struct packet_sock *po, struct packet_ring_buffer *rb, - unsigned int previous, + unsigned int idx, int status) { struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(rb); - struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, previous); + struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, idx); if (status != BLOCK_STATUS(pbd)) return NULL; @@ -1041,6 +1043,29 @@ static void packet_increment_head(struct packet_ring_buffer *buff) buff->head = buff->head != buff->frame_max ? buff->head+1 : 0; } +static bool packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb) +{ + struct sock *sk = &po->sk; + bool has_room; + + if (po->prot_hook.func != tpacket_rcv) + return (atomic_read(&sk->sk_rmem_alloc) + skb->truesize) + <= sk->sk_rcvbuf; + + spin_lock(&sk->sk_receive_queue.lock); + if (po->tp_version == TPACKET_V3) + has_room = prb_lookup_block(po, &po->rx_ring, + po->rx_ring.prb_bdqc.kactive_blk_num, + TP_STATUS_KERNEL); + else + has_room = packet_lookup_frame(po, &po->rx_ring, + po->rx_ring.head, + TP_STATUS_KERNEL); + spin_unlock(&sk->sk_receive_queue.lock); + + return has_room; +} + static void packet_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_error_queue); @@ -1066,16 +1091,16 @@ static int fanout_rr_next(struct packet_fanout *f, unsigned int num) return x; } -static struct sock *fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_hash(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) { - u32 idx, hash = skb->rxhash; - - idx = ((u64)hash * num) >> 32; - - return f->arr[idx]; + return (((u64)skb->rxhash) * num) >> 32; } -static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_lb(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) { int cur, old; @@ -1083,14 +1108,40 @@ static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb while ((old = atomic_cmpxchg(&f->rr_cur, cur, fanout_rr_next(f, num))) != cur) cur = old; - return f->arr[cur]; + return cur; +} + +static unsigned int fanout_demux_cpu(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) +{ + return smp_processor_id() % num; } -static struct sock *fanout_demux_cpu(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_rollover(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int idx, unsigned int skip, + unsigned int num) { - unsigned int cpu = smp_processor_id(); + unsigned int i, j; - return f->arr[cpu % num]; + i = j = min_t(int, f->next[idx], num - 1); + do { + if (i != skip && packet_rcv_has_room(pkt_sk(f->arr[i]), skb)) { + if (i != j) + f->next[idx] = i; + return i; + } + if (++i == num) + i = 0; + } while (i != j); + + return idx; +} + +static bool fanout_has_flag(struct packet_fanout *f, u16 flag) +{ + return f->flags & (flag >> 8); } static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, @@ -1099,7 +1150,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, struct packet_fanout *f = pt->af_packet_priv; unsigned int num = f->num_members; struct packet_sock *po; - struct sock *sk; + unsigned int idx; if (!net_eq(dev_net(dev), read_pnet(&f->net)) || !num) { @@ -1110,23 +1161,31 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, switch (f->type) { case PACKET_FANOUT_HASH: default: - if (f->defrag) { + if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) { skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET); if (!skb) return 0; } skb_get_rxhash(skb); - sk = fanout_demux_hash(f, skb, num); + idx = fanout_demux_hash(f, skb, num); break; case PACKET_FANOUT_LB: - sk = fanout_demux_lb(f, skb, num); + idx = fanout_demux_lb(f, skb, num); break; case PACKET_FANOUT_CPU: - sk = fanout_demux_cpu(f, skb, num); + idx = fanout_demux_cpu(f, skb, num); + break; + case PACKET_FANOUT_ROLLOVER: + idx = fanout_demux_rollover(f, skb, 0, (unsigned int) -1, num); break; } - po = pkt_sk(sk); + po = pkt_sk(f->arr[idx]); + if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER) && + unlikely(!packet_rcv_has_room(po, skb))) { + idx = fanout_demux_rollover(f, skb, idx, idx, num); + po = pkt_sk(f->arr[idx]); + } return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev); } @@ -1175,10 +1234,13 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) struct packet_sock *po = pkt_sk(sk); struct packet_fanout *f, *match; u8 type = type_flags & 0xff; - u8 defrag = (type_flags & PACKET_FANOUT_FLAG_DEFRAG) ? 1 : 0; + u8 flags = type_flags >> 8; int err; switch (type) { + case PACKET_FANOUT_ROLLOVER: + if (type_flags & PACKET_FANOUT_FLAG_ROLLOVER) + return -EINVAL; case PACKET_FANOUT_HASH: case PACKET_FANOUT_LB: case PACKET_FANOUT_CPU: @@ -1203,7 +1265,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) } } err = -EINVAL; - if (match && match->defrag != defrag) + if (match && match->flags != flags) goto out; if (!match) { err = -ENOMEM; @@ -1213,7 +1275,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) write_pnet(&match->net, sock_net(sk)); match->id = id; match->type = type; - match->defrag = defrag; + match->flags = flags; atomic_set(&match->rr_cur, 0); INIT_LIST_HEAD(&match->list); spin_lock_init(&match->lock); @@ -3240,7 +3302,8 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, case PACKET_FANOUT: val = (po->fanout ? ((u32)po->fanout->id | - ((u32)po->fanout->type << 16)) : + ((u32)po->fanout->type << 16) | + ((u32)po->fanout->flags << 24)) : 0); break; case PACKET_TX_HAS_OFF: diff --git a/net/packet/internal.h b/net/packet/internal.h index e84cab8cb7a..e891f025a1b 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -77,10 +77,11 @@ struct packet_fanout { unsigned int num_members; u16 id; u8 type; - u8 defrag; + u8 flags; atomic_t rr_cur; struct list_head list; struct sock *arr[PACKET_FANOUT_MAX]; + int next[PACKET_FANOUT_MAX]; spinlock_t lock; atomic_t sk_ref; struct packet_type prot_hook ____cacheline_aligned_in_smp; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7c6280f1cf4..7f50078d0e8 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs TARGETS += net-socket +TARGETS += net-afpacket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile new file mode 100644 index 00000000000..45f2ffb7fda --- /dev/null +++ b/tools/testing/selftests/net-afpacket/Makefile @@ -0,0 +1,18 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +AF_PACKET_PROGS = psock_fanout + +all: $(AF_PACKET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c new file mode 100644 index 00000000000..09dbf93c53d --- /dev/null +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -0,0 +1,326 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - datapath: PACKET_FANOUT_LB + * - datapath: PACKET_FANOUT_CPU + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hack: build even if local includes are old */ +#ifndef PACKET_FANOUT +#define PACKET_FANOUT 18 +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +#ifndef PACKET_FANOUT_ROLLOVER +#define PACKET_FANOUT_ROLLOVER 3 +#endif + +#ifndef PACKET_FANOUT_FLAG_ROLLOVER +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#endif + +#endif + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; + val *= num_packets; + /* hack: apparently, the above calculation is too small (TODO: fix) */ + val *= 3; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { + perror("setsockopt SO_RCVBUF"); + exit(1); + } + + sock_fanout_setfilter(fd); + return fd; +} + +static void sock_fanout_read(int fds[], const int expect[]) +{ + struct tpacket_stats stats; + socklen_t ssize; + int ret[2]; + + ssize = sizeof(stats); + if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 0"); + exit(1); + } + ret[0] = stats.tp_packets - stats.tp_drops; + ssize = sizeof(stats); + if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 1"); + exit(1); + } + ret[1] = stats.tp_packets - stats.tp_drops; + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + exit(1); + } +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static void test_datapath(uint16_t typeflags, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + int fds[2], fds_udp[2][2]; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + pair_udp_open(fds_udp[0], 8000); + pair_udp_open(fds_udp[1], 8002); + sock_fanout_read(fds, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + sock_fanout_read(fds, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + sock_fanout_read(fds, expect2); + + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + + test_control_single(); + test_control_group(); + + test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); + test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + expect_hash_rb[0], expect_hash_rb[1]); + test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests new file mode 100644 index 00000000000..7907824c635 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3-70-g09d2 From 947124460d0c47d465b5846946d3f2b5cee6026e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 17:05:50 -0400 Subject: net: Fix failure string in net-socket selftests Makefile. Signed-off-by: David S. Miller --- tools/testing/selftests/net-socket/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile index f27ee10da53..2450fd8bb10 100644 --- a/tools/testing/selftests/net-socket/Makefile +++ b/tools/testing/selftests/net-socket/Makefile @@ -10,7 +10,7 @@ all: $(NET_SOCK_PROGS) $(CC) $(CFLAGS) -o $@ $^ run_tests: all - @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" clean: $(RM) $(NET_SOCK_PROGS) -- cgit v1.2.3-70-g09d2 From b44540ea024b9280c9be9d055f084e0956bcfa44 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 18:08:45 -0400 Subject: net: Get rid of compat defines in psock_fanout.c selftest. Reported-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index 09dbf93c53d..af9b0196b0b 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -56,24 +56,6 @@ #include #include -/* Hack: build even if local includes are old */ -#ifndef PACKET_FANOUT -#define PACKET_FANOUT 18 -#define PACKET_FANOUT_HASH 0 -#define PACKET_FANOUT_LB 1 -#define PACKET_FANOUT_CPU 2 -#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 - -#ifndef PACKET_FANOUT_ROLLOVER -#define PACKET_FANOUT_ROLLOVER 3 -#endif - -#ifndef PACKET_FANOUT_FLAG_ROLLOVER -#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 -#endif - -#endif - #define DATA_LEN 100 #define DATA_CHAR 'a' -- cgit v1.2.3-70-g09d2 From 23a9072e3af0d9538e25837fb2b56bb94e4a8e67 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 20:42:44 +0000 Subject: net: fix psock_fanout selftest hash collision Fix flaky results with PACKET_FANOUT_HASH depending on whether the two flows hash into the same packet socket or not. Also adds tests for PACKET_FANOUT_LB and PACKET_FANOUT_CPU and replaces the counting method with a packet ring. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- .../testing/selftests/net-afpacket/psock_fanout.c | 160 +++++++++++++++------ 1 file changed, 120 insertions(+), 40 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index af9b0196b0b..f765f09931b 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -16,11 +16,11 @@ * The test currently runs for * - PACKET_FANOUT_HASH * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU * - PACKET_FANOUT_ROLLOVER * * Todo: - * - datapath: PACKET_FANOUT_LB - * - datapath: PACKET_FANOUT_CPU * - functionality: PACKET_FANOUT_FLAG_DEFRAG * * License (GPLv2): @@ -39,18 +39,23 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE /* for sched_setaffinity */ + #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -58,6 +63,8 @@ #define DATA_LEN 100 #define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 static void pair_udp_open(int fds[], uint16_t port) { @@ -162,37 +169,55 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) return -1; } - val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; - val *= num_packets; - /* hack: apparently, the above calculation is too small (TODO: fix) */ - val *= 3; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { - perror("setsockopt SO_RCVBUF"); - exit(1); - } - sock_fanout_setfilter(fd); return fd; } -static void sock_fanout_read(int fds[], const int expect[]) +static char *sock_fanout_open_ring(int fd) { - struct tpacket_stats stats; - socklen_t ssize; - int ret[2]; + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; - ssize = sizeof(stats); - if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 0"); + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); exit(1); } - ret[0] = stats.tp_packets - stats.tp_drops; - ssize = sizeof(stats); - if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 1"); + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); exit(1); } - ret[1] = stats.tp_packets - stats.tp_drops; + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", ret[0], ret[1], expect[0], expect[1]); @@ -200,8 +225,10 @@ static void sock_fanout_read(int fds[], const int expect[]) if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && (!(ret[0] == expect[1] && ret[1] == expect[0]))) { fprintf(stderr, "ERROR: incorrect queue lengths\n"); - exit(1); + return 1; } + + return 0; } /* Test illegal mode + flag combination */ @@ -253,11 +280,12 @@ static void test_control_group(void) } } -static void test_datapath(uint16_t typeflags, - const int expect1[], const int expect2[]) +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) { const int expect0[] = { 0, 0 }; - int fds[2], fds_udp[2][2]; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; fprintf(stderr, "test: datapath 0x%hx\n", typeflags); @@ -267,41 +295,93 @@ static void test_datapath(uint16_t typeflags, fprintf(stderr, "ERROR: failed open\n"); exit(1); } - pair_udp_open(fds_udp[0], 8000); - pair_udp_open(fds_udp[1], 8002); - sock_fanout_read(fds, expect0); + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); /* Send data, but not enough to overflow a queue */ pair_udp_send(fds_udp[0], 15); pair_udp_send(fds_udp[1], 5); - sock_fanout_read(fds, expect1); + ret = sock_fanout_read(fds, rings, expect1); /* Send more data, overflow the queue */ pair_udp_send(fds_udp[0], 15); /* TODO: ensure consistent order between expect1 and expect2 */ - sock_fanout_read(fds, expect2); + ret |= sock_fanout_read(fds, rings, expect2); + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || close(fds_udp[0][1]) || close(fds_udp[0][0]) || close(fds[1]) || close(fds[0])) { fprintf(stderr, "close datapath\n"); exit(1); } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; } int main(int argc, char **argv) { - const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; test_control_single(); test_control_group(); - test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); - test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - expect_hash_rb[0], expect_hash_rb[1]); - test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; printf("OK. All tests passed\n"); return 0; -- cgit v1.2.3-70-g09d2 From 4c1d8d0617a39c8325a7c2fd80ac14bf40fd8cc6 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Wed, 20 Mar 2013 19:28:56 +0200 Subject: net: fix psock_fanout selftest bind error message Signed-off-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index f765f09931b..226e5e33105 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -97,7 +97,7 @@ static void pair_udp_open(int fds[], uint16_t port) exit(1); } if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("bind"); + perror("connect"); exit(1); } } -- cgit v1.2.3-70-g09d2 From a6f68034de8a5784dfeabb337506254c80b4c8c6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 20 Mar 2013 15:07:56 -0400 Subject: net: Move selftests to common net/ subdirectory. Suggested-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 3 +- tools/testing/selftests/net-afpacket/Makefile | 18 - .../testing/selftests/net-afpacket/psock_fanout.c | 388 --------------------- .../selftests/net-afpacket/run_afpackettests | 16 - tools/testing/selftests/net-socket/Makefile | 16 - .../testing/selftests/net-socket/run_netsocktests | 12 - tools/testing/selftests/net-socket/socket.c | 92 ----- tools/testing/selftests/net/Makefile | 19 + tools/testing/selftests/net/psock_fanout.c | 388 +++++++++++++++++++++ tools/testing/selftests/net/run_afpackettests | 16 + tools/testing/selftests/net/run_netsocktests | 12 + tools/testing/selftests/net/socket.c | 92 +++++ 12 files changed, 528 insertions(+), 544 deletions(-) delete mode 100644 tools/testing/selftests/net-afpacket/Makefile delete mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c delete mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests delete mode 100644 tools/testing/selftests/net-socket/Makefile delete mode 100644 tools/testing/selftests/net-socket/run_netsocktests delete mode 100644 tools/testing/selftests/net-socket/socket.c create mode 100644 tools/testing/selftests/net/Makefile create mode 100644 tools/testing/selftests/net/psock_fanout.c create mode 100644 tools/testing/selftests/net/run_afpackettests create mode 100644 tools/testing/selftests/net/run_netsocktests create mode 100644 tools/testing/selftests/net/socket.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7f50078d0e8..a4805932972 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,8 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs -TARGETS += net-socket -TARGETS += net-afpacket +TARGETS += net all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile deleted file mode 100644 index 45f2ffb7fda..00000000000 --- a/tools/testing/selftests/net-afpacket/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -CFLAGS += -I../../../../usr/include/ - -AF_PACKET_PROGS = psock_fanout - -all: $(AF_PACKET_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" - -clean: - $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c deleted file mode 100644 index 226e5e33105..00000000000 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Author: Willem de Bruijn (willemb@google.com) - * - * A basic test of packet socket fanout behavior. - * - * Control: - * - create fanout fails as expected with illegal flag combinations - * - join fanout fails as expected with diverging types or flags - * - * Datapath: - * Open a pair of packet sockets and a pair of INET sockets, send a known - * number of packets across the two INET sockets and count the number of - * packets enqueued onto the two packet sockets. - * - * The test currently runs for - * - PACKET_FANOUT_HASH - * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER - * - PACKET_FANOUT_LB - * - PACKET_FANOUT_CPU - * - PACKET_FANOUT_ROLLOVER - * - * Todo: - * - functionality: PACKET_FANOUT_FLAG_DEFRAG - * - * License (GPLv2): - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE /* for sched_setaffinity */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DATA_LEN 100 -#define DATA_CHAR 'a' -#define RING_NUM_FRAMES 20 -#define PORT_BASE 8000 - -static void pair_udp_open(int fds[], uint16_t port) -{ - struct sockaddr_in saddr, daddr; - - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: socket dgram\n"); - exit(1); - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&daddr, 0, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_port = htons(port + 1); - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - /* must bind both to get consistent hash result */ - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { - perror("bind"); - exit(1); - } - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { - perror("bind"); - exit(1); - } - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("connect"); - exit(1); - } -} - -static void pair_udp_send(int fds[], int num) -{ - char buf[DATA_LEN], rbuf[DATA_LEN]; - - memset(buf, DATA_CHAR, sizeof(buf)); - while (num--) { - /* Should really handle EINTR and EAGAIN */ - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { - fprintf(stderr, "ERROR: send failed left=%d\n", num); - exit(1); - } - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { - fprintf(stderr, "ERROR: recv failed left=%d\n", num); - exit(1); - } - if (memcmp(buf, rbuf, sizeof(buf))) { - fprintf(stderr, "ERROR: data failed left=%d\n", num); - exit(1); - } - } -} - -static void sock_fanout_setfilter(int fd) -{ - struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x6, 0, 0, 0x00000060 }, /* RET match */ -/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ - }; - struct sock_fprog bpf_prog; - - bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, - sizeof(bpf_prog))) { - perror("setsockopt SO_ATTACH_FILTER"); - exit(1); - } -} - -/* Open a socket in a given fanout mode. - * @return -1 if mode is bad, a valid socket otherwise */ -static int sock_fanout_open(uint16_t typeflags, int num_packets) -{ - int fd, val; - - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); - if (fd < 0) { - perror("socket packet"); - exit(1); - } - - /* fanout group ID is always 0: tests whether old groups are deleted */ - val = ((int) typeflags) << 16; - if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { - if (close(fd)) { - perror("close packet"); - exit(1); - } - return -1; - } - - sock_fanout_setfilter(fd); - return fd; -} - -static char *sock_fanout_open_ring(int fd) -{ - struct tpacket_req req = { - .tp_block_size = getpagesize(), - .tp_frame_size = getpagesize(), - .tp_block_nr = RING_NUM_FRAMES, - .tp_frame_nr = RING_NUM_FRAMES, - }; - char *ring; - - if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, - sizeof(req))) { - perror("packetsock ring setsockopt"); - exit(1); - } - - ring = mmap(0, req.tp_block_size * req.tp_block_nr, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (!ring) { - fprintf(stderr, "packetsock ring mmap\n"); - exit(1); - } - - return ring; -} - -static int sock_fanout_read_ring(int fd, void *ring) -{ - struct tpacket_hdr *header = ring; - int count = 0; - - while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { - count++; - header = ring + (count * getpagesize()); - } - - return count; -} - -static int sock_fanout_read(int fds[], char *rings[], const int expect[]) -{ - int ret[2]; - - ret[0] = sock_fanout_read_ring(fds[0], rings[0]); - ret[1] = sock_fanout_read_ring(fds[1], rings[1]); - - fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", - ret[0], ret[1], expect[0], expect[1]); - - if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && - (!(ret[0] == expect[1] && ret[1] == expect[0]))) { - fprintf(stderr, "ERROR: incorrect queue lengths\n"); - return 1; - } - - return 0; -} - -/* Test illegal mode + flag combination */ -static void test_control_single(void) -{ - fprintf(stderr, "test: control single socket\n"); - - if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | - PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { - fprintf(stderr, "ERROR: opened socket with dual rollover\n"); - exit(1); - } -} - -/* Test illegal group with different modes or flags */ -static void test_control_group(void) -{ - int fds[2]; - - fprintf(stderr, "test: control multiple sockets\n"); - - fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[0] == -1) { - fprintf(stderr, "ERROR: failed to open HASH socket\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong mode\n"); - exit(1); - } - fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[1] == -1) { - fprintf(stderr, "ERROR: failed to join group\n"); - exit(1); - } - if (close(fds[1]) || close(fds[0])) { - fprintf(stderr, "ERROR: closing sockets\n"); - exit(1); - } -} - -static int test_datapath(uint16_t typeflags, int port_off, - const int expect1[], const int expect2[]) -{ - const int expect0[] = { 0, 0 }; - char *rings[2]; - int fds[2], fds_udp[2][2], ret; - - fprintf(stderr, "test: datapath 0x%hx\n", typeflags); - - fds[0] = sock_fanout_open(typeflags, 20); - fds[1] = sock_fanout_open(typeflags, 20); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: failed open\n"); - exit(1); - } - rings[0] = sock_fanout_open_ring(fds[0]); - rings[1] = sock_fanout_open_ring(fds[1]); - pair_udp_open(fds_udp[0], PORT_BASE); - pair_udp_open(fds_udp[1], PORT_BASE + port_off); - sock_fanout_read(fds, rings, expect0); - - /* Send data, but not enough to overflow a queue */ - pair_udp_send(fds_udp[0], 15); - pair_udp_send(fds_udp[1], 5); - ret = sock_fanout_read(fds, rings, expect1); - - /* Send more data, overflow the queue */ - pair_udp_send(fds_udp[0], 15); - /* TODO: ensure consistent order between expect1 and expect2 */ - ret |= sock_fanout_read(fds, rings, expect2); - - if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || - munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { - fprintf(stderr, "close rings\n"); - exit(1); - } - if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || - close(fds_udp[0][1]) || close(fds_udp[0][0]) || - close(fds[1]) || close(fds[0])) { - fprintf(stderr, "close datapath\n"); - exit(1); - } - - return ret; -} - -static int set_cpuaffinity(int cpuid) -{ - cpu_set_t mask; - - CPU_ZERO(&mask); - CPU_SET(cpuid, &mask); - if (sched_setaffinity(0, sizeof(mask), &mask)) { - if (errno != EINVAL) { - fprintf(stderr, "setaffinity %d\n", cpuid); - exit(1); - } - return 1; - } - - return 0; -} - -int main(int argc, char **argv) -{ - const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; - const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; - const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; - const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; - int port_off = 2, tries = 5, ret; - - test_control_single(); - test_control_group(); - - /* find a set of ports that do not collide onto the same socket */ - ret = test_datapath(PACKET_FANOUT_HASH, port_off, - expect_hash[0], expect_hash[1]); - while (ret && tries--) { - fprintf(stderr, "info: trying alternate ports (%d)\n", tries); - ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, - expect_hash[0], expect_hash[1]); - } - - ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - port_off, expect_hash_rb[0], expect_hash_rb[1]); - ret |= test_datapath(PACKET_FANOUT_LB, - port_off, expect_lb[0], expect_lb[1]); - ret |= test_datapath(PACKET_FANOUT_ROLLOVER, - port_off, expect_rb[0], expect_rb[1]); - - set_cpuaffinity(0); - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu0[0], expect_cpu0[1]); - if (!set_cpuaffinity(1)) - /* TODO: test that choice alternates with previous */ - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu1[0], expect_cpu1[1]); - - if (ret) - return 1; - - printf("OK. All tests passed\n"); - return 0; -} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests deleted file mode 100644 index 7907824c635..00000000000 --- a/tools/testing/selftests/net-afpacket/run_afpackettests +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -if [ $(id -u) != 0 ]; then - echo $msg must be run as root >&2 - exit 0 -fi - -echo "--------------------" -echo "running psock_fanout test" -echo "--------------------" -./psock_fanout -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile deleted file mode 100644 index 2450fd8bb10..00000000000 --- a/tools/testing/selftests/net-socket/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -NET_SOCK_PROGS = socket - -all: $(NET_SOCK_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" - -clean: - $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests deleted file mode 100644 index c09a682df56..00000000000 --- a/tools/testing/selftests/net-socket/run_netsocktests +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -echo "--------------------" -echo "running socket test" -echo "--------------------" -./socket -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi - diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c deleted file mode 100644 index 0f227f2f9be..00000000000 --- a/tools/testing/selftests/net-socket/socket.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -struct socket_testcase { - int domain; - int type; - int protocol; - - /* 0 = valid file descriptor - * -foo = error foo - */ - int expect; - - /* If non-zero, accept EAFNOSUPPORT to handle the case - * of the protocol not being configured into the kernel. - */ - int nosupport_ok; -}; - -static struct socket_testcase tests[] = { - { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, - { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, - { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, -}; - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#define ERR_STRING_SZ 64 - -static int run_tests(void) -{ - char err_string1[ERR_STRING_SZ]; - char err_string2[ERR_STRING_SZ]; - int i, err; - - err = 0; - for (i = 0; i < ARRAY_SIZE(tests); i++) { - struct socket_testcase *s = &tests[i]; - int fd; - - fd = socket(s->domain, s->type, s->protocol); - if (fd < 0) { - if (s->nosupport_ok && - errno == EAFNOSUPPORT) - continue; - - if (s->expect < 0 && - errno == -s->expect) - continue; - - strerror_r(-s->expect, err_string1, ERR_STRING_SZ); - strerror_r(errno, err_string2, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "err (%s) got (%s)\n", - s->domain, s->type, s->protocol, - err_string1, err_string2); - - err = -1; - break; - } else { - close(fd); - - if (s->expect < 0) { - strerror_r(errno, err_string1, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "success got err (%s)\n", - s->domain, s->type, s->protocol, - err_string1); - - err = -1; - break; - } - } - } - - return err; -} - -int main(void) -{ - int err = run_tests(); - - return err; -} diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile new file mode 100644 index 00000000000..bd6e272bab8 --- /dev/null +++ b/tools/testing/selftests/net/Makefile @@ -0,0 +1,19 @@ +# Makefile for net selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +NET_PROGS = socket psock_fanout + +all: $(NET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(NET_PROGS) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c new file mode 100644 index 00000000000..226e5e33105 --- /dev/null +++ b/tools/testing/selftests/net/psock_fanout.c @@ -0,0 +1,388 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE /* for sched_setaffinity */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_LEN 100 +#define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("connect"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + sock_fanout_setfilter(fd); + return fd; +} + +static char *sock_fanout_open_ring(int fd) +{ + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; + + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); + exit(1); + } + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); + exit(1); + } + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + return 1; + } + + return 0; +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + ret = sock_fanout_read(fds, rings, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + ret |= sock_fanout_read(fds, rings, expect2); + + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; + + test_control_single(); + test_control_group(); + + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests new file mode 100644 index 00000000000..7907824c635 --- /dev/null +++ b/tools/testing/selftests/net/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests new file mode 100644 index 00000000000..c09a682df56 --- /dev/null +++ b/tools/testing/selftests/net/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net/socket.c b/tools/testing/selftests/net/socket.c new file mode 100644 index 00000000000..0f227f2f9be --- /dev/null +++ b/tools/testing/selftests/net/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3-70-g09d2 From 98e821a2a927b6dc0f7adc4b64ad29bec1b6ff89 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 21 Mar 2013 14:10:03 -0400 Subject: net: fix psock_fanout on sparc64 The packetsocket fanout test uses a packet ring. Use TPACKET_V2 instead of TPACKET_V1 to work around a known 32/64 bit issue in the older ring that manifests on sparc64. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_fanout.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 226e5e33105..59bd6367af7 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -182,7 +182,13 @@ static char *sock_fanout_open_ring(int fd) .tp_frame_nr = RING_NUM_FRAMES, }; char *ring; + int val = TPACKET_V2; + if (setsockopt(fd, SOL_PACKET, PACKET_VERSION, (void *) &val, + sizeof(val))) { + perror("packetsock ring setsockopt version"); + exit(1); + } if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))) { perror("packetsock ring setsockopt"); @@ -201,7 +207,7 @@ static char *sock_fanout_open_ring(int fd) static int sock_fanout_read_ring(int fd, void *ring) { - struct tpacket_hdr *header = ring; + struct tpacket2_hdr *header = ring; int count = 0; while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { -- cgit v1.2.3-70-g09d2 From 23a9544206dd91dfe048fcf67abec3f3104c42b9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 Apr 2013 13:00:51 +0000 Subject: selftests: net: add PF_PACKET TPACKET v1/v2/v3 selftests This patch adds a simple test case that probes the packet socket's TPACKET_V1, TPACKET_V2 and TPACKET_V3 behavior regarding mmap(2)'ed I/O for a small burst of 100 packets. The test currently runs for ... TPACKET_V1: RX_RING, TX_RING TPACKET_V2: RX_RING, TX_RING TPACKET_V3: RX_RING ... and will output on success: test: TPACKET_V1 with PACKET_RX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V1 with PACKET_TX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V2 with PACKET_RX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V2 with PACKET_TX_RING .................... 100 pkts (9600 bytes) test: TPACKET_V3 with PACKET_RX_RING .................... 100 pkts (9600 bytes) OK. All tests passed Reusable parts of psock_fanout.c have been put into a psock_lib.h file for common usage. Test case successfully tested on x86_64. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/net/Makefile | 4 +- tools/testing/selftests/net/psock_fanout.c | 88 +-- tools/testing/selftests/net/psock_lib.h | 127 ++++ tools/testing/selftests/net/psock_tpacket.c | 824 ++++++++++++++++++++++++++ tools/testing/selftests/net/run_afpackettests | 10 + 5 files changed, 966 insertions(+), 87 deletions(-) create mode 100644 tools/testing/selftests/net/psock_lib.h create mode 100644 tools/testing/selftests/net/psock_tpacket.c (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index bd6e272bab8..750512ba2c8 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -1,11 +1,11 @@ # Makefile for net selftests CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall +CFLAGS = -Wall -O2 -g CFLAGS += -I../../../../usr/include/ -NET_PROGS = socket psock_fanout +NET_PROGS = socket psock_fanout psock_tpacket all: $(NET_PROGS) %: %.c diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 59bd6367af7..57b9c2b7c4f 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -61,91 +61,9 @@ #include #include -#define DATA_LEN 100 -#define DATA_CHAR 'a' -#define RING_NUM_FRAMES 20 -#define PORT_BASE 8000 - -static void pair_udp_open(int fds[], uint16_t port) -{ - struct sockaddr_in saddr, daddr; - - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: socket dgram\n"); - exit(1); - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&daddr, 0, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_port = htons(port + 1); - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +#include "psock_lib.h" - /* must bind both to get consistent hash result */ - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { - perror("bind"); - exit(1); - } - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { - perror("bind"); - exit(1); - } - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("connect"); - exit(1); - } -} - -static void pair_udp_send(int fds[], int num) -{ - char buf[DATA_LEN], rbuf[DATA_LEN]; - - memset(buf, DATA_CHAR, sizeof(buf)); - while (num--) { - /* Should really handle EINTR and EAGAIN */ - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { - fprintf(stderr, "ERROR: send failed left=%d\n", num); - exit(1); - } - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { - fprintf(stderr, "ERROR: recv failed left=%d\n", num); - exit(1); - } - if (memcmp(buf, rbuf, sizeof(buf))) { - fprintf(stderr, "ERROR: data failed left=%d\n", num); - exit(1); - } - } -} - -static void sock_fanout_setfilter(int fd) -{ - struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x6, 0, 0, 0x00000060 }, /* RET match */ -/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ - }; - struct sock_fprog bpf_prog; - - bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, - sizeof(bpf_prog))) { - perror("setsockopt SO_ATTACH_FILTER"); - exit(1); - } -} +#define RING_NUM_FRAMES 20 /* Open a socket in a given fanout mode. * @return -1 if mode is bad, a valid socket otherwise */ @@ -169,7 +87,7 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) return -1; } - sock_fanout_setfilter(fd); + pair_udp_setfilter(fd); return fd; } diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h new file mode 100644 index 00000000000..37da54ac85a --- /dev/null +++ b/tools/testing/selftests/net/psock_lib.h @@ -0,0 +1,127 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn + * Daniel Borkmann + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef PSOCK_LIB_H +#define PSOCK_LIB_H + +#include +#include +#include +#include +#include + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +#define PORT_BASE 8000 + +#ifndef __maybe_unused +# define __maybe_unused __attribute__ ((__unused__)) +#endif + +static __maybe_unused void pair_udp_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x06, 0, 0, 0x00000060 }, /* RET match */ + { 0x06, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +static __maybe_unused void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("connect"); + exit(1); + } +} + +static __maybe_unused void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static __maybe_unused void pair_udp_close(int fds[]) +{ + close(fds[0]); + close(fds[1]); +} + +#endif /* PSOCK_LIB_H */ diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c new file mode 100644 index 00000000000..a8d7ffadd49 --- /dev/null +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -0,0 +1,824 @@ +/* + * Copyright 2013 Red Hat, Inc. + * Author: Daniel Borkmann + * + * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior. + * + * Control: + * Test the setup of the TPACKET socket with different patterns that are + * known to fail (TODO) resp. succeed (OK). + * + * Datapath: + * Open a pair of packet sockets and send resp. receive an a priori known + * packet pattern accross the sockets and check if it was received resp. + * sent correctly. Fanout in combination with RX_RING is currently not + * tested here. + * + * The test currently runs for + * - TPACKET_V1: RX_RING, TX_RING + * - TPACKET_V2: RX_RING, TX_RING + * - TPACKET_V3: RX_RING + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psock_lib.h" + +#ifndef bug_on +# define bug_on(cond) assert(!(cond)) +#endif + +#ifndef __aligned_tpacket +# define __aligned_tpacket __attribute__((aligned(TPACKET_ALIGNMENT))) +#endif + +#ifndef __align_tpacket +# define __align_tpacket(x) __attribute__((aligned(TPACKET_ALIGN(x)))) +#endif + +#define BLOCK_STATUS(x) ((x)->h1.block_status) +#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) +#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) +#define BLOCK_LEN(x) ((x)->h1.blk_len) +#define BLOCK_SNUM(x) ((x)->h1.seq_num) +#define BLOCK_O2PRIV(x) ((x)->offset_to_priv) +#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) +#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) +#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) +#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) + +#define NUM_PACKETS 100 + +struct ring { + struct iovec *rd; + uint8_t *mm_space; + size_t mm_len, rd_len; + struct sockaddr_ll ll; + void (*walk)(int sock, struct ring *ring); + int type, rd_num, flen, version; + union { + struct tpacket_req req; + struct tpacket_req3 req3; + }; +}; + +struct block_desc { + uint32_t version; + uint32_t offset_to_priv; + struct tpacket_hdr_v1 h1; +}; + +union frame_map { + struct { + struct tpacket_hdr tp_h __aligned_tpacket; + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket_hdr)); + } *v1; + struct { + struct tpacket2_hdr tp_h __aligned_tpacket; + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket2_hdr)); + } *v2; + void *raw; +}; + +static unsigned int total_packets, total_bytes; + +static int pfsocket(int ver) +{ + int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sock == -1) { + perror("socket"); + exit(1); + } + + ret = setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver)); + if (ret == -1) { + perror("setsockopt"); + exit(1); + } + + return sock; +} + +static void status_bar_update(void) +{ + if (total_packets % 10 == 0) { + fprintf(stderr, "."); + fflush(stderr); + } +} + +static void test_payload(void *pay, size_t len) +{ + struct ethhdr *eth = pay; + + if (len < sizeof(struct ethhdr)) { + fprintf(stderr, "test_payload: packet too " + "small: %zu bytes!\n", len); + exit(1); + } + + if (eth->h_proto != htons(ETH_P_IP)) { + fprintf(stderr, "test_payload: wrong ethernet " + "type: 0x%x!\n", ntohs(eth->h_proto)); + exit(1); + } +} + +static void create_payload(void *pay, size_t *len) +{ + int i; + struct ethhdr *eth = pay; + struct iphdr *ip = pay + sizeof(*eth); + + /* Lets create some broken crap, that still passes + * our BPF filter. + */ + + *len = DATA_LEN + 42; + + memset(pay, 0xff, ETH_ALEN * 2); + eth->h_proto = htons(ETH_P_IP); + + for (i = 0; i < sizeof(*ip); ++i) + ((uint8_t *) pay)[i + sizeof(*eth)] = (uint8_t) rand(); + + ip->ihl = 5; + ip->version = 4; + ip->protocol = 0x11; + ip->frag_off = 0; + ip->ttl = 64; + ip->tot_len = htons((uint16_t) *len - sizeof(*eth)); + + ip->saddr = htonl(INADDR_LOOPBACK); + ip->daddr = htonl(INADDR_LOOPBACK); + + memset(pay + sizeof(*eth) + sizeof(*ip), + DATA_CHAR, DATA_LEN); +} + +static inline int __v1_rx_kernel_ready(struct tpacket_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); +} + +static inline void __v1_rx_user_ready(struct tpacket_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static inline int __v2_rx_kernel_ready(struct tpacket2_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); +} + +static inline void __v2_rx_user_ready(struct tpacket2_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static inline int __v1_v2_rx_kernel_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + return __v1_rx_kernel_ready(base); + case TPACKET_V2: + return __v2_rx_kernel_ready(base); + default: + bug_on(1); + return 0; + } +} + +static inline void __v1_v2_rx_user_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + __v1_rx_user_ready(base); + break; + case TPACKET_V2: + __v2_rx_user_ready(base); + break; + } +} + +static void walk_v1_v2_rx(int sock, struct ring *ring) +{ + struct pollfd pfd; + int udp_sock[2]; + union frame_map ppd; + unsigned int frame_num = 0; + + bug_on(ring->type != PACKET_RX_RING); + + pair_udp_open(udp_sock, PORT_BASE); + pair_udp_setfilter(sock); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN | POLLERR; + pfd.revents = 0; + + pair_udp_send(udp_sock, NUM_PACKETS); + + while (total_packets < NUM_PACKETS * 2) { + while (__v1_v2_rx_kernel_ready(ring->rd[frame_num].iov_base, + ring->version)) { + ppd.raw = ring->rd[frame_num].iov_base; + + switch (ring->version) { + case TPACKET_V1: + test_payload((uint8_t *) ppd.raw + ppd.v1->tp_h.tp_mac, + ppd.v1->tp_h.tp_snaplen); + total_bytes += ppd.v1->tp_h.tp_snaplen; + break; + + case TPACKET_V2: + test_payload((uint8_t *) ppd.raw + ppd.v2->tp_h.tp_mac, + ppd.v2->tp_h.tp_snaplen); + total_bytes += ppd.v2->tp_h.tp_snaplen; + break; + } + + status_bar_update(); + total_packets++; + + __v1_v2_rx_user_ready(ppd.raw, ring->version); + + frame_num = (frame_num + 1) % ring->rd_num; + } + + poll(&pfd, 1, 1); + } + + pair_udp_close(udp_sock); + + if (total_packets != 2 * NUM_PACKETS) { + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", + ring->version, total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); +} + +static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); +} + +static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_SEND_REQUEST; + __sync_synchronize(); +} + +static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr) +{ + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); +} + +static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_SEND_REQUEST; + __sync_synchronize(); +} + +static inline int __v1_v2_tx_kernel_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + return __v1_tx_kernel_ready(base); + case TPACKET_V2: + return __v2_tx_kernel_ready(base); + default: + bug_on(1); + return 0; + } +} + +static inline void __v1_v2_tx_user_ready(void *base, int version) +{ + switch (version) { + case TPACKET_V1: + __v1_tx_user_ready(base); + break; + case TPACKET_V2: + __v2_tx_user_ready(base); + break; + } +} + +static void __v1_v2_set_packet_loss_discard(int sock) +{ + int ret, discard = 1; + + ret = setsockopt(sock, SOL_PACKET, PACKET_LOSS, (void *) &discard, + sizeof(discard)); + if (ret == -1) { + perror("setsockopt"); + exit(1); + } +} + +static void walk_v1_v2_tx(int sock, struct ring *ring) +{ + struct pollfd pfd; + int rcv_sock, ret; + size_t packet_len; + union frame_map ppd; + char packet[1024]; + unsigned int frame_num = 0, got = 0; + struct sockaddr_ll ll = { + .sll_family = PF_PACKET, + .sll_halen = ETH_ALEN, + }; + + bug_on(ring->type != PACKET_TX_RING); + bug_on(ring->rd_num < NUM_PACKETS); + + rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (rcv_sock == -1) { + perror("socket"); + exit(1); + } + + pair_udp_setfilter(rcv_sock); + + ll.sll_ifindex = if_nametoindex("lo"); + ret = bind(rcv_sock, (struct sockaddr *) &ll, sizeof(ll)); + if (ret == -1) { + perror("bind"); + exit(1); + } + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLOUT | POLLERR; + pfd.revents = 0; + + total_packets = NUM_PACKETS; + create_payload(packet, &packet_len); + + while (total_packets > 0) { + while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base, + ring->version) && + total_packets > 0) { + ppd.raw = ring->rd[frame_num].iov_base; + + switch (ring->version) { + case TPACKET_V1: + ppd.v1->tp_h.tp_snaplen = packet_len; + ppd.v1->tp_h.tp_len = packet_len; + + memcpy((uint8_t *) ppd.raw + TPACKET_HDRLEN - + sizeof(struct sockaddr_ll), packet, + packet_len); + total_bytes += ppd.v1->tp_h.tp_snaplen; + break; + + case TPACKET_V2: + ppd.v2->tp_h.tp_snaplen = packet_len; + ppd.v2->tp_h.tp_len = packet_len; + + memcpy((uint8_t *) ppd.raw + TPACKET2_HDRLEN - + sizeof(struct sockaddr_ll), packet, + packet_len); + total_bytes += ppd.v2->tp_h.tp_snaplen; + break; + } + + status_bar_update(); + total_packets--; + + __v1_v2_tx_user_ready(ppd.raw, ring->version); + + frame_num = (frame_num + 1) % ring->rd_num; + } + + poll(&pfd, 1, 1); + } + + bug_on(total_packets != 0); + + ret = sendto(sock, NULL, 0, 0, NULL, 0); + if (ret == -1) { + perror("sendto"); + exit(1); + } + + while ((ret = recvfrom(rcv_sock, packet, sizeof(packet), + 0, NULL, NULL)) > 0 && + total_packets < NUM_PACKETS) { + got += ret; + test_payload(packet, ret); + + status_bar_update(); + total_packets++; + } + + close(rcv_sock); + + if (total_packets != NUM_PACKETS) { + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", + ring->version, total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, got); +} + +static void walk_v1_v2(int sock, struct ring *ring) +{ + if (ring->type == PACKET_RX_RING) + walk_v1_v2_rx(sock, ring); + else + walk_v1_v2_tx(sock, ring); +} + +static uint64_t __v3_prev_block_seq_num = 0; + +void __v3_test_block_seq_num(struct block_desc *pbd) +{ + if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) { + fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected " + "seq:%"PRIu64" != actual seq:%"PRIu64"\n", + __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1, + (uint64_t) BLOCK_SNUM(pbd)); + exit(1); + } + + __v3_prev_block_seq_num = BLOCK_SNUM(pbd); +} + +static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) +{ + if (BLOCK_NUM_PKTS(pbd)) { + if (bytes != BLOCK_LEN(pbd)) { + fprintf(stderr, "\nblock:%u with %upackets, expected " + "len:%u != actual len:%u\n", block_num, + BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); + exit(1); + } + } else { + if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) { + fprintf(stderr, "\nblock:%u, expected len:%lu != " + "actual len:%u\n", block_num, BLOCK_HDR_LEN, + BLOCK_LEN(pbd)); + exit(1); + } + } +} + +static void __v3_test_block_header(struct block_desc *pbd, const int block_num) +{ + uint32_t block_status = BLOCK_STATUS(pbd); + + if ((block_status & TP_STATUS_USER) == 0) { + fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num); + exit(1); + } + + __v3_test_block_seq_num(pbd); +} + +static void __v3_walk_block(struct block_desc *pbd, const int block_num) +{ + int num_pkts = BLOCK_NUM_PKTS(pbd), i; + unsigned long bytes = 0; + unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13); + struct tpacket3_hdr *ppd; + + __v3_test_block_header(pbd, block_num); + + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); + for (i = 0; i < num_pkts; ++i) { + bytes += ppd->tp_snaplen; + + if (ppd->tp_next_offset) + bytes_with_padding += ppd->tp_next_offset; + else + bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); + + test_payload((uint8_t *) ppd + ppd->tp_mac, ppd->tp_snaplen); + + status_bar_update(); + total_packets++; + + ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); + __sync_synchronize(); + } + + __v3_test_block_len(pbd, bytes_with_padding, block_num); + total_bytes += bytes; +} + +void __v3_flush_block(struct block_desc *pbd) +{ + BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static void walk_v3_rx(int sock, struct ring *ring) +{ + unsigned int block_num = 0; + struct pollfd pfd; + struct block_desc *pbd; + int udp_sock[2]; + + bug_on(ring->type != PACKET_RX_RING); + + pair_udp_open(udp_sock, PORT_BASE); + pair_udp_setfilter(sock); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN | POLLERR; + pfd.revents = 0; + + pair_udp_send(udp_sock, NUM_PACKETS); + + while (total_packets < NUM_PACKETS * 2) { + pbd = (struct block_desc *) ring->rd[block_num].iov_base; + + while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) + poll(&pfd, 1, 1); + + __v3_walk_block(pbd, block_num); + __v3_flush_block(pbd); + + block_num = (block_num + 1) % ring->rd_num; + } + + pair_udp_close(udp_sock); + + if (total_packets != 2 * NUM_PACKETS) { + fprintf(stderr, "walk_v3_rx: received %u out of %u pkts\n", + total_packets, NUM_PACKETS); + exit(1); + } + + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); +} + +static void walk_v3(int sock, struct ring *ring) +{ + if (ring->type == PACKET_RX_RING) + walk_v3_rx(sock, ring); + else + bug_on(1); +} + +static void __v1_v2_fill(struct ring *ring, unsigned int blocks) +{ + ring->req.tp_block_size = getpagesize() << 2; + ring->req.tp_frame_size = TPACKET_ALIGNMENT << 7; + ring->req.tp_block_nr = blocks; + + ring->req.tp_frame_nr = ring->req.tp_block_size / + ring->req.tp_frame_size * + ring->req.tp_block_nr; + + ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr; + ring->walk = walk_v1_v2; + ring->rd_num = ring->req.tp_frame_nr; + ring->flen = ring->req.tp_frame_size; +} + +static void __v3_fill(struct ring *ring, unsigned int blocks) +{ + ring->req3.tp_retire_blk_tov = 64; + ring->req3.tp_sizeof_priv = 13; + ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; + + ring->req3.tp_block_size = getpagesize() << 2; + ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7; + ring->req3.tp_block_nr = blocks; + + ring->req3.tp_frame_nr = ring->req3.tp_block_size / + ring->req3.tp_frame_size * + ring->req3.tp_block_nr; + + ring->mm_len = ring->req3.tp_block_size * ring->req3.tp_block_nr; + ring->walk = walk_v3; + ring->rd_num = ring->req3.tp_block_nr; + ring->flen = ring->req3.tp_block_size; +} + +static void setup_ring(int sock, struct ring *ring, int version, int type) +{ + int ret = 0; + unsigned int blocks = 256; + + ring->type = type; + ring->version = version; + + switch (version) { + case TPACKET_V1: + case TPACKET_V2: + if (type == PACKET_TX_RING) + __v1_v2_set_packet_loss_discard(sock); + __v1_v2_fill(ring, blocks); + ret = setsockopt(sock, SOL_PACKET, type, &ring->req, + sizeof(ring->req)); + break; + + case TPACKET_V3: + __v3_fill(ring, blocks); + ret = setsockopt(sock, SOL_PACKET, type, &ring->req3, + sizeof(ring->req3)); + break; + } + + if (ret == -1) { + perror("setsockopt"); + exit(1); + } + + ring->rd_len = ring->rd_num * sizeof(*ring->rd); + ring->rd = malloc(ring->rd_len); + if (ring->rd == NULL) { + perror("malloc"); + exit(1); + } + + total_packets = 0; + total_bytes = 0; +} + +static void mmap_ring(int sock, struct ring *ring) +{ + int i; + + ring->mm_space = mmap(0, ring->mm_len, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0); + if (ring->mm_space == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + memset(ring->rd, 0, ring->rd_len); + for (i = 0; i < ring->rd_num; ++i) { + ring->rd[i].iov_base = ring->mm_space + (i * ring->flen); + ring->rd[i].iov_len = ring->flen; + } +} + +static void bind_ring(int sock, struct ring *ring) +{ + int ret; + + ring->ll.sll_family = PF_PACKET; + ring->ll.sll_protocol = htons(ETH_P_ALL); + ring->ll.sll_ifindex = if_nametoindex("lo"); + ring->ll.sll_hatype = 0; + ring->ll.sll_pkttype = 0; + ring->ll.sll_halen = 0; + + ret = bind(sock, (struct sockaddr *) &ring->ll, sizeof(ring->ll)); + if (ret == -1) { + perror("bind"); + exit(1); + } +} + +static void walk_ring(int sock, struct ring *ring) +{ + ring->walk(sock, ring); +} + +static void unmap_ring(int sock, struct ring *ring) +{ + munmap(ring->mm_space, ring->mm_len); + free(ring->rd); +} + +static int test_kernel_bit_width(void) +{ + char in[512], *ptr; + int num = 0, fd; + ssize_t ret; + + fd = open("/proc/kallsyms", O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + + ret = read(fd, in, sizeof(in)); + if (ret <= 0) { + perror("read"); + exit(1); + } + + close(fd); + + ptr = in; + while(!isspace(*ptr)) { + num++; + ptr++; + } + + return num * 4; +} + +static int test_user_bit_width(void) +{ + return __WORDSIZE; +} + +static const char *tpacket_str[] = { + [TPACKET_V1] = "TPACKET_V1", + [TPACKET_V2] = "TPACKET_V2", + [TPACKET_V3] = "TPACKET_V3", +}; + +static const char *type_str[] = { + [PACKET_RX_RING] = "PACKET_RX_RING", + [PACKET_TX_RING] = "PACKET_TX_RING", +}; + +static int test_tpacket(int version, int type) +{ + int sock; + struct ring ring; + + fprintf(stderr, "test: %s with %s ", tpacket_str[version], + type_str[type]); + fflush(stderr); + + if (version == TPACKET_V1 && + test_kernel_bit_width() != test_user_bit_width()) { + fprintf(stderr, "test: skip %s %s since user and kernel " + "space have different bit width\n", + tpacket_str[version], type_str[type]); + return 0; + } + + sock = pfsocket(version); + memset(&ring, 0, sizeof(ring)); + setup_ring(sock, &ring, version, type); + mmap_ring(sock, &ring); + bind_ring(sock, &ring); + walk_ring(sock, &ring); + unmap_ring(sock, &ring); + close(sock); + + fprintf(stderr, "\n"); + return 0; +} + +int main(void) +{ + int ret = 0; + + ret |= test_tpacket(TPACKET_V1, PACKET_RX_RING); + ret |= test_tpacket(TPACKET_V1, PACKET_TX_RING); + + ret |= test_tpacket(TPACKET_V2, PACKET_RX_RING); + ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING); + + ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING); + + if (ret) + return 1; + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests index 7907824c635..5246e782d6e 100644 --- a/tools/testing/selftests/net/run_afpackettests +++ b/tools/testing/selftests/net/run_afpackettests @@ -14,3 +14,13 @@ if [ $? -ne 0 ]; then else echo "[PASS]" fi + +echo "--------------------" +echo "running psock_tpacket test" +echo "--------------------" +./psock_tpacket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3-70-g09d2 From cf270148662a117c04753d17324e3bd28ce0396a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 19 Apr 2013 16:36:12 -0400 Subject: net: Add .gitignore to networking selftests directory. Signed-off-by: David S. Miller --- tools/testing/selftests/net/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tools/testing/selftests/net/.gitignore (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore new file mode 100644 index 00000000000..00326629d4a --- /dev/null +++ b/tools/testing/selftests/net/.gitignore @@ -0,0 +1,3 @@ +socket +psock_fanout +psock_tpacket -- cgit v1.2.3-70-g09d2 From fdd5f43a1b53a844d04c6eda2cbdbe044b629ae7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 24 Apr 2013 23:08:00 +0000 Subject: selftests: psock_tpacket: fix status check Testing like this for TP_STATUS_AVAILABLE clearly is a stupid bug since it always returns true. Fix this by only checking for flags where the kernel owns the packet and negate this result, since we also could run into the non-zero status TP_STATUS_WRONG_FORMAT and need to reclaim frames. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_tpacket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c index a8d7ffadd49..c41b58640a0 100644 --- a/tools/testing/selftests/net/psock_tpacket.c +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -300,7 +300,7 @@ static void walk_v1_v2_rx(int sock, struct ring *ring) static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr) { - return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); + return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)); } static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) @@ -311,7 +311,7 @@ static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr) { - return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); + return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)); } static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) -- cgit v1.2.3-70-g09d2