1 /**************************************************************************/
2 /* CAN user space gateway for performance benchmarks */
3 /* Copyright (C) 2013, 2014 Michal Sojka, DCE, FEE, CTU Prague */
5 /**************************************************************************/
8 #include <sys/socket.h>
10 #include <sys/ioctl.h>
18 #include <linux/if_ether.h>
19 #include <linux/if_packet.h>
22 #include <arpa/inet.h>
28 #define SO_BUSY_POLL 46
31 #define FRAME_SIZE 128
32 #define BLOCK_SIZE 4096
34 #define FRAME_NR (BLOCK_NR*(BLOCK_SIZE/FRAME_SIZE))
36 #define STRINGIFY(val) #val
37 #define TOSTRING(val) STRINGIFY(val)
38 #define CHECK(cmd) ({ int ret = (cmd); if (ret == -1) { perror(#cmd " line " TOSTRING(__LINE__)); exit(1); }; ret; })
39 #define CHECKPTR(cmd) ({ void *ptr = (cmd); if (ptr == (void*)-1) { perror(#cmd " line " TOSTRING(__LINE__)); exit(1); }; ptr; })
42 char *devout = "can1";
43 enum { IN_READ, IN_RECVMMSG, IN_MMAP, IN_MMAPBUSY } in_method = IN_READ;
44 enum { WRITE, OUT_MMAP } out_method = WRITE;
47 bool nonblocking = false;
56 #define VERBOSE(format, ...) do { if (!quiet) fprintf(stderr, format, ##__VA_ARGS__); } while (0)
63 enum in2out (*in_fn)(struct in_ctx *ctx, struct can_frame *cf);
72 int (*out_fn)(struct out_ctx *ctx, struct can_frame *cf);
82 printf("store:%d\nsend:%d\ntotal:%d\n",
83 stats.store, stats.send, stats.store + stats.send);
87 enum in2out in_read(struct in_ctx *ctx, struct can_frame *cf)
89 int ret = read(ctx->s, cf, sizeof(*cf));
90 if (nonblocking && ret == -1 && errno == EAGAIN)
92 if (ret != sizeof(*cf)) {
99 void init_read(struct in_ctx *ctx)
102 struct sockaddr_can addr;
105 s = CHECK(socket(PF_CAN, SOCK_RAW, CAN_RAW));
107 int rcvbuf = 25000; /* Limit rcvbuf to not have so big queueing latencies */
108 CHECK(setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)));
111 int flags = CHECK(fcntl(s, F_GETFL, 0));
112 CHECK(fcntl(s, F_SETFL, flags | O_NONBLOCK));
116 CHECK(setsockopt(s, SOL_SOCKET, SO_BUSY_POLL,
117 &busy_poll_us, sizeof(busy_poll_us)));
120 strncpy(ifr.ifr_name, devin, sizeof(ifr.ifr_name));
121 if (-1 == ioctl(s, SIOCGIFINDEX, &ifr)) {
126 addr.can_family = AF_CAN;
127 addr.can_ifindex = ifr.ifr_ifindex;
129 CHECK(bind(s, (struct sockaddr *)&addr, sizeof(addr)));
132 ctx->in_fn = in_read;
135 enum in2out in_packet_rx(struct in_ctx *ctx, struct can_frame *cf)
137 volatile struct tpacket2_hdr *hdr = ctx->ptr + ctx->current*FRAME_SIZE;
140 while (hdr->tp_status == TP_STATUS_KERNEL) {
141 if (in_method != IN_MMAPBUSY) {
142 struct pollfd pfd = {.fd = ctx->s, .revents = 0,
143 .events = POLLIN|POLLRDNORM|POLLERR };
144 ret = CHECK(poll(&pfd, 1, -1));
147 //struct sockaddr_ll *addr = (void*)hdr + TPACKET_ALIGN(ctx->hdrlen);
149 struct can_frame *cf_mmap = (void*)hdr + hdr->tp_mac;
152 ctx->current = (ctx->current + 1) % FRAME_NR;
153 hdr = ctx->ptr + ctx->current*FRAME_SIZE;
154 return (hdr->tp_status == TP_STATUS_KERNEL) ? SEND : STORE_ONLY;
158 void init_packet_rx(struct in_ctx *ctx)
161 struct sockaddr_ll my_addr;
164 s = CHECK(socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
167 CHECK(setsockopt(s, SOL_SOCKET, SO_BUSY_POLL,
168 &busy_poll_us, sizeof(busy_poll_us)));
171 int val = TPACKET_V2;
172 CHECK(setsockopt(s, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)));
173 socklen_t len = sizeof(ctx->hdrlen);
174 CHECK(getsockopt(s, SOL_PACKET, PACKET_HDRLEN, &ctx->hdrlen, &len));
176 strncpy (ifr.ifr_name, devin, sizeof(ifr.ifr_name));
177 CHECK(ioctl(s, SIOCGIFINDEX, &ifr));
179 my_addr.sll_family = AF_PACKET;
180 my_addr.sll_protocol = htons(ETH_P_ALL);
181 my_addr.sll_ifindex = ifr.ifr_ifindex;
183 CHECK(bind(s, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)));
185 struct tpacket_req req = {
186 .tp_block_size = BLOCK_SIZE,
187 .tp_frame_size = FRAME_SIZE,
188 .tp_block_nr = BLOCK_NR,
189 .tp_frame_nr = FRAME_NR,
191 CHECK(setsockopt(s, SOL_PACKET, PACKET_RX_RING, (char *)&req, sizeof(req)));
193 ctx->ptr = (char*)CHECKPTR(mmap(0, BLOCK_SIZE*BLOCK_NR, PROT_READ|PROT_WRITE, MAP_SHARED, s, 0));
196 ctx->in_fn = in_packet_rx;
200 int out_write(struct out_ctx *ctx, struct can_frame *cf)
202 if (ctx->from_in == NOP)
205 int ret = write(ctx->s, cf, sizeof(*cf));
206 if (ret != sizeof(*cf)) {
213 void init_write(struct out_ctx *ctx)
216 struct sockaddr_can addr;
219 s = CHECK(socket(PF_CAN, SOCK_RAW, CAN_RAW));
221 strncpy(ifr.ifr_name, devout, sizeof(ifr.ifr_name));
222 if (-1 == ioctl(s, SIOCGIFINDEX, &ifr)) {
227 addr.can_family = AF_CAN;
228 addr.can_ifindex = ifr.ifr_ifindex;
230 CHECK(bind(s, (struct sockaddr *)&addr, sizeof(addr)));
233 ctx->out_fn = out_write;
236 int out_packet_tx(struct out_ctx *ctx, struct can_frame *cf)
238 volatile struct tpacket2_hdr *hdr = ctx->ptr + ctx->current*FRAME_SIZE;
240 if (ctx->from_in == NOP) {
241 CHECK(send(ctx->s, NULL, 0, 0));
245 while (hdr->tp_status != TP_STATUS_AVAILABLE) {
246 CHECK(send(ctx->s, NULL, 0, 0));
249 //struct sockaddr_ll *addr = (void*)hdr + TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
250 struct can_frame *cf_mmap = (void*)hdr + TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
252 hdr->tp_len = sizeof(*cf);
253 hdr->tp_status = TP_STATUS_SEND_REQUEST;
254 ctx->current = (ctx->current + 1) % FRAME_NR;
256 if (ctx->from_in == SEND){
257 CHECK(send(ctx->s, NULL, 0, 0));
264 void init_packet_tx(struct out_ctx *ctx)
267 struct sockaddr_ll my_addr;
270 s = CHECK(socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
272 int val = TPACKET_V2;
273 CHECK(setsockopt(s, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)));
274 socklen_t len = sizeof(ctx->hdrlen);
275 CHECK(getsockopt(s, SOL_PACKET, PACKET_HDRLEN, &ctx->hdrlen, &len));
277 strncpy (ifr.ifr_name, devout, sizeof(ifr.ifr_name));
278 CHECK(ioctl(s, SIOCGIFINDEX, &ifr));
280 my_addr.sll_family = AF_PACKET;
281 my_addr.sll_protocol = htons(ETH_P_CAN);
282 my_addr.sll_ifindex = ifr.ifr_ifindex;
284 CHECK(bind(s, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)));
286 struct tpacket_req req = {
287 .tp_block_size = BLOCK_SIZE,
288 .tp_frame_size = FRAME_SIZE,
289 .tp_block_nr = BLOCK_NR,
290 .tp_frame_nr = FRAME_NR,
292 CHECK(setsockopt(s, SOL_PACKET, PACKET_TX_RING, (char *)&req, sizeof(req)));
294 ctx->ptr = (char*)CHECKPTR(mmap(0, BLOCK_SIZE*BLOCK_NR, PROT_READ|PROT_WRITE, MAP_SHARED, s, 0));
297 ctx->out_fn = out_packet_tx;
301 void init_in(struct in_ctx *in)
303 memset(in, 0, sizeof(*in));
313 fprintf(stderr, "Unknown \"in method\" %d\n", in_method);
318 int in(struct in_ctx *ctx, struct can_frame *cf)
320 return ctx->in_fn(ctx, cf);
323 void init_out(struct out_ctx *out)
325 memset(out, 0, sizeof(*out));
326 switch (out_method) {
327 case WRITE: init_write(out); break;
328 case OUT_MMAP: init_packet_tx(out); break;
330 fprintf(stderr, "Unknown \"out method\" %d\n", out_method);
335 int out(struct out_ctx *ctx, struct can_frame *cf)
337 return ctx->out_fn(ctx, cf);
349 VERBOSE("UGW started\n");
352 oc.from_in = in(&ic, &cf);
353 switch (oc.from_in) {
354 case SEND: stats.send++; break;
355 case STORE_ONLY: stats.store++; break;
363 int main(int argc, char *argv[])
367 while ((opt = getopt(argc, argv, "b:nqr:t:")) != -1) {
370 busy_poll_us = atoi(optarg);
379 if (strcmp(optarg, "read") == 0)
381 else if (strcmp(optarg, "mmap") == 0)
383 else if (strcmp(optarg, "mmapbusy") == 0)
384 in_method = IN_MMAPBUSY;
386 fprintf(stderr, "Unsuported RX method: %s\n", optarg);
391 if (strcmp(optarg, "write") == 0)
393 else if (strcmp(optarg, "mmap") == 0)
394 out_method = OUT_MMAP;
396 fprintf(stderr, "Unsuported TX method: %s\n", optarg);
401 fprintf(stderr, "Usage: %s [in_interface out_interface]\n",
408 devin = argv[optind];
410 devout = argv[optind+1];
412 signal(SIGINT, sigint);