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