History

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.

Willem de Bruijn committed 13 years ago

Commit 77f65eb1 parent b0aa73b7 files · +451 −24Browse files

Unified · Split

7 files
  1. include/uapi/linux/if_packet.h +2 −0
  2. net/packet/af_packet.c +86 −23
  3. net/packet/internal.h +2 −1
  4. tools/testing/selftests/Makefile +1 −0
  5. tools/testing/selftests/net-afpacket/Makefile +18 −0
  6. tools/testing/selftests/net-afpacket/psock_fanout.c +326 −0
  7. tools/testing/selftests/net-afpacket/run_afpackettests +16 −0
include/uapi/linux/if_packet.h+2−0
@@ -55,6 +55,8 @@ struct sockaddr_ll { · 54 unmodified lines
5555 #define PACKET_FANOUT_HASH 0
5656 #define PACKET_FANOUT_LB 1
5757 #define PACKET_FANOUT_CPU 2
58+#define PACKET_FANOUT_ROLLOVER 3
59+#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000
5860 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000
5961
6062 struct tpacket_stats {
net/packet/af_packet.c+86−23
@@ -181,6 +181,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, · 180 unmodified lines
181181
182182 struct packet_sock;
183183 static int tpacket_snd(struct packet_sock *po, struct msghdr *msg);
184+static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
185+ struct packet_type *pt, struct net_device *orig_dev);
184186
185187 static void *packet_previous_frame(struct packet_sock *po,
186188 struct packet_ring_buffer *rb,
@@ -973,11 +975,11 @@ static void *packet_current_rx_frame(struct packet_sock *po, · 786 unmodified lines
973975
974976 static void *prb_lookup_block(struct packet_sock *po,
975977 struct packet_ring_buffer *rb,
976− unsigned int previous,
978+ unsigned int idx,
977979 int status)
978980 {
979981 struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(rb);
980− struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, previous);
982+ struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, idx);
981983
982984 if (status != BLOCK_STATUS(pbd))
983985 return NULL;
@@ -1041,6 +1043,29 @@ static void packet_increment_head(struct packet_ring_buffer *buff) · 57 unmodified lines
10411043 buff->head = buff->head != buff->frame_max ? buff->head+1 : 0;
10421044 }
10431045
1046+static bool packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb)
1047+{
1048+ struct sock *sk = &po->sk;
1049+ bool has_room;
1050+
1051+ if (po->prot_hook.func != tpacket_rcv)
1052+ return (atomic_read(&sk->sk_rmem_alloc) + skb->truesize)
1053+ <= sk->sk_rcvbuf;
1054+
1055+ spin_lock(&sk->sk_receive_queue.lock);
1056+ if (po->tp_version == TPACKET_V3)
1057+ has_room = prb_lookup_block(po, &po->rx_ring,
1058+ po->rx_ring.prb_bdqc.kactive_blk_num,
1059+ TP_STATUS_KERNEL);
1060+ else
1061+ has_room = packet_lookup_frame(po, &po->rx_ring,
1062+ po->rx_ring.head,
1063+ TP_STATUS_KERNEL);
1064+ spin_unlock(&sk->sk_receive_queue.lock);
1065+
1066+ return has_room;
1067+}
1068+
10441069 static void packet_sock_destruct(struct sock *sk)
10451070 {
10461071 skb_queue_purge(&sk->sk_error_queue);
@@ -1066,16 +1091,16 @@ static int fanout_rr_next(struct packet_fanout *f, unsigned int num) · 19 unmodified lines
10661091 return x;
10671092 }
10681093
1069−static struct sock *fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
1094+static unsigned int fanout_demux_hash(struct packet_fanout *f,
1095+ struct sk_buff *skb,
1096+ unsigned int num)
10701097 {
1071− u32 idx, hash = skb->rxhash;
1072−
1073− idx = ((u64)hash * num) >> 32;
1074−
1075− return f->arr[idx];
1098+ return (((u64)skb->rxhash) * num) >> 32;
10761099 }
10771100
1078−static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
1101+static unsigned int fanout_demux_lb(struct packet_fanout *f,
1102+ struct sk_buff *skb,
1103+ unsigned int num)
10791104 {
10801105 int cur, old;
10811106
@@ -1083,14 +1108,40 @@ static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb · 1 unmodified line
10831108 while ((old = atomic_cmpxchg(&f->rr_cur, cur,
10841109 fanout_rr_next(f, num))) != cur)
10851110 cur = old;
1086− return f->arr[cur];
1111+ return cur;
1112+}
1113+
1114+static unsigned int fanout_demux_cpu(struct packet_fanout *f,
1115+ struct sk_buff *skb,
1116+ unsigned int num)
1117+{
1118+ return smp_processor_id() % num;
10871119 }
10881120
1089−static struct sock *fanout_demux_cpu(struct packet_fanout *f, struct sk_buff *skb, unsigned int num)
1121+static unsigned int fanout_demux_rollover(struct packet_fanout *f,
1122+ struct sk_buff *skb,
1123+ unsigned int idx, unsigned int skip,
1124+ unsigned int num)
10901125 {
1091− unsigned int cpu = smp_processor_id();
1126+ unsigned int i, j;
10921127
1093− return f->arr[cpu % num];
1128+ i = j = min_t(int, f->next[idx], num - 1);
1129+ do {
1130+ if (i != skip && packet_rcv_has_room(pkt_sk(f->arr[i]), skb)) {
1131+ if (i != j)
1132+ f->next[idx] = i;
1133+ return i;
1134+ }
1135+ if (++i == num)
1136+ i = 0;
1137+ } while (i != j);
1138+
1139+ return idx;
1140+}
1141+
1142+static bool fanout_has_flag(struct packet_fanout *f, u16 flag)
1143+{
1144+ return f->flags & (flag >> 8);
10941145 }
10951146
10961147 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, · 2 unmodified lines
10991150 struct packet_fanout *f = pt->af_packet_priv;
11001151 unsigned int num = f->num_members;
11011152 struct packet_sock *po;
1102− struct sock *sk;
1153+ unsigned int idx;
11031154
11041155 if (!net_eq(dev_net(dev), read_pnet(&f->net)) ||
11051156 !num) {
@@ -1110,23 +1161,31 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, · 4 unmodified lines
11101161 switch (f->type) {
11111162 case PACKET_FANOUT_HASH:
11121163 default:
1113− if (f->defrag) {
1164+ if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {
11141165 skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET);
11151166 if (!skb)
11161167 return 0;
11171168 }
11181169 skb_get_rxhash(skb);
1119− sk = fanout_demux_hash(f, skb, num);
1170+ idx = fanout_demux_hash(f, skb, num);
11201171 break;
11211172 case PACKET_FANOUT_LB:
1122− sk = fanout_demux_lb(f, skb, num);
1173+ idx = fanout_demux_lb(f, skb, num);
11231174 break;
11241175 case PACKET_FANOUT_CPU:
1125− sk = fanout_demux_cpu(f, skb, num);
1176+ idx = fanout_demux_cpu(f, skb, num);
1177+ break;
1178+ case PACKET_FANOUT_ROLLOVER:
1179+ idx = fanout_demux_rollover(f, skb, 0, (unsigned int) -1, num);
11261180 break;
11271181 }
11281182
1129− po = pkt_sk(sk);
1183+ po = pkt_sk(f->arr[idx]);
1184+ if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER) &&
1185+ unlikely(!packet_rcv_has_room(po, skb))) {
1186+ idx = fanout_demux_rollover(f, skb, idx, idx, num);
1187+ po = pkt_sk(f->arr[idx]);
1188+ }
11301189
11311190 return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev);
11321191 }
@@ -1175,10 +1234,13 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) · 42 unmodified lines
11751234 struct packet_sock *po = pkt_sk(sk);
11761235 struct packet_fanout *f, *match;
11771236 u8 type = type_flags & 0xff;
1178− u8 defrag = (type_flags & PACKET_FANOUT_FLAG_DEFRAG) ? 1 : 0;
1237+ u8 flags = type_flags >> 8;
11791238 int err;
11801239
11811240 switch (type) {
1241+ case PACKET_FANOUT_ROLLOVER:
1242+ if (type_flags & PACKET_FANOUT_FLAG_ROLLOVER)
1243+ return -EINVAL;
11821244 case PACKET_FANOUT_HASH:
11831245 case PACKET_FANOUT_LB:
11841246 case PACKET_FANOUT_CPU:
@@ -1203,7 +1265,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) · 18 unmodified lines
12031265 }
12041266 }
12051267 err = -EINVAL;
1206− if (match && match->defrag != defrag)
1268+ if (match && match->flags != flags)
12071269 goto out;
12081270 if (!match) {
12091271 err = -ENOMEM;
@@ -1213,7 +1275,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) · 3 unmodified lines
12131275 write_pnet(&match->net, sock_net(sk));
12141276 match->id = id;
12151277 match->type = type;
1216− match->defrag = defrag;
1278+ match->flags = flags;
12171279 atomic_set(&match->rr_cur, 0);
12181280 INIT_LIST_HEAD(&match->list);
12191281 spin_lock_init(&match->lock);
@@ -3240,7 +3302,8 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, · 2020 unmodified lines
32403302 case PACKET_FANOUT:
32413303 val = (po->fanout ?
32423304 ((u32)po->fanout->id |
3243− ((u32)po->fanout->type << 16)) :
3305+ ((u32)po->fanout->type << 16) |
3306+ ((u32)po->fanout->flags << 24)) :
32443307 0);
32453308 break;
32463309 case PACKET_TX_HAS_OFF:
net/packet/internal.h+2−1
@@ -77,10 +77,11 @@ struct packet_fanout { · 76 unmodified lines
7777 unsigned int num_members;
7878 u16 id;
7979 u8 type;
80− u8 defrag;
80+ u8 flags;
8181 atomic_t rr_cur;
8282 struct list_head list;
8383 struct sock *arr[PACKET_FANOUT_MAX];
84+ int next[PACKET_FANOUT_MAX];
8485 spinlock_t lock;
8586 atomic_t sk_ref;
8687 struct packet_type prot_hook ____cacheline_aligned_in_smp;
tools/testing/selftests/Makefile+1−0
@@ -6,6 +6,7 @@ TARGETS += cpu-hotplug · 5 unmodified lines
66 TARGETS += memory-hotplug
77 TARGETS += efivarfs
88 TARGETS += net-socket
9+TARGETS += net-afpacket
910
1011 all:
1112 for TARGET in $(TARGETS); do \
tools/testing/selftests/net-afpacket/Makefile+18−0
@@ -0,0 +1,18 @@
1+# Makefile for net-socket selftests
2+
3+CC = $(CROSS_COMPILE)gcc
4+CFLAGS = -Wall
5+
6+CFLAGS += -I../../../../usr/include/
7+
8+AF_PACKET_PROGS = psock_fanout
9+
10+all: $(AF_PACKET_PROGS)
11+%: %.c
12+ $(CC) $(CFLAGS) -o $@ $^
13+
14+run_tests: all
15+ @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
16+
17+clean:
18+ $(RM) $(AF_PACKET_PROGS)
tools/testing/selftests/net-afpacket/psock_fanout.c+326−0
@@ -0,0 +1,326 @@
1+/*
2+ * Copyright 2013 Google Inc.
3+ * Author: Willem de Bruijn (willemb@google.com)
4+ *
5+ * A basic test of packet socket fanout behavior.
6+ *
7+ * Control:
8+ * - create fanout fails as expected with illegal flag combinations
9+ * - join fanout fails as expected with diverging types or flags
10+ *
11+ * Datapath:
12+ * Open a pair of packet sockets and a pair of INET sockets, send a known
13+ * number of packets across the two INET sockets and count the number of
14+ * packets enqueued onto the two packet sockets.
15+ *
16+ * The test currently runs for
17+ * - PACKET_FANOUT_HASH
18+ * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER
19+ * - PACKET_FANOUT_ROLLOVER
20+ *
21+ * Todo:
22+ * - datapath: PACKET_FANOUT_LB
23+ * - datapath: PACKET_FANOUT_CPU
24+ * - functionality: PACKET_FANOUT_FLAG_DEFRAG
25+ *
26+ * License (GPLv2):
27+ *
28+ * This program is free software; you can redistribute it and/or modify it
29+ * under the terms and conditions of the GNU General Public License,
30+ * version 2, as published by the Free Software Foundation.
31+ *
32+ * This program is distributed in the hope it will be useful, but WITHOUT
33+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
34+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
35+ * more details.
36+ *
37+ * You should have received a copy of the GNU General Public License along with
38+ * this program; if not, write to the Free Software Foundation, Inc.,
39+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
40+ */
41+
42+#include <arpa/inet.h>
43+#include <errno.h>
44+#include <linux/filter.h>
45+#include <linux/if_packet.h>
46+#include <net/ethernet.h>
47+#include <netinet/ip.h>
48+#include <netinet/udp.h>
49+#include <fcntl.h>
50+#include <stdint.h>
51+#include <stdio.h>
52+#include <stdlib.h>
53+#include <string.h>
54+#include <sys/socket.h>
55+#include <sys/stat.h>
56+#include <sys/types.h>
57+#include <unistd.h>
58+
59+/* Hack: build even if local includes are old */
60+#ifndef PACKET_FANOUT
61+#define PACKET_FANOUT 18
62+#define PACKET_FANOUT_HASH 0
63+#define PACKET_FANOUT_LB 1
64+#define PACKET_FANOUT_CPU 2
65+#define PACKET_FANOUT_FLAG_DEFRAG 0x8000
66+
67+#ifndef PACKET_FANOUT_ROLLOVER
68+#define PACKET_FANOUT_ROLLOVER 3
69+#endif
70+
71+#ifndef PACKET_FANOUT_FLAG_ROLLOVER
72+#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000
73+#endif
74+
75+#endif
76+
77+#define DATA_LEN 100
78+#define DATA_CHAR 'a'
79+
80+static void pair_udp_open(int fds[], uint16_t port)
81+{
82+ struct sockaddr_in saddr, daddr;
83+
84+ fds[0] = socket(PF_INET, SOCK_DGRAM, 0);
85+ fds[1] = socket(PF_INET, SOCK_DGRAM, 0);
86+ if (fds[0] == -1 || fds[1] == -1) {
87+ fprintf(stderr, "ERROR: socket dgram\n");
88+ exit(1);
89+ }
90+
91+ memset(&saddr, 0, sizeof(saddr));
92+ saddr.sin_family = AF_INET;
93+ saddr.sin_port = htons(port);
94+ saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
95+
96+ memset(&daddr, 0, sizeof(daddr));
97+ daddr.sin_family = AF_INET;
98+ daddr.sin_port = htons(port + 1);
99+ daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
100+
101+ /* must bind both to get consistent hash result */
102+ if (bind(fds[1], (void *) &daddr, sizeof(daddr))) {
103+ perror("bind");
104+ exit(1);
105+ }
106+ if (bind(fds[0], (void *) &saddr, sizeof(saddr))) {
107+ perror("bind");
108+ exit(1);
109+ }
110+ if (connect(fds[0], (void *) &daddr, sizeof(daddr))) {
111+ perror("bind");
112+ exit(1);
113+ }
114+}
115+
116+static void pair_udp_send(int fds[], int num)
117+{
118+ char buf[DATA_LEN], rbuf[DATA_LEN];
119+
120+ memset(buf, DATA_CHAR, sizeof(buf));
121+ while (num--) {
122+ /* Should really handle EINTR and EAGAIN */
123+ if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) {
124+ fprintf(stderr, "ERROR: send failed left=%d\n", num);
125+ exit(1);
126+ }
127+ if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) {
128+ fprintf(stderr, "ERROR: recv failed left=%d\n", num);
129+ exit(1);
130+ }
131+ if (memcmp(buf, rbuf, sizeof(buf))) {
132+ fprintf(stderr, "ERROR: data failed left=%d\n", num);
133+ exit(1);
134+ }
135+ }
136+}
137+
138+static void sock_fanout_setfilter(int fd)
139+{
140+ struct sock_filter bpf_filter[] = {
141+ { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */
142+ { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/
143+ { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */
144+ { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/
145+ { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */
146+ { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/
147+ { 0x6, 0, 0, 0x00000060 }, /* RET match */
148+/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */
149+ };
150+ struct sock_fprog bpf_prog;
151+
152+ bpf_prog.filter = bpf_filter;
153+ bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter);
154+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog,
155+ sizeof(bpf_prog))) {
156+ perror("setsockopt SO_ATTACH_FILTER");
157+ exit(1);
158+ }
159+}
160+
161+/* Open a socket in a given fanout mode.
162+ * @return -1 if mode is bad, a valid socket otherwise */
163+static int sock_fanout_open(uint16_t typeflags, int num_packets)
164+{
165+ int fd, val;
166+
167+ fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
168+ if (fd < 0) {
169+ perror("socket packet");
170+ exit(1);
171+ }
172+
173+ /* fanout group ID is always 0: tests whether old groups are deleted */
174+ val = ((int) typeflags) << 16;
175+ if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) {
176+ if (close(fd)) {
177+ perror("close packet");
178+ exit(1);
179+ }
180+ return -1;
181+ }
182+
183+ val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN;
184+ val *= num_packets;
185+ /* hack: apparently, the above calculation is too small (TODO: fix) */
186+ val *= 3;
187+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) {
188+ perror("setsockopt SO_RCVBUF");
189+ exit(1);
190+ }
191+
192+ sock_fanout_setfilter(fd);
193+ return fd;
194+}
195+
196+static void sock_fanout_read(int fds[], const int expect[])
197+{
198+ struct tpacket_stats stats;
199+ socklen_t ssize;
200+ int ret[2];
201+
202+ ssize = sizeof(stats);
203+ if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) {
204+ perror("getsockopt statistics 0");
205+ exit(1);
206+ }
207+ ret[0] = stats.tp_packets - stats.tp_drops;
208+ ssize = sizeof(stats);
209+ if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) {
210+ perror("getsockopt statistics 1");
211+ exit(1);
212+ }
213+ ret[1] = stats.tp_packets - stats.tp_drops;
214+
215+ fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n",
216+ ret[0], ret[1], expect[0], expect[1]);
217+
218+ if ((!(ret[0] == expect[0] && ret[1] == expect[1])) &&
219+ (!(ret[0] == expect[1] && ret[1] == expect[0]))) {
220+ fprintf(stderr, "ERROR: incorrect queue lengths\n");
221+ exit(1);
222+ }
223+}
224+
225+/* Test illegal mode + flag combination */
226+static void test_control_single(void)
227+{
228+ fprintf(stderr, "test: control single socket\n");
229+
230+ if (sock_fanout_open(PACKET_FANOUT_ROLLOVER |
231+ PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) {
232+ fprintf(stderr, "ERROR: opened socket with dual rollover\n");
233+ exit(1);
234+ }
235+}
236+
237+/* Test illegal group with different modes or flags */
238+static void test_control_group(void)
239+{
240+ int fds[2];
241+
242+ fprintf(stderr, "test: control multiple sockets\n");
243+
244+ fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
245+ if (fds[0] == -1) {
246+ fprintf(stderr, "ERROR: failed to open HASH socket\n");
247+ exit(1);
248+ }
249+ if (sock_fanout_open(PACKET_FANOUT_HASH |
250+ PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) {
251+ fprintf(stderr, "ERROR: joined group with wrong flag defrag\n");
252+ exit(1);
253+ }
254+ if (sock_fanout_open(PACKET_FANOUT_HASH |
255+ PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) {
256+ fprintf(stderr, "ERROR: joined group with wrong flag ro\n");
257+ exit(1);
258+ }
259+ if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) {
260+ fprintf(stderr, "ERROR: joined group with wrong mode\n");
261+ exit(1);
262+ }
263+ fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
264+ if (fds[1] == -1) {
265+ fprintf(stderr, "ERROR: failed to join group\n");
266+ exit(1);
267+ }
268+ if (close(fds[1]) || close(fds[0])) {
269+ fprintf(stderr, "ERROR: closing sockets\n");
270+ exit(1);
271+ }
272+}
273+
274+static void test_datapath(uint16_t typeflags,
275+ const int expect1[], const int expect2[])
276+{
277+ const int expect0[] = { 0, 0 };
278+ int fds[2], fds_udp[2][2];
279+
280+ fprintf(stderr, "test: datapath 0x%hx\n", typeflags);
281+
282+ fds[0] = sock_fanout_open(typeflags, 20);
283+ fds[1] = sock_fanout_open(typeflags, 20);
284+ if (fds[0] == -1 || fds[1] == -1) {
285+ fprintf(stderr, "ERROR: failed open\n");
286+ exit(1);
287+ }
288+ pair_udp_open(fds_udp[0], 8000);
289+ pair_udp_open(fds_udp[1], 8002);
290+ sock_fanout_read(fds, expect0);
291+
292+ /* Send data, but not enough to overflow a queue */
293+ pair_udp_send(fds_udp[0], 15);
294+ pair_udp_send(fds_udp[1], 5);
295+ sock_fanout_read(fds, expect1);
296+
297+ /* Send more data, overflow the queue */
298+ pair_udp_send(fds_udp[0], 15);
299+ /* TODO: ensure consistent order between expect1 and expect2 */
300+ sock_fanout_read(fds, expect2);
301+
302+ if (close(fds_udp[1][1]) || close(fds_udp[1][0]) ||
303+ close(fds_udp[0][1]) || close(fds_udp[0][0]) ||
304+ close(fds[1]) || close(fds[0])) {
305+ fprintf(stderr, "close datapath\n");
306+ exit(1);
307+ }
308+}
309+
310+int main(int argc, char **argv)
311+{
312+ const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } };
313+ const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } };
314+ const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } };
315+
316+ test_control_single();
317+ test_control_group();
318+
319+ test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]);
320+ test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER,
321+ expect_hash_rb[0], expect_hash_rb[1]);
322+ test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]);
323+
324+ printf("OK. All tests passed\n");
325+ return 0;
326+}
tools/testing/selftests/net-afpacket/run_afpackettests+16−0
@@ -0,0 +1,16 @@
1+#!/bin/sh
2+
3+if [ $(id -u) != 0 ]; then
4+ echo $msg must be run as root >&2
5+ exit 0
6+fi
7+
8+echo "--------------------"
9+echo "running psock_fanout test"
10+echo "--------------------"
11+./psock_fanout
12+if [ $? -ne 0 ]; then
13+ echo "[FAIL]"
14+else
15+ echo "[PASS]"
16+fi

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t