]> rtime.felk.cvut.cz Git - sojka/can-utils.git/commitdiff
Added '-d' option to support the receive packet drop counting introduced in
authorOliver Hartkopp <socketcan@hartkopp.net>
Fri, 15 Jan 2010 18:35:37 +0000 (18:35 +0000)
committerOliver Hartkopp <socketcan@hartkopp.net>
Fri, 15 Jan 2010 18:35:37 +0000 (18:35 +0000)
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
candump.c

index 5db72c8890e066f1304788260deb4d43cbdfa9be..91dfc043c47441cfe2c76f710157c18a493f0dcf 100644 (file)
--- 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
 
index ae203747fd126d024dcceb11cc6b865ec462eb85..2cf7ed42f308be235dc9c8f1ccf0ea544f11d745 100644 (file)
--- 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 <count>  (terminate after receiption of <count> CAN frames)\n");
        fprintf(stderr, "         -r <size>   (set socket receive buffer to <size>)\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: <ifname>[,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,
+                                      &timestamp_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]);