diff options
author | Willem de Bruijn <willemb@google.com> | 2013-03-19 16:42:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-20 12:33:18 -0400 |
commit | 23a9072e3af0d9538e25837fb2b56bb94e4a8e67 (patch) | |
tree | 225d85e6ad2d380ce077a470dbf5a0a93e62ea62 /tools | |
parent | b44540ea024b9280c9be9d055f084e0956bcfa44 (diff) |
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 <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/net-afpacket/psock_fanout.c | 160 |
1 files changed, 120 insertions, 40 deletions
diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index af9b0196b0be..f765f09931bd 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c | |||
@@ -16,11 +16,11 @@ | |||
16 | * The test currently runs for | 16 | * The test currently runs for |
17 | * - PACKET_FANOUT_HASH | 17 | * - PACKET_FANOUT_HASH |
18 | * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER | 18 | * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER |
19 | * - PACKET_FANOUT_LB | ||
20 | * - PACKET_FANOUT_CPU | ||
19 | * - PACKET_FANOUT_ROLLOVER | 21 | * - PACKET_FANOUT_ROLLOVER |
20 | * | 22 | * |
21 | * Todo: | 23 | * Todo: |
22 | * - datapath: PACKET_FANOUT_LB | ||
23 | * - datapath: PACKET_FANOUT_CPU | ||
24 | * - functionality: PACKET_FANOUT_FLAG_DEFRAG | 24 | * - functionality: PACKET_FANOUT_FLAG_DEFRAG |
25 | * | 25 | * |
26 | * License (GPLv2): | 26 | * License (GPLv2): |
@@ -39,18 +39,23 @@ | |||
39 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 39 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
40 | */ | 40 | */ |
41 | 41 | ||
42 | #define _GNU_SOURCE /* for sched_setaffinity */ | ||
43 | |||
42 | #include <arpa/inet.h> | 44 | #include <arpa/inet.h> |
43 | #include <errno.h> | 45 | #include <errno.h> |
46 | #include <fcntl.h> | ||
44 | #include <linux/filter.h> | 47 | #include <linux/filter.h> |
45 | #include <linux/if_packet.h> | 48 | #include <linux/if_packet.h> |
46 | #include <net/ethernet.h> | 49 | #include <net/ethernet.h> |
47 | #include <netinet/ip.h> | 50 | #include <netinet/ip.h> |
48 | #include <netinet/udp.h> | 51 | #include <netinet/udp.h> |
49 | #include <fcntl.h> | 52 | #include <poll.h> |
53 | #include <sched.h> | ||
50 | #include <stdint.h> | 54 | #include <stdint.h> |
51 | #include <stdio.h> | 55 | #include <stdio.h> |
52 | #include <stdlib.h> | 56 | #include <stdlib.h> |
53 | #include <string.h> | 57 | #include <string.h> |
58 | #include <sys/mman.h> | ||
54 | #include <sys/socket.h> | 59 | #include <sys/socket.h> |
55 | #include <sys/stat.h> | 60 | #include <sys/stat.h> |
56 | #include <sys/types.h> | 61 | #include <sys/types.h> |
@@ -58,6 +63,8 @@ | |||
58 | 63 | ||
59 | #define DATA_LEN 100 | 64 | #define DATA_LEN 100 |
60 | #define DATA_CHAR 'a' | 65 | #define DATA_CHAR 'a' |
66 | #define RING_NUM_FRAMES 20 | ||
67 | #define PORT_BASE 8000 | ||
61 | 68 | ||
62 | static void pair_udp_open(int fds[], uint16_t port) | 69 | static void pair_udp_open(int fds[], uint16_t port) |
63 | { | 70 | { |
@@ -162,37 +169,55 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) | |||
162 | return -1; | 169 | return -1; |
163 | } | 170 | } |
164 | 171 | ||
165 | val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; | ||
166 | val *= num_packets; | ||
167 | /* hack: apparently, the above calculation is too small (TODO: fix) */ | ||
168 | val *= 3; | ||
169 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { | ||
170 | perror("setsockopt SO_RCVBUF"); | ||
171 | exit(1); | ||
172 | } | ||
173 | |||
174 | sock_fanout_setfilter(fd); | 172 | sock_fanout_setfilter(fd); |
175 | return fd; | 173 | return fd; |
176 | } | 174 | } |
177 | 175 | ||
178 | static void sock_fanout_read(int fds[], const int expect[]) | 176 | static char *sock_fanout_open_ring(int fd) |
179 | { | 177 | { |
180 | struct tpacket_stats stats; | 178 | struct tpacket_req req = { |
181 | socklen_t ssize; | 179 | .tp_block_size = getpagesize(), |
182 | int ret[2]; | 180 | .tp_frame_size = getpagesize(), |
181 | .tp_block_nr = RING_NUM_FRAMES, | ||
182 | .tp_frame_nr = RING_NUM_FRAMES, | ||
183 | }; | ||
184 | char *ring; | ||
183 | 185 | ||
184 | ssize = sizeof(stats); | 186 | if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, |
185 | if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { | 187 | sizeof(req))) { |
186 | perror("getsockopt statistics 0"); | 188 | perror("packetsock ring setsockopt"); |
187 | exit(1); | 189 | exit(1); |
188 | } | 190 | } |
189 | ret[0] = stats.tp_packets - stats.tp_drops; | 191 | |
190 | ssize = sizeof(stats); | 192 | ring = mmap(0, req.tp_block_size * req.tp_block_nr, |
191 | if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { | 193 | PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
192 | perror("getsockopt statistics 1"); | 194 | if (!ring) { |
195 | fprintf(stderr, "packetsock ring mmap\n"); | ||
193 | exit(1); | 196 | exit(1); |
194 | } | 197 | } |
195 | ret[1] = stats.tp_packets - stats.tp_drops; | 198 | |
199 | return ring; | ||
200 | } | ||
201 | |||
202 | static int sock_fanout_read_ring(int fd, void *ring) | ||
203 | { | ||
204 | struct tpacket_hdr *header = ring; | ||
205 | int count = 0; | ||
206 | |||
207 | while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { | ||
208 | count++; | ||
209 | header = ring + (count * getpagesize()); | ||
210 | } | ||
211 | |||
212 | return count; | ||
213 | } | ||
214 | |||
215 | static int sock_fanout_read(int fds[], char *rings[], const int expect[]) | ||
216 | { | ||
217 | int ret[2]; | ||
218 | |||
219 | ret[0] = sock_fanout_read_ring(fds[0], rings[0]); | ||
220 | ret[1] = sock_fanout_read_ring(fds[1], rings[1]); | ||
196 | 221 | ||
197 | fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", | 222 | fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", |
198 | ret[0], ret[1], expect[0], expect[1]); | 223 | ret[0], ret[1], expect[0], expect[1]); |
@@ -200,8 +225,10 @@ static void sock_fanout_read(int fds[], const int expect[]) | |||
200 | if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && | 225 | if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && |
201 | (!(ret[0] == expect[1] && ret[1] == expect[0]))) { | 226 | (!(ret[0] == expect[1] && ret[1] == expect[0]))) { |
202 | fprintf(stderr, "ERROR: incorrect queue lengths\n"); | 227 | fprintf(stderr, "ERROR: incorrect queue lengths\n"); |
203 | exit(1); | 228 | return 1; |
204 | } | 229 | } |
230 | |||
231 | return 0; | ||
205 | } | 232 | } |
206 | 233 | ||
207 | /* Test illegal mode + flag combination */ | 234 | /* Test illegal mode + flag combination */ |
@@ -253,11 +280,12 @@ static void test_control_group(void) | |||
253 | } | 280 | } |
254 | } | 281 | } |
255 | 282 | ||
256 | static void test_datapath(uint16_t typeflags, | 283 | static int test_datapath(uint16_t typeflags, int port_off, |
257 | const int expect1[], const int expect2[]) | 284 | const int expect1[], const int expect2[]) |
258 | { | 285 | { |
259 | const int expect0[] = { 0, 0 }; | 286 | const int expect0[] = { 0, 0 }; |
260 | int fds[2], fds_udp[2][2]; | 287 | char *rings[2]; |
288 | int fds[2], fds_udp[2][2], ret; | ||
261 | 289 | ||
262 | fprintf(stderr, "test: datapath 0x%hx\n", typeflags); | 290 | fprintf(stderr, "test: datapath 0x%hx\n", typeflags); |
263 | 291 | ||
@@ -267,41 +295,93 @@ static void test_datapath(uint16_t typeflags, | |||
267 | fprintf(stderr, "ERROR: failed open\n"); | 295 | fprintf(stderr, "ERROR: failed open\n"); |
268 | exit(1); | 296 | exit(1); |
269 | } | 297 | } |
270 | pair_udp_open(fds_udp[0], 8000); | 298 | rings[0] = sock_fanout_open_ring(fds[0]); |
271 | pair_udp_open(fds_udp[1], 8002); | 299 | rings[1] = sock_fanout_open_ring(fds[1]); |
272 | sock_fanout_read(fds, expect0); | 300 | pair_udp_open(fds_udp[0], PORT_BASE); |
301 | pair_udp_open(fds_udp[1], PORT_BASE + port_off); | ||
302 | sock_fanout_read(fds, rings, expect0); | ||
273 | 303 | ||
274 | /* Send data, but not enough to overflow a queue */ | 304 | /* Send data, but not enough to overflow a queue */ |
275 | pair_udp_send(fds_udp[0], 15); | 305 | pair_udp_send(fds_udp[0], 15); |
276 | pair_udp_send(fds_udp[1], 5); | 306 | pair_udp_send(fds_udp[1], 5); |
277 | sock_fanout_read(fds, expect1); | 307 | ret = sock_fanout_read(fds, rings, expect1); |
278 | 308 | ||
279 | /* Send more data, overflow the queue */ | 309 | /* Send more data, overflow the queue */ |
280 | pair_udp_send(fds_udp[0], 15); | 310 | pair_udp_send(fds_udp[0], 15); |
281 | /* TODO: ensure consistent order between expect1 and expect2 */ | 311 | /* TODO: ensure consistent order between expect1 and expect2 */ |
282 | sock_fanout_read(fds, expect2); | 312 | ret |= sock_fanout_read(fds, rings, expect2); |
283 | 313 | ||
314 | if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || | ||
315 | munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { | ||
316 | fprintf(stderr, "close rings\n"); | ||
317 | exit(1); | ||
318 | } | ||
284 | if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || | 319 | if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || |
285 | close(fds_udp[0][1]) || close(fds_udp[0][0]) || | 320 | close(fds_udp[0][1]) || close(fds_udp[0][0]) || |
286 | close(fds[1]) || close(fds[0])) { | 321 | close(fds[1]) || close(fds[0])) { |
287 | fprintf(stderr, "close datapath\n"); | 322 | fprintf(stderr, "close datapath\n"); |
288 | exit(1); | 323 | exit(1); |
289 | } | 324 | } |
325 | |||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | static int set_cpuaffinity(int cpuid) | ||
330 | { | ||
331 | cpu_set_t mask; | ||
332 | |||
333 | CPU_ZERO(&mask); | ||
334 | CPU_SET(cpuid, &mask); | ||
335 | if (sched_setaffinity(0, sizeof(mask), &mask)) { | ||
336 | if (errno != EINVAL) { | ||
337 | fprintf(stderr, "setaffinity %d\n", cpuid); | ||
338 | exit(1); | ||
339 | } | ||
340 | return 1; | ||
341 | } | ||
342 | |||
343 | return 0; | ||
290 | } | 344 | } |
291 | 345 | ||
292 | int main(int argc, char **argv) | 346 | int main(int argc, char **argv) |
293 | { | 347 | { |
294 | const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; | 348 | const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; |
295 | const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; | 349 | const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; |
296 | const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; | 350 | const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; |
351 | const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; | ||
352 | const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; | ||
353 | const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; | ||
354 | int port_off = 2, tries = 5, ret; | ||
297 | 355 | ||
298 | test_control_single(); | 356 | test_control_single(); |
299 | test_control_group(); | 357 | test_control_group(); |
300 | 358 | ||
301 | test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); | 359 | /* find a set of ports that do not collide onto the same socket */ |
302 | test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, | 360 | ret = test_datapath(PACKET_FANOUT_HASH, port_off, |
303 | expect_hash_rb[0], expect_hash_rb[1]); | 361 | expect_hash[0], expect_hash[1]); |
304 | test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); | 362 | while (ret && tries--) { |
363 | fprintf(stderr, "info: trying alternate ports (%d)\n", tries); | ||
364 | ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, | ||
365 | expect_hash[0], expect_hash[1]); | ||
366 | } | ||
367 | |||
368 | ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, | ||
369 | port_off, expect_hash_rb[0], expect_hash_rb[1]); | ||
370 | ret |= test_datapath(PACKET_FANOUT_LB, | ||
371 | port_off, expect_lb[0], expect_lb[1]); | ||
372 | ret |= test_datapath(PACKET_FANOUT_ROLLOVER, | ||
373 | port_off, expect_rb[0], expect_rb[1]); | ||
374 | |||
375 | set_cpuaffinity(0); | ||
376 | ret |= test_datapath(PACKET_FANOUT_CPU, port_off, | ||
377 | expect_cpu0[0], expect_cpu0[1]); | ||
378 | if (!set_cpuaffinity(1)) | ||
379 | /* TODO: test that choice alternates with previous */ | ||
380 | ret |= test_datapath(PACKET_FANOUT_CPU, port_off, | ||
381 | expect_cpu1[0], expect_cpu1[1]); | ||
382 | |||
383 | if (ret) | ||
384 | return 1; | ||
305 | 385 | ||
306 | printf("OK. All tests passed\n"); | 386 | printf("OK. All tests passed\n"); |
307 | return 0; | 387 | return 0; |