aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/networking/timestamping
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 21:40:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-08 21:40:54 -0400
commit35a9ad8af0bb0fa3525e6d0d20e32551d226f38e (patch)
tree15b4b33206818886d9cff371fd2163e073b70568 /Documentation/networking/timestamping
parentd5935b07da53f74726e2a65dd4281d0f2c70e5d4 (diff)
parent64b1f00a0830e1c53874067273a096b228d83d36 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Most notable changes in here: 1) By far the biggest accomplishment, thanks to a large range of contributors, is the addition of multi-send for transmit. This is the result of discussions back in Chicago, and the hard work of several individuals. Now, when the ->ndo_start_xmit() method of a driver sees skb->xmit_more as true, it can choose to defer the doorbell telling the driver to start processing the new TX queue entires. skb->xmit_more means that the generic networking is guaranteed to call the driver immediately with another SKB to send. There is logic added to the qdisc layer to dequeue multiple packets at a time, and the handling mis-predicted offloads in software is now done with no locks held. Finally, pktgen is extended to have a "burst" parameter that can be used to test a multi-send implementation. Several drivers have xmit_more support: i40e, igb, ixgbe, mlx4, virtio_net Adding support is almost trivial, so export more drivers to support this optimization soon. I want to thank, in no particular or implied order, Jesper Dangaard Brouer, Eric Dumazet, Alexander Duyck, Tom Herbert, Jamal Hadi Salim, John Fastabend, Florian Westphal, Daniel Borkmann, David Tat, Hannes Frederic Sowa, and Rusty Russell. 2) PTP and timestamping support in bnx2x, from Michal Kalderon. 3) Allow adjusting the rx_copybreak threshold for a driver via ethtool, and add rx_copybreak support to enic driver. From Govindarajulu Varadarajan. 4) Significant enhancements to the generic PHY layer and the bcm7xxx driver in particular (EEE support, auto power down, etc.) from Florian Fainelli. 5) Allow raw buffers to be used for flow dissection, allowing drivers to determine the optimal "linear pull" size for devices that DMA into pools of pages. The objective is to get exactly the necessary amount of headers into the linear SKB area pre-pulled, but no more. The new interface drivers use is eth_get_headlen(). From WANG Cong, with driver conversions (several had their own by-hand duplicated implementations) by Alexander Duyck and Eric Dumazet. 6) Support checksumming more smoothly and efficiently for encapsulations, and add "foo over UDP" facility. From Tom Herbert. 7) Add Broadcom SF2 switch driver to DSA layer, from Florian Fainelli. 8) eBPF now can load programs via a system call and has an extensive testsuite. Alexei Starovoitov and Daniel Borkmann. 9) Major overhaul of the packet scheduler to use RCU in several major areas such as the classifiers and rate estimators. From John Fastabend. 10) Add driver for Intel FM10000 Ethernet Switch, from Alexander Duyck. 11) Rearrange TCP_SKB_CB() to reduce cache line misses, from Eric Dumazet. 12) Add Datacenter TCP congestion control algorithm support, From Florian Westphal. 13) Reorganize sk_buff so that __copy_skb_header() is significantly faster. From Eric Dumazet" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1558 commits) netlabel: directly return netlbl_unlabel_genl_init() net: add netdev_txq_bql_{enqueue, complete}_prefetchw() helpers net: description of dma_cookie cause make xmldocs warning cxgb4: clean up a type issue cxgb4: potential shift wrapping bug i40e: skb->xmit_more support net: fs_enet: Add NAPI TX net: fs_enet: Remove non NAPI RX r8169:add support for RTL8168EP net_sched: copy exts->type in tcf_exts_change() wimax: convert printk to pr_foo() af_unix: remove 0 assignment on static ipv6: Do not warn for informational ICMP messages, regardless of type. Update Intel Ethernet Driver maintainers list bridge: Save frag_max_size between PRE_ROUTING and POST_ROUTING tipc: fix bug in multicast congestion handling net: better IFF_XMIT_DST_RELEASE support net/mlx4_en: remove NETDEV_TX_BUSY 3c59x: fix bad split of cpu_to_le32(pci_map_single()) net: bcmgenet: fix Tx ring priority programming ...
Diffstat (limited to 'Documentation/networking/timestamping')
-rw-r--r--Documentation/networking/timestamping/Makefile8
-rw-r--r--Documentation/networking/timestamping/txtimestamp.c469
2 files changed, 476 insertions, 1 deletions
diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile
index 52ac67da9315..8c20dfaa4d6e 100644
--- a/Documentation/networking/timestamping/Makefile
+++ b/Documentation/networking/timestamping/Makefile
@@ -1,8 +1,14 @@
1# To compile, from the source root
2#
3# make headers_install
4# make M=documentation
5
1# List of programs to build 6# List of programs to build
2hostprogs-y := hwtstamp_config timestamping 7hostprogs-y := hwtstamp_config timestamping txtimestamp
3 8
4# Tell kbuild to always build the programs 9# Tell kbuild to always build the programs
5always := $(hostprogs-y) 10always := $(hostprogs-y)
6 11
7HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include 12HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include
13HOSTCFLAGS_txtimestamp.o += -I$(objtree)/usr/include
8HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include 14HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include
diff --git a/Documentation/networking/timestamping/txtimestamp.c b/Documentation/networking/timestamping/txtimestamp.c
new file mode 100644
index 000000000000..b32fc2a07734
--- /dev/null
+++ b/Documentation/networking/timestamping/txtimestamp.c
@@ -0,0 +1,469 @@
1/*
2 * Copyright 2014 Google Inc.
3 * Author: willemb@google.com (Willem de Bruijn)
4 *
5 * Test software tx timestamping, including
6 *
7 * - SCHED, SND and ACK timestamps
8 * - RAW, UDP and TCP
9 * - IPv4 and IPv6
10 * - various packet sizes (to test GSO and TSO)
11 *
12 * Consult the command line arguments for help on running
13 * the various testcases.
14 *
15 * This test requires a dummy TCP server.
16 * A simple `nc6 [-u] -l -p $DESTPORT` will do
17 *
18 *
19 * This program is free software; you can redistribute it and/or modify it
20 * under the terms and conditions of the GNU General Public License,
21 * version 2, as published by the Free Software Foundation.
22 *
23 * This program is distributed in the hope it will be useful, but WITHOUT
24 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
26 * more details.
27 *
28 * You should have received a copy of the GNU General Public License along with
29 * this program; if not, write to the Free Software Foundation, Inc.,
30 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
31 */
32
33#include <arpa/inet.h>
34#include <asm/types.h>
35#include <error.h>
36#include <errno.h>
37#include <linux/errqueue.h>
38#include <linux/if_ether.h>
39#include <linux/net_tstamp.h>
40#include <netdb.h>
41#include <net/if.h>
42#include <netinet/in.h>
43#include <netinet/ip.h>
44#include <netinet/udp.h>
45#include <netinet/tcp.h>
46#include <netpacket/packet.h>
47#include <poll.h>
48#include <stdarg.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <sys/ioctl.h>
54#include <sys/select.h>
55#include <sys/socket.h>
56#include <sys/time.h>
57#include <sys/types.h>
58#include <time.h>
59#include <unistd.h>
60
61/* command line parameters */
62static int cfg_proto = SOCK_STREAM;
63static int cfg_ipproto = IPPROTO_TCP;
64static int cfg_num_pkts = 4;
65static int do_ipv4 = 1;
66static int do_ipv6 = 1;
67static int cfg_payload_len = 10;
68static uint16_t dest_port = 9000;
69
70static struct sockaddr_in daddr;
71static struct sockaddr_in6 daddr6;
72static struct timespec ts_prev;
73
74static void __print_timestamp(const char *name, struct timespec *cur,
75 uint32_t key, int payload_len)
76{
77 if (!(cur->tv_sec | cur->tv_nsec))
78 return;
79
80 fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)",
81 name, cur->tv_sec, cur->tv_nsec / 1000,
82 key, payload_len);
83
84 if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
85 int64_t cur_ms, prev_ms;
86
87 cur_ms = (long) cur->tv_sec * 1000 * 1000;
88 cur_ms += cur->tv_nsec / 1000;
89
90 prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
91 prev_ms += ts_prev.tv_nsec / 1000;
92
93 fprintf(stderr, " (%+ld us)", cur_ms - prev_ms);
94 }
95
96 ts_prev = *cur;
97 fprintf(stderr, "\n");
98}
99
100static void print_timestamp_usr(void)
101{
102 struct timespec ts;
103 struct timeval tv; /* avoid dependency on -lrt */
104
105 gettimeofday(&tv, NULL);
106 ts.tv_sec = tv.tv_sec;
107 ts.tv_nsec = tv.tv_usec * 1000;
108
109 __print_timestamp(" USR", &ts, 0, 0);
110}
111
112static void print_timestamp(struct scm_timestamping *tss, int tstype,
113 int tskey, int payload_len)
114{
115 const char *tsname;
116
117 switch (tstype) {
118 case SCM_TSTAMP_SCHED:
119 tsname = " ENQ";
120 break;
121 case SCM_TSTAMP_SND:
122 tsname = " SND";
123 break;
124 case SCM_TSTAMP_ACK:
125 tsname = " ACK";
126 break;
127 default:
128 error(1, 0, "unknown timestamp type: %u",
129 tstype);
130 }
131 __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
132}
133
134static void __poll(int fd)
135{
136 struct pollfd pollfd;
137 int ret;
138
139 memset(&pollfd, 0, sizeof(pollfd));
140 pollfd.fd = fd;
141 ret = poll(&pollfd, 1, 100);
142 if (ret != 1)
143 error(1, errno, "poll");
144}
145
146static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
147{
148 struct sock_extended_err *serr = NULL;
149 struct scm_timestamping *tss = NULL;
150 struct cmsghdr *cm;
151
152 for (cm = CMSG_FIRSTHDR(msg);
153 cm && cm->cmsg_len;
154 cm = CMSG_NXTHDR(msg, cm)) {
155 if (cm->cmsg_level == SOL_SOCKET &&
156 cm->cmsg_type == SCM_TIMESTAMPING) {
157 tss = (void *) CMSG_DATA(cm);
158 } else if ((cm->cmsg_level == SOL_IP &&
159 cm->cmsg_type == IP_RECVERR) ||
160 (cm->cmsg_level == SOL_IPV6 &&
161 cm->cmsg_type == IPV6_RECVERR)) {
162
163 serr = (void *) CMSG_DATA(cm);
164 if (serr->ee_errno != ENOMSG ||
165 serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
166 fprintf(stderr, "unknown ip error %d %d\n",
167 serr->ee_errno,
168 serr->ee_origin);
169 serr = NULL;
170 }
171 } else
172 fprintf(stderr, "unknown cmsg %d,%d\n",
173 cm->cmsg_level, cm->cmsg_type);
174 }
175
176 if (serr && tss)
177 print_timestamp(tss, serr->ee_info, serr->ee_data, payload_len);
178}
179
180static int recv_errmsg(int fd)
181{
182 static char ctrl[1024 /* overprovision*/];
183 static struct msghdr msg;
184 struct iovec entry;
185 static char *data;
186 int ret = 0;
187
188 data = malloc(cfg_payload_len);
189 if (!data)
190 error(1, 0, "malloc");
191
192 memset(&msg, 0, sizeof(msg));
193 memset(&entry, 0, sizeof(entry));
194 memset(ctrl, 0, sizeof(ctrl));
195
196 entry.iov_base = data;
197 entry.iov_len = cfg_payload_len;
198 msg.msg_iov = &entry;
199 msg.msg_iovlen = 1;
200 msg.msg_name = NULL;
201 msg.msg_namelen = 0;
202 msg.msg_control = ctrl;
203 msg.msg_controllen = sizeof(ctrl);
204
205 ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
206 if (ret == -1 && errno != EAGAIN)
207 error(1, errno, "recvmsg");
208
209 __recv_errmsg_cmsg(&msg, ret);
210
211 free(data);
212 return ret == -1;
213}
214
215static void do_test(int family, unsigned int opt)
216{
217 char *buf;
218 int fd, i, val, total_len;
219
220 if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) {
221 /* due to lack of checksum generation code */
222 fprintf(stderr, "test: skipping datagram over IPv6\n");
223 return;
224 }
225
226 total_len = cfg_payload_len;
227 if (cfg_proto == SOCK_RAW) {
228 total_len += sizeof(struct udphdr);
229 if (cfg_ipproto == IPPROTO_RAW)
230 total_len += sizeof(struct iphdr);
231 }
232
233 buf = malloc(total_len);
234 if (!buf)
235 error(1, 0, "malloc");
236
237 fd = socket(family, cfg_proto, cfg_ipproto);
238 if (fd < 0)
239 error(1, errno, "socket");
240
241 if (cfg_proto == SOCK_STREAM) {
242 val = 1;
243 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
244 (char*) &val, sizeof(val)))
245 error(1, 0, "setsockopt no nagle");
246
247 if (family == PF_INET) {
248 if (connect(fd, (void *) &daddr, sizeof(daddr)))
249 error(1, errno, "connect ipv4");
250 } else {
251 if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
252 error(1, errno, "connect ipv6");
253 }
254 }
255
256 opt |= SOF_TIMESTAMPING_SOFTWARE |
257 SOF_TIMESTAMPING_OPT_ID;
258 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
259 (char *) &opt, sizeof(opt)))
260 error(1, 0, "setsockopt timestamping");
261
262 for (i = 0; i < cfg_num_pkts; i++) {
263 memset(&ts_prev, 0, sizeof(ts_prev));
264 memset(buf, 'a' + i, total_len);
265 buf[total_len - 2] = '\n';
266 buf[total_len - 1] = '\0';
267
268 if (cfg_proto == SOCK_RAW) {
269 struct udphdr *udph;
270 int off = 0;
271
272 if (cfg_ipproto == IPPROTO_RAW) {
273 struct iphdr *iph = (void *) buf;
274
275 memset(iph, 0, sizeof(*iph));
276 iph->ihl = 5;
277 iph->version = 4;
278 iph->ttl = 2;
279 iph->daddr = daddr.sin_addr.s_addr;
280 iph->protocol = IPPROTO_UDP;
281 /* kernel writes saddr, csum, len */
282
283 off = sizeof(*iph);
284 }
285
286 udph = (void *) buf + off;
287 udph->source = ntohs(9000); /* random spoof */
288 udph->dest = ntohs(dest_port);
289 udph->len = ntohs(sizeof(*udph) + cfg_payload_len);
290 udph->check = 0; /* not allowed for IPv6 */
291 }
292
293 print_timestamp_usr();
294 if (cfg_proto != SOCK_STREAM) {
295 if (family == PF_INET)
296 val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
297 else
298 val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
299 } else {
300 val = send(fd, buf, cfg_payload_len, 0);
301 }
302 if (val != total_len)
303 error(1, errno, "send");
304
305 /* wait for all errors to be queued, else ACKs arrive OOO */
306 usleep(50 * 1000);
307
308 __poll(fd);
309
310 while (!recv_errmsg(fd)) {}
311 }
312
313 if (close(fd))
314 error(1, errno, "close");
315
316 free(buf);
317 usleep(400 * 1000);
318}
319
320static void __attribute__((noreturn)) usage(const char *filepath)
321{
322 fprintf(stderr, "\nUsage: %s [options] hostname\n"
323 "\nwhere options are:\n"
324 " -4: only IPv4\n"
325 " -6: only IPv6\n"
326 " -h: show this message\n"
327 " -l N: send N bytes at a time\n"
328 " -r: use raw\n"
329 " -R: use raw (IP_HDRINCL)\n"
330 " -p N: connect to port N\n"
331 " -u: use udp\n",
332 filepath);
333 exit(1);
334}
335
336static void parse_opt(int argc, char **argv)
337{
338 int proto_count = 0;
339 char c;
340
341 while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) {
342 switch (c) {
343 case '4':
344 do_ipv6 = 0;
345 break;
346 case '6':
347 do_ipv4 = 0;
348 break;
349 case 'r':
350 proto_count++;
351 cfg_proto = SOCK_RAW;
352 cfg_ipproto = IPPROTO_UDP;
353 break;
354 case 'R':
355 proto_count++;
356 cfg_proto = SOCK_RAW;
357 cfg_ipproto = IPPROTO_RAW;
358 break;
359 case 'u':
360 proto_count++;
361 cfg_proto = SOCK_DGRAM;
362 cfg_ipproto = IPPROTO_UDP;
363 break;
364 case 'l':
365 cfg_payload_len = strtoul(optarg, NULL, 10);
366 break;
367 case 'p':
368 dest_port = strtoul(optarg, NULL, 10);
369 break;
370 case 'h':
371 default:
372 usage(argv[0]);
373 }
374 }
375
376 if (!cfg_payload_len)
377 error(1, 0, "payload may not be nonzero");
378 if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
379 error(1, 0, "udp packet might exceed expected MTU");
380 if (!do_ipv4 && !do_ipv6)
381 error(1, 0, "pass -4 or -6, not both");
382 if (proto_count > 1)
383 error(1, 0, "pass -r, -R or -u, not multiple");
384
385 if (optind != argc - 1)
386 error(1, 0, "missing required hostname argument");
387}
388
389static void resolve_hostname(const char *hostname)
390{
391 struct addrinfo *addrs, *cur;
392 int have_ipv4 = 0, have_ipv6 = 0;
393
394 if (getaddrinfo(hostname, NULL, NULL, &addrs))
395 error(1, errno, "getaddrinfo");
396
397 cur = addrs;
398 while (cur && !have_ipv4 && !have_ipv6) {
399 if (!have_ipv4 && cur->ai_family == AF_INET) {
400 memcpy(&daddr, cur->ai_addr, sizeof(daddr));
401 daddr.sin_port = htons(dest_port);
402 have_ipv4 = 1;
403 }
404 else if (!have_ipv6 && cur->ai_family == AF_INET6) {
405 memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
406 daddr6.sin6_port = htons(dest_port);
407 have_ipv6 = 1;
408 }
409 cur = cur->ai_next;
410 }
411 if (addrs)
412 freeaddrinfo(addrs);
413
414 do_ipv4 &= have_ipv4;
415 do_ipv6 &= have_ipv6;
416}
417
418static void do_main(int family)
419{
420 fprintf(stderr, "family: %s\n",
421 family == PF_INET ? "INET" : "INET6");
422
423 fprintf(stderr, "test SND\n");
424 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
425
426 fprintf(stderr, "test ENQ\n");
427 do_test(family, SOF_TIMESTAMPING_TX_SCHED);
428
429 fprintf(stderr, "test ENQ + SND\n");
430 do_test(family, SOF_TIMESTAMPING_TX_SCHED |
431 SOF_TIMESTAMPING_TX_SOFTWARE);
432
433 if (cfg_proto == SOCK_STREAM) {
434 fprintf(stderr, "\ntest ACK\n");
435 do_test(family, SOF_TIMESTAMPING_TX_ACK);
436
437 fprintf(stderr, "\ntest SND + ACK\n");
438 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
439 SOF_TIMESTAMPING_TX_ACK);
440
441 fprintf(stderr, "\ntest ENQ + SND + ACK\n");
442 do_test(family, SOF_TIMESTAMPING_TX_SCHED |
443 SOF_TIMESTAMPING_TX_SOFTWARE |
444 SOF_TIMESTAMPING_TX_ACK);
445 }
446}
447
448const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
449
450int main(int argc, char **argv)
451{
452 if (argc == 1)
453 usage(argv[0]);
454
455 parse_opt(argc, argv);
456 resolve_hostname(argv[argc - 1]);
457
458 fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]);
459 fprintf(stderr, "payload: %u\n", cfg_payload_len);
460 fprintf(stderr, "server port: %u\n", dest_port);
461 fprintf(stderr, "\n");
462
463 if (do_ipv4)
464 do_main(PF_INET);
465 if (do_ipv6)
466 do_main(PF_INET6);
467
468 return 0;
469}