Add reference from /usr/src/linux
[can-benchmark.git] / recvmmsg / can_recvmmsg.c
1 /**************************************************************************/
2 /* CAN performance benchmark for recvmmsg()                               */
3 /* Copyright (C) 2013, 2014 Michal Sojka, DCE, FEE, CTU Prague            */
4 /* License: GPLv2                                                         */
5 /**************************************************************************/
6
7 #define _GNU_SOURCE
8 #include <sys/socket.h>
9 #include <linux/can.h>
10 #include <sys/ioctl.h>
11 #include <net/if.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <time.h>
17 #include <stdbool.h>
18
19 #define STRINGIFY(val) #val
20 #define TOSTRING(val) STRINGIFY(val)
21 #define CHECK(cmd) ({ int ret = (cmd); if (ret == -1) { perror(#cmd " line " TOSTRING(__LINE__)); exit(1); }; ret; })
22
23 char *dev = "vcan0";
24 int count = 10000;
25 enum { READWRITE, MMSG } method = READWRITE;
26 bool quiet = false;
27
28 /* Subtract the `struct timespec' values X and Y,
29    storing the result in RESULT (result = x - y).
30    Return 1 if the difference is negative, otherwise 0.  */
31
32 int
33 timespec_subtract (struct timespec *result,
34                    const struct timespec *x,
35                    const struct timespec *y)
36 {
37         int carry = (x->tv_nsec < y->tv_nsec);
38
39         result->tv_sec = x->tv_sec - y->tv_sec - carry;
40         result->tv_nsec = x->tv_nsec + carry*1000000000 - y->tv_nsec;
41
42 /*      printf("%ld.%09ld - %ld.%09ld = %ld.%09ld\n", */
43 /*             x->tv_sec, x->tv_nsec, */
44 /*             y->tv_sec, y->tv_nsec, */
45 /*             result->tv_sec, result->tv_nsec); */
46
47         /* Return 1 if result is negative. */
48         return x->tv_sec < y->tv_sec;
49 }
50
51
52 void benchmark()
53 {
54         int ss, sr;
55         struct sockaddr_can addr;
56         struct ifreq ifr;
57         struct can_frame cf;
58         int ret, i;
59         struct timespec ttx, trx, t1, t2;
60
61         ss = socket(PF_CAN, SOCK_RAW, CAN_RAW);
62         sr = socket(PF_CAN, SOCK_RAW, CAN_RAW);
63
64         strcpy(ifr.ifr_name, dev);
65         ioctl(ss, SIOCGIFINDEX, &ifr);
66
67         addr.can_family = AF_CAN;
68         addr.can_ifindex = ifr.ifr_ifindex;
69
70         bind(ss, (struct sockaddr *)&addr, sizeof(addr));
71         bind(sr, (struct sockaddr *)&addr, sizeof(addr));
72
73         int rcvbuf = 30 * count * sizeof(cf);
74         CHECK(setsockopt(sr, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof(rcvbuf)));
75
76         memset(&cf, 0, sizeof(cf));
77         cf.can_dlc = 8;
78
79         switch (method) {
80         case READWRITE: {
81                 if (!quiet) fprintf(stderr, "Sending %d frames via write()\n", count);
82                 clock_gettime(CLOCK_MONOTONIC, &t1);
83                 for (i = 0; i < count; i++) {
84                         ret = write(ss, &cf, sizeof(cf));
85                         if (ret != sizeof(cf)) {
86                                 perror("write");
87                                 exit(1);
88                         }
89                 }
90                 clock_gettime(CLOCK_MONOTONIC, &t2);
91                 break;
92         }
93         case MMSG: {
94                 if (!quiet) fprintf(stderr, "Sending %d frames via sendmmsg()\n", count);
95                 struct mmsghdr msgs[count];
96                 struct iovec iovecs[count];
97                 memset(msgs, 0, sizeof(msgs));
98                 memset(iovecs, 0, sizeof(iovecs));
99                 for (i = 0; i < count; i++) {
100                         iovecs[i].iov_base         = &cf;
101                         iovecs[i].iov_len          = sizeof(cf);
102                         msgs[i].msg_hdr.msg_iov    = &iovecs[i];
103                         msgs[i].msg_hdr.msg_iovlen = 1;
104                 }
105                 clock_gettime(CLOCK_MONOTONIC, &t1);
106                 for (i = 0; i < count; i += ret)
107                         ret = CHECK(sendmmsg(ss, &msgs[i], count - i, 0));
108                 clock_gettime(CLOCK_MONOTONIC, &t2);
109                 break;
110         }
111         }
112         timespec_subtract(&ttx, &t2, &t1);
113
114         switch (method) {
115         case READWRITE: {
116                 if (!quiet) fprintf(stderr, "Receiving %d frames with read()\n", count);
117                 clock_gettime(CLOCK_MONOTONIC, &t1);
118                 for (i = 0; i < count; i++) {
119                         //fprintf(stderr, "Receiving frame %d\r", i);
120                         ret = read(sr, &cf, sizeof(cf));
121                         if (ret != sizeof(cf)) {
122                                 perror("read");
123                                 exit(1);
124                         }
125                 }
126                 clock_gettime(CLOCK_MONOTONIC, &t2);
127                 //fprintf(stderr, "\n");
128                 break;
129         }
130         case MMSG: {
131                 if (!quiet) fprintf(stderr, "Receiving %d frames with recvmmsg()\n", count);
132                 struct mmsghdr msgs[count];
133                 struct iovec iovecs[count];
134                 char bufs[count][sizeof(struct can_frame)];
135
136                 memset(msgs, 0, sizeof(msgs));
137                 for (i = 0; i < count; i++) {
138                         iovecs[i].iov_base         = bufs[i];
139                         iovecs[i].iov_len          = sizeof(struct can_frame);
140                         msgs[i].msg_hdr.msg_iov    = &iovecs[i];
141                         msgs[i].msg_hdr.msg_iovlen = 1;
142                 }
143
144                 clock_gettime(CLOCK_MONOTONIC, &t1);
145                 ret = recvmmsg(sr, msgs, count, 0, NULL);
146                 clock_gettime(CLOCK_MONOTONIC, &t2);
147                 if (ret == -1) {
148                         perror("recvmmsg()");
149                         exit(1);
150                 } else if (ret != count) {
151                         fprintf(stderr, "Error: Only %d messages received\n", ret);
152                         exit(1);
153                 }
154                 break;
155         }
156         }
157         timespec_subtract(&trx, &t2, &t1);
158         printf("tx %ld rx %ld [us]\n", ttx.tv_sec*1000000 + ttx.tv_nsec/1000,
159                                        trx.tv_sec*1000000 + trx.tv_nsec/1000);
160 }
161
162 int main(int argc, char *argv[])
163 {
164         int opt;
165
166         while ((opt = getopt(argc, argv, "c:mrq")) != -1) {
167                 switch (opt) {
168                 case 'c':
169                         count = atoi(optarg);
170                         break;
171                 case 'm':
172                         method = MMSG;
173                         break;
174                 case 'r':
175                         method = READWRITE;
176                         break;
177                 case 'q':
178                         quiet = true;
179                         break;
180                 default: /* '?' */
181                         fprintf(stderr, "Usage: %s [-c <count>] [-r] [-m]  [interface]\n",
182                                 argv[0]);
183                         exit(EXIT_FAILURE);
184                 }
185         }
186
187         if (optind < argc)
188                 dev = argv[optind];
189
190         benchmark();
191
192         return 0;
193 }