candump: use epoll_wait() instead of select()

Using epoll_wait() instead of select() gives higher
performance for listening on multiple interfaces.
Additionally, the read order has a higher chance
to resemble the true temporal order.
select() gives implicit priority to the lower index socket.

Signed-off-by: Jörg Hohensohn <joerg.hohensohn@gmx.de>
pull/264/head
Jörg Hohensohn 2020-12-03 17:36:32 +01:00 committed by Jörg Hohensohn
parent 6a14256323
commit 639498bc80
1 changed files with 220 additions and 210 deletions

110
candump.c
View File

@ -54,6 +54,7 @@
#include <unistd.h> #include <unistd.h>
#include <net/if.h> #include <net/if.h>
#include <sys/epoll.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
@ -98,9 +99,14 @@
const char col_on [MAXCOL][19] = {BLUE, RED, GREEN, BOLD, MAGENTA, CYAN}; const char col_on [MAXCOL][19] = {BLUE, RED, GREEN, BOLD, MAGENTA, CYAN};
const char col_off [] = ATTRESET; const char col_off [] = ATTRESET;
static char *cmdlinename[MAXSOCK]; struct if_info { /* bundled information per open socket */
static __u32 dropcnt[MAXSOCK]; int s; /* socket */
static __u32 last_dropcnt[MAXSOCK]; char *cmdlinename;
__u32 dropcnt;
__u32 last_dropcnt;
};
static struct if_info sock_info[MAXSOCK];
static char devname[MAXIFNAMES][IFNAMSIZ+1]; static char devname[MAXIFNAMES][IFNAMSIZ+1];
static int dindex[MAXIFNAMES]; static int dindex[MAXIFNAMES];
static int max_devname_len; /* to prevent frazzled device name output */ static int max_devname_len; /* to prevent frazzled device name output */
@ -216,8 +222,11 @@ int idx2dindex(int ifidx, int socket) {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
fd_set rdfs; int fd_epoll;
int s[MAXSOCK]; struct epoll_event events_pending[MAXSOCK];
struct epoll_event event_setup = {
.events = EPOLLIN, /* prepare the common part */
};
unsigned char timestamp = 0; unsigned char timestamp = 0;
unsigned char hwtimestamp = 0; unsigned char hwtimestamp = 0;
unsigned char down_causes_exit = 1; unsigned char down_causes_exit = 1;
@ -231,7 +240,7 @@ int main(int argc, char **argv)
unsigned char logfrmt = 0; unsigned char logfrmt = 0;
int count = 0; int count = 0;
int rcvbuf_size = 0; int rcvbuf_size = 0;
int opt, ret; int opt, num_events;
int currmax, numfilter; int currmax, numfilter;
int join_filter; int join_filter;
char *ptr, *nptr; char *ptr, *nptr;
@ -246,7 +255,7 @@ int main(int argc, char **argv)
int nbytes, i, maxdlen; int nbytes, i, maxdlen;
struct ifreq ifr; struct ifreq ifr;
struct timeval tv, last_tv; struct timeval tv, last_tv;
struct timeval timeout, timeout_config = { 0, 0 }, *timeout_current = NULL; int timeout_ms = -1; /* default to no timeout */
FILE *logfile = NULL; FILE *logfile = NULL;
signal(SIGTERM, sigterm); signal(SIGTERM, sigterm);
@ -342,14 +351,11 @@ int main(int argc, char **argv)
case 'T': case 'T':
errno = 0; errno = 0;
timeout_config.tv_usec = strtol(optarg, NULL, 0); timeout_ms = strtol(optarg, NULL, 0);
if (errno != 0) { if (errno != 0) {
print_usage(basename(argv[0])); print_usage(basename(argv[0]));
exit(1); exit(1);
} }
timeout_config.tv_sec = timeout_config.tv_usec / 1000;
timeout_config.tv_usec = (timeout_config.tv_usec % 1000) * 1000;
timeout_current = &timeout;
break; break;
default: default:
print_usage(basename(argv[0])); print_usage(basename(argv[0]));
@ -383,8 +389,14 @@ int main(int argc, char **argv)
return 1; return 1;
} }
for (i=0; i < currmax; i++) { fd_epoll = epoll_create(1);
if (fd_epoll < 0) {
perror("epoll_create");
return 1;
}
for (i = 0; i < currmax; i++) {
struct if_info* obj = &sock_info[i];
ptr = argv[optind+i]; ptr = argv[optind+i];
nptr = strchr(ptr, ','); nptr = strchr(ptr, ',');
@ -392,13 +404,19 @@ int main(int argc, char **argv)
printf("open %d '%s'.\n", i, ptr); printf("open %d '%s'.\n", i, ptr);
#endif #endif
s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW); obj->s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s[i] < 0) { if (obj->s < 0) {
perror("socket"); perror("socket");
return 1; return 1;
} }
cmdlinename[i] = ptr; /* save pointer to cmdline name of this socket */ event_setup.data.ptr = obj; /* remember the instance as private data */
if (epoll_ctl(fd_epoll, EPOLL_CTL_ADD, obj->s, &event_setup)) {
perror("failed to add socket to epoll");
return 1;
}
obj->cmdlinename = ptr; /* save pointer to cmdline name of this socket */
if (nptr) if (nptr)
nbytes = nptr - ptr; /* interface name is up the first ',' */ nbytes = nptr - ptr; /* interface name is up the first ',' */
@ -423,7 +441,7 @@ int main(int argc, char **argv)
#endif #endif
if (strcmp(ANYDEV, ifr.ifr_name) != 0) { if (strcmp(ANYDEV, ifr.ifr_name) != 0) {
if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0) { if (ioctl(obj->s, SIOCGIFINDEX, &ifr) < 0) {
perror("SIOCGIFINDEX"); perror("SIOCGIFINDEX");
exit(1); exit(1);
} }
@ -483,17 +501,17 @@ int main(int argc, char **argv)
} }
if (err_mask) if (err_mask)
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_ERR_FILTER, setsockopt(obj->s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&err_mask, sizeof(err_mask)); &err_mask, sizeof(err_mask));
if (join_filter && setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS, if (join_filter && setsockopt(obj->s, SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS,
&join_filter, sizeof(join_filter)) < 0) { &join_filter, sizeof(join_filter)) < 0) {
perror("setsockopt CAN_RAW_JOIN_FILTERS not supported by your Linux Kernel"); perror("setsockopt CAN_RAW_JOIN_FILTERS not supported by your Linux Kernel");
return 1; return 1;
} }
if (numfilter) if (numfilter)
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FILTER, setsockopt(obj->s, SOL_CAN_RAW, CAN_RAW_FILTER,
rfilter, numfilter * sizeof(struct can_filter)); rfilter, numfilter * sizeof(struct can_filter));
free(rfilter); free(rfilter);
@ -501,7 +519,7 @@ int main(int argc, char **argv)
} /* if (nptr) */ } /* if (nptr) */
/* try to switch the socket into CAN FD mode */ /* try to switch the socket into CAN FD mode */
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)); setsockopt(obj->s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on));
if (rcvbuf_size) { if (rcvbuf_size) {
@ -509,18 +527,18 @@ int main(int argc, char **argv)
socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size); socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size);
/* try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */ /* try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */
if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUFFORCE, if (setsockopt(obj->s, SOL_SOCKET, SO_RCVBUFFORCE,
&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { &rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
#ifdef DEBUG #ifdef DEBUG
printf("SO_RCVBUFFORCE failed so try SO_RCVBUF ...\n"); printf("SO_RCVBUFFORCE failed so try SO_RCVBUF ...\n");
#endif #endif
if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUF, if (setsockopt(obj->s, SOL_SOCKET, SO_RCVBUF,
&rcvbuf_size, sizeof(rcvbuf_size)) < 0) { &rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
perror("setsockopt SO_RCVBUF"); perror("setsockopt SO_RCVBUF");
return 1; return 1;
} }
if (getsockopt(s[i], SOL_SOCKET, SO_RCVBUF, if (getsockopt(obj->s, SOL_SOCKET, SO_RCVBUF,
&curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) { &curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) {
perror("getsockopt SO_RCVBUF"); perror("getsockopt SO_RCVBUF");
return 1; return 1;
@ -541,7 +559,7 @@ int main(int argc, char **argv)
SOF_TIMESTAMPING_RX_SOFTWARE | \ SOF_TIMESTAMPING_RX_SOFTWARE | \
SOF_TIMESTAMPING_RAW_HARDWARE); SOF_TIMESTAMPING_RAW_HARDWARE);
if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMPING, if (setsockopt(obj->s, SOL_SOCKET, SO_TIMESTAMPING,
&timestamping_flags, sizeof(timestamping_flags)) < 0) { &timestamping_flags, sizeof(timestamping_flags)) < 0) {
perror("setsockopt SO_TIMESTAMPING is not supported by your Linux kernel"); perror("setsockopt SO_TIMESTAMPING is not supported by your Linux kernel");
return 1; return 1;
@ -549,7 +567,7 @@ int main(int argc, char **argv)
} else { } else {
const int timestamp_on = 1; const int timestamp_on = 1;
if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMP, if (setsockopt(obj->s, SOL_SOCKET, SO_TIMESTAMP,
&timestamp_on, sizeof(timestamp_on)) < 0) { &timestamp_on, sizeof(timestamp_on)) < 0) {
perror("setsockopt SO_TIMESTAMP"); perror("setsockopt SO_TIMESTAMP");
return 1; return 1;
@ -561,14 +579,14 @@ int main(int argc, char **argv)
const int dropmonitor_on = 1; const int dropmonitor_on = 1;
if (setsockopt(s[i], SOL_SOCKET, SO_RXQ_OVFL, if (setsockopt(obj->s, SOL_SOCKET, SO_RXQ_OVFL,
&dropmonitor_on, sizeof(dropmonitor_on)) < 0) { &dropmonitor_on, sizeof(dropmonitor_on)) < 0) {
perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel");
return 1; return 1;
} }
} }
if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) { if (bind(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind"); perror("bind");
return 1; return 1;
} }
@ -615,23 +633,14 @@ int main(int argc, char **argv)
while (running) { while (running) {
FD_ZERO(&rdfs); if ((num_events = epoll_wait(fd_epoll, events_pending, currmax, timeout_ms)) <= 0) {
for (i=0; i<currmax; i++) //perror("epoll_wait");
FD_SET(s[i], &rdfs);
if (timeout_current)
*timeout_current = timeout_config;
if ((ret = select(s[currmax-1]+1, &rdfs, NULL, NULL, timeout_current)) <= 0) {
//perror("select");
running = 0; running = 0;
continue; continue;
} }
for (i=0; i<currmax; i++) { /* check all CAN RAW sockets */ for (i = 0; i < num_events; i++) { /* check waiting CAN RAW sockets */
struct if_info* obj = events_pending[i].data.ptr;
if (FD_ISSET(s[i], &rdfs)) {
int idx; int idx;
char *extra_info = ""; char *extra_info = "";
@ -641,8 +650,8 @@ int main(int argc, char **argv)
msg.msg_controllen = sizeof(ctrlmsg); msg.msg_controllen = sizeof(ctrlmsg);
msg.msg_flags = 0; msg.msg_flags = 0;
nbytes = recvmsg(s[i], &msg, 0); nbytes = recvmsg(obj->s, &msg, 0);
idx = idx2dindex(addr.can_ifindex, s[i]); idx = idx2dindex(addr.can_ifindex, obj->s);
if (nbytes < 0) { if (nbytes < 0) {
if ((errno == ENETDOWN) && !down_causes_exit) { if ((errno == ENETDOWN) && !down_causes_exit) {
@ -684,23 +693,23 @@ int main(int argc, char **argv)
tv.tv_sec = stamp[2].tv_sec; tv.tv_sec = stamp[2].tv_sec;
tv.tv_usec = stamp[2].tv_nsec/1000; tv.tv_usec = stamp[2].tv_nsec/1000;
} else if (cmsg->cmsg_type == SO_RXQ_OVFL) } else if (cmsg->cmsg_type == SO_RXQ_OVFL)
memcpy(&dropcnt[i], CMSG_DATA(cmsg), sizeof(__u32)); memcpy(&obj->dropcnt, CMSG_DATA(cmsg), sizeof(__u32));
} }
/* check for (unlikely) dropped frames on this specific socket */ /* check for (unlikely) dropped frames on this specific socket */
if (dropcnt[i] != last_dropcnt[i]) { if (obj->dropcnt != obj->last_dropcnt) {
__u32 frames = dropcnt[i] - last_dropcnt[i]; __u32 frames = obj->dropcnt - obj->last_dropcnt;
if (silent != SILENT_ON) if (silent != SILENT_ON)
printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]); frames, (frames > 1)?"s":"", devname[idx], obj->dropcnt);
if (log) if (log)
fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n", fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]); frames, (frames > 1)?"s":"", devname[idx], obj->dropcnt);
last_dropcnt[i] = dropcnt[i]; obj->last_dropcnt = obj->dropcnt;
} }
/* once we detected a EFF frame indent SFF frames accordingly */ /* once we detected a EFF frame indent SFF frames accordingly */
@ -805,7 +814,6 @@ int main(int argc, char **argv)
printf("%s", (color>1)?col_off:""); printf("%s", (color>1)?col_off:"");
printf("\n"); printf("\n");
}
out_fflush: out_fflush:
fflush(stdout); fflush(stdout);
@ -813,7 +821,9 @@ int main(int argc, char **argv)
} }
for (i=0; i<currmax; i++) for (i=0; i<currmax; i++)
close(s[i]); close(sock_info[i].s);
close(fd_epoll);
if (log) if (log)
fclose(logfile); fclose(logfile);