From 3c019ea61169d7f08d10d1fece95433c629eb4a6 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 15 Jan 2010 18:35:37 +0000 Subject: [PATCH] Added '-d' option to support the receive packet drop counting introduced in http://git.kernel.org/?p=linux/kernel/git/davem/net-next-2.6.git;a=commitdiff;h=3b885787ea4112eaa80945999ea0901bf742707f This is done by using recvmsg() instead of recvfrom() to allow the timestamp and the dropcounter to be received within one syscall. When the application (here 'candump') ist not fast enough to process the incomming CAN frames the frames are dropped in the socket receive queue. When this happens and '-d' is set, we get this info now: DROPCOUNT: dropped 1 CAN frame on 'xxx' socket (total drops 1) --- Makefile | 1 + candump.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 5db72c8..91dfc04 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ MAKEFLAGS = -k CFLAGS = -O2 -Wall -Wno-parentheses -I$(KERNELDIR)/include \ -fno-strict-aliasing \ + -DSO_RXQ_OVFL=40 \ -DPF_CAN=29 \ -DAF_CAN=PF_CAN diff --git a/candump.c b/candump.c index ae20374..2cf7ed4 100644 --- a/candump.c +++ b/candump.c @@ -89,6 +89,9 @@ const char col_on [MAXCOL][19] = {BLUE, RED, GREEN, BOLD, MAGENTA, CYAN}; const char col_off [] = ATTRESET; +static char *cmdlinename[MAXSOCK]; +static __u32 dropcnt[MAXSOCK]; +static __u32 last_dropcnt[MAXSOCK]; static char devname[MAXIFNAMES][IFNAMSIZ+1]; static int dindex[MAXIFNAMES]; static int max_devname_len; /* to prevent frazzled device name output */ @@ -116,6 +119,7 @@ void print_usage(char *prg) fprintf(stderr, " -L (use log file format on stdout)\n"); fprintf(stderr, " -n (terminate after receiption of CAN frames)\n"); fprintf(stderr, " -r (set socket receive buffer to )\n"); + fprintf(stderr, " -d (monitor dropped CAN frames)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Up to %d CAN interfaces with optional filter sets can be specified\n", MAXSOCK); fprintf(stderr, "on the commandline in the form: [,filter]*\n"); @@ -197,6 +201,7 @@ int main(int argc, char **argv) int s[MAXSOCK]; int bridge = 0; unsigned char timestamp = 0; + unsigned char dropmonitor = 0; unsigned char silent = SILENT_INI; unsigned char silentani = 0; unsigned char color = 0; @@ -209,6 +214,10 @@ int main(int argc, char **argv) int currmax, numfilter; char *ptr, *nptr; struct sockaddr_can addr; + char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; struct can_filter *rfilter; can_err_mask_t err_mask; struct can_frame frame; @@ -224,7 +233,7 @@ int main(int argc, char **argv) last_tv.tv_sec = 0; last_tv.tv_usec = 0; - while ((opt = getopt(argc, argv, "t:ciaSs:b:B:lLn:r:h?")) != -1) { + while ((opt = getopt(argc, argv, "t:ciaSs:b:B:ldLn:r:h?")) != -1) { switch (opt) { case 't': timestamp = optarg[0]; @@ -286,7 +295,7 @@ int main(int argc, char **argv) setsockopt(bridge, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); if (opt == 'B') { - int loopback = 0; + const int loopback = 0; setsockopt(bridge, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)); @@ -303,6 +312,10 @@ int main(int argc, char **argv) log = 1; break; + case 'd': + dropmonitor = 1; + break; + case 'L': logfrmt = 1; break; @@ -370,6 +383,8 @@ int main(int argc, char **argv) return 1; } + cmdlinename[i] = ptr; /* save pointer to cmdline name of this socket */ + if (nptr) nbytes = nptr - ptr; /* interface name is up the first ',' */ else @@ -446,7 +461,7 @@ int main(int argc, char **argv) } else if (sscanf(ptr, "#%lx", (long unsigned int *)&err_mask) != 1) { fprintf(stderr, "Error in filter option parsing: '%s'\n", ptr); - exit(1); + return 1; } } @@ -470,13 +485,13 @@ int main(int argc, char **argv) if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size)) < 0) { perror("setsockopt SO_RCVBUF"); - exit(1); + return 1; } if (getsockopt(s[i], SOL_SOCKET, SO_RCVBUF, &curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) { perror("getsockopt SO_RCVBUF"); - exit(1); + return 1; } /* Only print a warning the first time we detect the adjustment */ @@ -486,6 +501,28 @@ int main(int argc, char **argv) "adjusted due to /proc/sys/net/core/rmem_max.\n"); } + if (timestamp || log || logfrmt) { + + const int timestamp_on = 1; + + if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMP, + ×tamp_on, sizeof(timestamp_on)) < 0) { + perror("setsockopt SO_TIMESTAMP"); + return 1; + } + } + + if (dropmonitor) { + + const int dropmonitor_on = 1; + + if (setsockopt(s[i], SOL_SOCKET, SO_RXQ_OVFL, + &dropmonitor_on, sizeof(dropmonitor_on)) < 0) { + perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); + return 1; + } + } + if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; @@ -524,6 +561,13 @@ int main(int argc, char **argv) } } + /* these settings are static and can be held out of the hot path */ + iov.iov_base = &frame; + msg.msg_name = &addr; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctrlmsg; + while (running) { FD_ZERO(&rdfs); @@ -540,11 +584,15 @@ int main(int argc, char **argv) if (FD_ISSET(s[i], &rdfs)) { - socklen_t len = sizeof(addr); int idx; - nbytes = recvfrom(s[i], &frame, sizeof(struct can_frame), 0, - (struct sockaddr*)&addr, &len); + /* these settings may be modified by recvmsg() */ + iov.iov_len = sizeof(frame); + msg.msg_namelen = sizeof(addr); + msg.msg_controllen = sizeof(ctrlmsg); + msg.msg_flags = 0; + + nbytes = recvmsg(s[i], &msg, 0); if (nbytes < 0) { perror("read"); return 1; @@ -569,10 +617,35 @@ int main(int argc, char **argv) } } - if (timestamp || log || logfrmt) - if (ioctl(s[i], SIOCGSTAMP, &tv) < 0) - perror("SIOCGSTAMP"); + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg && (cmsg->cmsg_level == SOL_SOCKET); + cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_type == SO_TIMESTAMP) + tv = *(struct timeval *)CMSG_DATA(cmsg); + else if (cmsg->cmsg_type == SO_RXQ_OVFL) + dropcnt[i] = *(__u32 *)CMSG_DATA(cmsg); + } + + /* check for (unlikely) dropped frames on this specific socket */ + if (dropcnt[i] != last_dropcnt[i]) { + + __u32 frames; + + if (dropcnt[i] > last_dropcnt[i]) + frames = dropcnt[i] - last_dropcnt[i]; + else + frames = 4294967295U - last_dropcnt[i] + dropcnt[i]; /* 4294967295U == UINT32_MAX */ + if (silent != SILENT_ON) + printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", + frames, (frames > 1)?"s":"", cmdlinename[i], dropcnt[i]); + + if (log) + fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", + frames, (frames > 1)?"s":"", cmdlinename[i], dropcnt[i]); + + last_dropcnt[i] = dropcnt[i]; + } idx = idx2dindex(addr.can_ifindex, s[i]); -- 2.39.2