~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/vsock/vsock_perf.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * vsock_perf - benchmark utility for vsock.
  4  *
  5  * Copyright (C) 2022 SberDevices.
  6  *
  7  * Author: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
  8  */
  9 #include <getopt.h>
 10 #include <stdio.h>
 11 #include <stdlib.h>
 12 #include <stdbool.h>
 13 #include <string.h>
 14 #include <errno.h>
 15 #include <unistd.h>
 16 #include <time.h>
 17 #include <stdint.h>
 18 #include <poll.h>
 19 #include <sys/socket.h>
 20 #include <linux/vm_sockets.h>
 21 #include <sys/mman.h>
 22 
 23 #include "msg_zerocopy_common.h"
 24 
 25 #define DEFAULT_BUF_SIZE_BYTES  (128 * 1024)
 26 #define DEFAULT_TO_SEND_BYTES   (64 * 1024)
 27 #define DEFAULT_VSOCK_BUF_BYTES (256 * 1024)
 28 #define DEFAULT_RCVLOWAT_BYTES  1
 29 #define DEFAULT_PORT            1234
 30 
 31 #define BYTES_PER_GB            (1024 * 1024 * 1024ULL)
 32 #define NSEC_PER_SEC            (1000000000ULL)
 33 
 34 static unsigned int port = DEFAULT_PORT;
 35 static unsigned long buf_size_bytes = DEFAULT_BUF_SIZE_BYTES;
 36 static unsigned long vsock_buf_bytes = DEFAULT_VSOCK_BUF_BYTES;
 37 static bool zerocopy;
 38 
 39 static void error(const char *s)
 40 {
 41         perror(s);
 42         exit(EXIT_FAILURE);
 43 }
 44 
 45 static time_t current_nsec(void)
 46 {
 47         struct timespec ts;
 48 
 49         if (clock_gettime(CLOCK_REALTIME, &ts))
 50                 error("clock_gettime");
 51 
 52         return (ts.tv_sec * NSEC_PER_SEC) + ts.tv_nsec;
 53 }
 54 
 55 /* From lib/cmdline.c. */
 56 static unsigned long memparse(const char *ptr)
 57 {
 58         char *endptr;
 59 
 60         unsigned long long ret = strtoull(ptr, &endptr, 0);
 61 
 62         switch (*endptr) {
 63         case 'E':
 64         case 'e':
 65                 ret <<= 10;
 66         case 'P':
 67         case 'p':
 68                 ret <<= 10;
 69         case 'T':
 70         case 't':
 71                 ret <<= 10;
 72         case 'G':
 73         case 'g':
 74                 ret <<= 10;
 75         case 'M':
 76         case 'm':
 77                 ret <<= 10;
 78         case 'K':
 79         case 'k':
 80                 ret <<= 10;
 81                 endptr++;
 82         default:
 83                 break;
 84         }
 85 
 86         return ret;
 87 }
 88 
 89 static void vsock_increase_buf_size(int fd)
 90 {
 91         if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
 92                        &vsock_buf_bytes, sizeof(vsock_buf_bytes)))
 93                 error("setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
 94 
 95         if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
 96                        &vsock_buf_bytes, sizeof(vsock_buf_bytes)))
 97                 error("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
 98 }
 99 
100 static int vsock_connect(unsigned int cid, unsigned int port)
101 {
102         union {
103                 struct sockaddr sa;
104                 struct sockaddr_vm svm;
105         } addr = {
106                 .svm = {
107                         .svm_family = AF_VSOCK,
108                         .svm_port = port,
109                         .svm_cid = cid,
110                 },
111         };
112         int fd;
113 
114         fd = socket(AF_VSOCK, SOCK_STREAM, 0);
115 
116         if (fd < 0) {
117                 perror("socket");
118                 return -1;
119         }
120 
121         if (connect(fd, &addr.sa, sizeof(addr.svm)) < 0) {
122                 perror("connect");
123                 close(fd);
124                 return -1;
125         }
126 
127         return fd;
128 }
129 
130 static float get_gbps(unsigned long bits, time_t ns_delta)
131 {
132         return ((float)bits / 1000000000ULL) /
133                ((float)ns_delta / NSEC_PER_SEC);
134 }
135 
136 static void run_receiver(unsigned long rcvlowat_bytes)
137 {
138         unsigned int read_cnt;
139         time_t rx_begin_ns;
140         time_t in_read_ns;
141         size_t total_recv;
142         int client_fd;
143         char *data;
144         int fd;
145         union {
146                 struct sockaddr sa;
147                 struct sockaddr_vm svm;
148         } addr = {
149                 .svm = {
150                         .svm_family = AF_VSOCK,
151                         .svm_port = port,
152                         .svm_cid = VMADDR_CID_ANY,
153                 },
154         };
155         union {
156                 struct sockaddr sa;
157                 struct sockaddr_vm svm;
158         } clientaddr;
159 
160         socklen_t clientaddr_len = sizeof(clientaddr.svm);
161 
162         printf("Run as receiver\n");
163         printf("Listen port %u\n", port);
164         printf("RX buffer %lu bytes\n", buf_size_bytes);
165         printf("vsock buffer %lu bytes\n", vsock_buf_bytes);
166         printf("SO_RCVLOWAT %lu bytes\n", rcvlowat_bytes);
167 
168         fd = socket(AF_VSOCK, SOCK_STREAM, 0);
169 
170         if (fd < 0)
171                 error("socket");
172 
173         if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0)
174                 error("bind");
175 
176         if (listen(fd, 1) < 0)
177                 error("listen");
178 
179         client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
180 
181         if (client_fd < 0)
182                 error("accept");
183 
184         vsock_increase_buf_size(client_fd);
185 
186         if (setsockopt(client_fd, SOL_SOCKET, SO_RCVLOWAT,
187                        &rcvlowat_bytes,
188                        sizeof(rcvlowat_bytes)))
189                 error("setsockopt(SO_RCVLOWAT)");
190 
191         data = malloc(buf_size_bytes);
192 
193         if (!data) {
194                 fprintf(stderr, "'malloc()' failed\n");
195                 exit(EXIT_FAILURE);
196         }
197 
198         read_cnt = 0;
199         in_read_ns = 0;
200         total_recv = 0;
201         rx_begin_ns = current_nsec();
202 
203         while (1) {
204                 struct pollfd fds = { 0 };
205 
206                 fds.fd = client_fd;
207                 fds.events = POLLIN | POLLERR |
208                              POLLHUP | POLLRDHUP;
209 
210                 if (poll(&fds, 1, -1) < 0)
211                         error("poll");
212 
213                 if (fds.revents & POLLERR) {
214                         fprintf(stderr, "'poll()' error\n");
215                         exit(EXIT_FAILURE);
216                 }
217 
218                 if (fds.revents & POLLIN) {
219                         ssize_t bytes_read;
220                         time_t t;
221 
222                         t = current_nsec();
223                         bytes_read = read(fds.fd, data, buf_size_bytes);
224                         in_read_ns += (current_nsec() - t);
225                         read_cnt++;
226 
227                         if (!bytes_read)
228                                 break;
229 
230                         if (bytes_read < 0) {
231                                 perror("read");
232                                 exit(EXIT_FAILURE);
233                         }
234 
235                         total_recv += bytes_read;
236                 }
237 
238                 if (fds.revents & (POLLHUP | POLLRDHUP))
239                         break;
240         }
241 
242         printf("total bytes received: %zu\n", total_recv);
243         printf("rx performance: %f Gbits/s\n",
244                get_gbps(total_recv * 8, current_nsec() - rx_begin_ns));
245         printf("total time in 'read()': %f sec\n", (float)in_read_ns / NSEC_PER_SEC);
246         printf("average time in 'read()': %f ns\n", (float)in_read_ns / read_cnt);
247         printf("POLLIN wakeups: %i\n", read_cnt);
248 
249         free(data);
250         close(client_fd);
251         close(fd);
252 }
253 
254 static void run_sender(int peer_cid, unsigned long to_send_bytes)
255 {
256         time_t tx_begin_ns;
257         time_t tx_total_ns;
258         size_t total_send;
259         time_t time_in_send;
260         void *data;
261         int fd;
262 
263         if (zerocopy)
264                 printf("Run as sender MSG_ZEROCOPY\n");
265         else
266                 printf("Run as sender\n");
267 
268         printf("Connect to %i:%u\n", peer_cid, port);
269         printf("Send %lu bytes\n", to_send_bytes);
270         printf("TX buffer %lu bytes\n", buf_size_bytes);
271 
272         fd = vsock_connect(peer_cid, port);
273 
274         if (fd < 0)
275                 exit(EXIT_FAILURE);
276 
277         if (zerocopy) {
278                 enable_so_zerocopy(fd);
279 
280                 data = mmap(NULL, buf_size_bytes, PROT_READ | PROT_WRITE,
281                             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
282                 if (data == MAP_FAILED) {
283                         perror("mmap");
284                         exit(EXIT_FAILURE);
285                 }
286         } else {
287                 data = malloc(buf_size_bytes);
288 
289                 if (!data) {
290                         fprintf(stderr, "'malloc()' failed\n");
291                         exit(EXIT_FAILURE);
292                 }
293         }
294 
295         memset(data, 0, buf_size_bytes);
296         total_send = 0;
297         time_in_send = 0;
298         tx_begin_ns = current_nsec();
299 
300         while (total_send < to_send_bytes) {
301                 ssize_t sent;
302                 size_t rest_bytes;
303                 time_t before;
304 
305                 rest_bytes = to_send_bytes - total_send;
306 
307                 before = current_nsec();
308                 sent = send(fd, data, (rest_bytes > buf_size_bytes) ?
309                             buf_size_bytes : rest_bytes,
310                             zerocopy ? MSG_ZEROCOPY : 0);
311                 time_in_send += (current_nsec() - before);
312 
313                 if (sent <= 0)
314                         error("write");
315 
316                 total_send += sent;
317 
318                 if (zerocopy) {
319                         struct pollfd fds = { 0 };
320 
321                         fds.fd = fd;
322 
323                         if (poll(&fds, 1, -1) < 0) {
324                                 perror("poll");
325                                 exit(EXIT_FAILURE);
326                         }
327 
328                         if (!(fds.revents & POLLERR)) {
329                                 fprintf(stderr, "POLLERR expected\n");
330                                 exit(EXIT_FAILURE);
331                         }
332 
333                         vsock_recv_completion(fd, NULL);
334                 }
335         }
336 
337         tx_total_ns = current_nsec() - tx_begin_ns;
338 
339         printf("total bytes sent: %zu\n", total_send);
340         printf("tx performance: %f Gbits/s\n",
341                get_gbps(total_send * 8, time_in_send));
342         printf("total time in tx loop: %f sec\n",
343                (float)tx_total_ns / NSEC_PER_SEC);
344         printf("time in 'send()': %f sec\n",
345                (float)time_in_send / NSEC_PER_SEC);
346 
347         close(fd);
348 
349         if (zerocopy)
350                 munmap(data, buf_size_bytes);
351         else
352                 free(data);
353 }
354 
355 static const char optstring[] = "";
356 static const struct option longopts[] = {
357         {
358                 .name = "help",
359                 .has_arg = no_argument,
360                 .val = 'H',
361         },
362         {
363                 .name = "sender",
364                 .has_arg = required_argument,
365                 .val = 'S',
366         },
367         {
368                 .name = "port",
369                 .has_arg = required_argument,
370                 .val = 'P',
371         },
372         {
373                 .name = "bytes",
374                 .has_arg = required_argument,
375                 .val = 'M',
376         },
377         {
378                 .name = "buf-size",
379                 .has_arg = required_argument,
380                 .val = 'B',
381         },
382         {
383                 .name = "vsk-size",
384                 .has_arg = required_argument,
385                 .val = 'V',
386         },
387         {
388                 .name = "rcvlowat",
389                 .has_arg = required_argument,
390                 .val = 'R',
391         },
392         {
393                 .name = "zerocopy",
394                 .has_arg = no_argument,
395                 .val = 'Z',
396         },
397         {},
398 };
399 
400 static void usage(void)
401 {
402         printf("Usage: ./vsock_perf [--help] [options]\n"
403                "\n"
404                "This is benchmarking utility, to test vsock performance.\n"
405                "It runs in two modes: sender or receiver. In sender mode, it\n"
406                "connects to the specified CID and starts data transmission.\n"
407                "\n"
408                "Options:\n"
409                "  --help                        This message\n"
410                "  --sender   <cid>              Sender mode (receiver default)\n"
411                "                                <cid> of the receiver to connect to\n"
412                "  --zerocopy                    Enable zerocopy (for sender mode only)\n"
413                "  --port     <port>             Port (default %d)\n"
414                "  --bytes    <bytes>KMG         Bytes to send (default %d)\n"
415                "  --buf-size <bytes>KMG         Data buffer size (default %d). In sender mode\n"
416                "                                it is the buffer size, passed to 'write()'. In\n"
417                "                                receiver mode it is the buffer size passed to 'read()'.\n"
418                "  --vsk-size <bytes>KMG         Socket buffer size (default %d)\n"
419                "  --rcvlowat <bytes>KMG         SO_RCVLOWAT value (default %d)\n"
420                "\n", DEFAULT_PORT, DEFAULT_TO_SEND_BYTES,
421                DEFAULT_BUF_SIZE_BYTES, DEFAULT_VSOCK_BUF_BYTES,
422                DEFAULT_RCVLOWAT_BYTES);
423         exit(EXIT_FAILURE);
424 }
425 
426 static long strtolx(const char *arg)
427 {
428         long value;
429         char *end;
430 
431         value = strtol(arg, &end, 10);
432 
433         if (end != arg + strlen(arg))
434                 usage();
435 
436         return value;
437 }
438 
439 int main(int argc, char **argv)
440 {
441         unsigned long to_send_bytes = DEFAULT_TO_SEND_BYTES;
442         unsigned long rcvlowat_bytes = DEFAULT_RCVLOWAT_BYTES;
443         int peer_cid = -1;
444         bool sender = false;
445 
446         while (1) {
447                 int opt = getopt_long(argc, argv, optstring, longopts, NULL);
448 
449                 if (opt == -1)
450                         break;
451 
452                 switch (opt) {
453                 case 'V': /* Peer buffer size. */
454                         vsock_buf_bytes = memparse(optarg);
455                         break;
456                 case 'R': /* SO_RCVLOWAT value. */
457                         rcvlowat_bytes = memparse(optarg);
458                         break;
459                 case 'P': /* Port to connect to. */
460                         port = strtolx(optarg);
461                         break;
462                 case 'M': /* Bytes to send. */
463                         to_send_bytes = memparse(optarg);
464                         break;
465                 case 'B': /* Size of rx/tx buffer. */
466                         buf_size_bytes = memparse(optarg);
467                         break;
468                 case 'S': /* Sender mode. CID to connect to. */
469                         peer_cid = strtolx(optarg);
470                         sender = true;
471                         break;
472                 case 'H': /* Help. */
473                         usage();
474                         break;
475                 case 'Z': /* Zerocopy. */
476                         zerocopy = true;
477                         break;
478                 default:
479                         usage();
480                 }
481         }
482 
483         if (!sender)
484                 run_receiver(rcvlowat_bytes);
485         else
486                 run_sender(peer_cid, to_send_bytes);
487 
488         return 0;
489 }
490 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php