Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/vsock/vsock_test_zerocopy.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/* MSG_ZEROCOPY feature tests for vsock
3
*
4
* Copyright (C) 2023 SberDevices.
5
*
6
* Author: Arseniy Krasnov <[email protected]>
7
*/
8
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <string.h>
12
#include <sys/mman.h>
13
#include <unistd.h>
14
#include <poll.h>
15
#include <linux/errqueue.h>
16
#include <linux/kernel.h>
17
#include <errno.h>
18
19
#include "control.h"
20
#include "vsock_test_zerocopy.h"
21
#include "msg_zerocopy_common.h"
22
23
#ifndef PAGE_SIZE
24
#define PAGE_SIZE 4096
25
#endif
26
27
#define VSOCK_TEST_DATA_MAX_IOV 3
28
29
struct vsock_test_data {
30
/* This test case if for SOCK_STREAM only. */
31
bool stream_only;
32
/* Data must be zerocopied. This field is checked against
33
* field 'ee_code' of the 'struct sock_extended_err', which
34
* contains bit to detect that zerocopy transmission was
35
* fallbacked to copy mode.
36
*/
37
bool zerocopied;
38
/* Enable SO_ZEROCOPY option on the socket. Without enabled
39
* SO_ZEROCOPY, every MSG_ZEROCOPY transmission will behave
40
* like without MSG_ZEROCOPY flag.
41
*/
42
bool so_zerocopy;
43
/* 'errno' after 'sendmsg()' call. */
44
int sendmsg_errno;
45
/* Number of valid elements in 'vecs'. */
46
int vecs_cnt;
47
struct iovec vecs[VSOCK_TEST_DATA_MAX_IOV];
48
};
49
50
static struct vsock_test_data test_data_array[] = {
51
/* Last element has non-page aligned size. */
52
{
53
.zerocopied = true,
54
.so_zerocopy = true,
55
.sendmsg_errno = 0,
56
.vecs_cnt = 3,
57
{
58
{ NULL, PAGE_SIZE },
59
{ NULL, PAGE_SIZE },
60
{ NULL, 200 }
61
}
62
},
63
/* All elements have page aligned base and size. */
64
{
65
.zerocopied = true,
66
.so_zerocopy = true,
67
.sendmsg_errno = 0,
68
.vecs_cnt = 3,
69
{
70
{ NULL, PAGE_SIZE },
71
{ NULL, PAGE_SIZE * 2 },
72
{ NULL, PAGE_SIZE * 3 }
73
}
74
},
75
/* All elements have page aligned base and size. But
76
* data length is bigger than 64Kb.
77
*/
78
{
79
.zerocopied = true,
80
.so_zerocopy = true,
81
.sendmsg_errno = 0,
82
.vecs_cnt = 3,
83
{
84
{ NULL, PAGE_SIZE * 16 },
85
{ NULL, PAGE_SIZE * 16 },
86
{ NULL, PAGE_SIZE * 16 }
87
}
88
},
89
/* Middle element has both non-page aligned base and size. */
90
{
91
.zerocopied = true,
92
.so_zerocopy = true,
93
.sendmsg_errno = 0,
94
.vecs_cnt = 3,
95
{
96
{ NULL, PAGE_SIZE },
97
{ (void *)1, 100 },
98
{ NULL, PAGE_SIZE }
99
}
100
},
101
/* Middle element is unmapped. */
102
{
103
.zerocopied = false,
104
.so_zerocopy = true,
105
.sendmsg_errno = ENOMEM,
106
.vecs_cnt = 3,
107
{
108
{ NULL, PAGE_SIZE },
109
{ MAP_FAILED, PAGE_SIZE },
110
{ NULL, PAGE_SIZE }
111
}
112
},
113
/* Valid data, but SO_ZEROCOPY is off. This
114
* will trigger fallback to copy.
115
*/
116
{
117
.zerocopied = false,
118
.so_zerocopy = false,
119
.sendmsg_errno = 0,
120
.vecs_cnt = 1,
121
{
122
{ NULL, PAGE_SIZE }
123
}
124
},
125
/* Valid data, but message is bigger than peer's
126
* buffer, so this will trigger fallback to copy.
127
* This test is for SOCK_STREAM only, because
128
* for SOCK_SEQPACKET, 'sendmsg()' returns EMSGSIZE.
129
*/
130
{
131
.stream_only = true,
132
.zerocopied = false,
133
.so_zerocopy = true,
134
.sendmsg_errno = 0,
135
.vecs_cnt = 1,
136
{
137
{ NULL, 100 * PAGE_SIZE }
138
}
139
},
140
};
141
142
#define POLL_TIMEOUT_MS 100
143
144
static void test_client(const struct test_opts *opts,
145
const struct vsock_test_data *test_data,
146
bool sock_seqpacket)
147
{
148
struct pollfd fds = { 0 };
149
struct msghdr msg = { 0 };
150
ssize_t sendmsg_res;
151
struct iovec *iovec;
152
int fd;
153
154
if (sock_seqpacket)
155
fd = vsock_seqpacket_connect(opts->peer_cid, opts->peer_port);
156
else
157
fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
158
159
if (fd < 0) {
160
perror("connect");
161
exit(EXIT_FAILURE);
162
}
163
164
if (test_data->so_zerocopy)
165
enable_so_zerocopy_check(fd);
166
167
iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
168
169
msg.msg_iov = iovec;
170
msg.msg_iovlen = test_data->vecs_cnt;
171
172
errno = 0;
173
174
sendmsg_res = sendmsg(fd, &msg, MSG_ZEROCOPY);
175
if (errno != test_data->sendmsg_errno) {
176
fprintf(stderr, "expected 'errno' == %i, got %i\n",
177
test_data->sendmsg_errno, errno);
178
exit(EXIT_FAILURE);
179
}
180
181
if (!errno) {
182
if (sendmsg_res != iovec_bytes(iovec, test_data->vecs_cnt)) {
183
fprintf(stderr, "expected 'sendmsg()' == %li, got %li\n",
184
iovec_bytes(iovec, test_data->vecs_cnt),
185
sendmsg_res);
186
exit(EXIT_FAILURE);
187
}
188
}
189
190
fds.fd = fd;
191
fds.events = 0;
192
193
if (poll(&fds, 1, POLL_TIMEOUT_MS) < 0) {
194
perror("poll");
195
exit(EXIT_FAILURE);
196
}
197
198
if (fds.revents & POLLERR) {
199
vsock_recv_completion(fd, &test_data->zerocopied);
200
} else if (test_data->so_zerocopy && !test_data->sendmsg_errno) {
201
/* If we don't have data in the error queue, but
202
* SO_ZEROCOPY was enabled and 'sendmsg()' was
203
* successful - this is an error.
204
*/
205
fprintf(stderr, "POLLERR expected\n");
206
exit(EXIT_FAILURE);
207
}
208
209
if (!test_data->sendmsg_errno)
210
control_writeulong(iovec_hash_djb2(iovec, test_data->vecs_cnt));
211
else
212
control_writeulong(0);
213
214
control_writeln("DONE");
215
free_test_iovec(test_data->vecs, iovec, test_data->vecs_cnt);
216
close(fd);
217
}
218
219
void test_stream_msgzcopy_client(const struct test_opts *opts)
220
{
221
int i;
222
223
for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
224
test_client(opts, &test_data_array[i], false);
225
}
226
227
void test_seqpacket_msgzcopy_client(const struct test_opts *opts)
228
{
229
int i;
230
231
for (i = 0; i < ARRAY_SIZE(test_data_array); i++) {
232
if (test_data_array[i].stream_only)
233
continue;
234
235
test_client(opts, &test_data_array[i], true);
236
}
237
}
238
239
static void test_server(const struct test_opts *opts,
240
const struct vsock_test_data *test_data,
241
bool sock_seqpacket)
242
{
243
unsigned long remote_hash;
244
unsigned long local_hash;
245
ssize_t total_bytes_rec;
246
unsigned char *data;
247
size_t data_len;
248
int fd;
249
250
if (sock_seqpacket)
251
fd = vsock_seqpacket_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
252
else
253
fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
254
255
if (fd < 0) {
256
perror("accept");
257
exit(EXIT_FAILURE);
258
}
259
260
data_len = iovec_bytes(test_data->vecs, test_data->vecs_cnt);
261
262
data = malloc(data_len);
263
if (!data) {
264
perror("malloc");
265
exit(EXIT_FAILURE);
266
}
267
268
total_bytes_rec = 0;
269
270
while (total_bytes_rec != data_len) {
271
ssize_t bytes_rec;
272
273
bytes_rec = read(fd, data + total_bytes_rec,
274
data_len - total_bytes_rec);
275
if (bytes_rec <= 0)
276
break;
277
278
total_bytes_rec += bytes_rec;
279
}
280
281
if (test_data->sendmsg_errno == 0)
282
local_hash = hash_djb2(data, data_len);
283
else
284
local_hash = 0;
285
286
free(data);
287
288
/* Waiting for some result. */
289
remote_hash = control_readulong();
290
if (remote_hash != local_hash) {
291
fprintf(stderr, "hash mismatch\n");
292
exit(EXIT_FAILURE);
293
}
294
295
control_expectln("DONE");
296
close(fd);
297
}
298
299
void test_stream_msgzcopy_server(const struct test_opts *opts)
300
{
301
int i;
302
303
for (i = 0; i < ARRAY_SIZE(test_data_array); i++)
304
test_server(opts, &test_data_array[i], false);
305
}
306
307
void test_seqpacket_msgzcopy_server(const struct test_opts *opts)
308
{
309
int i;
310
311
for (i = 0; i < ARRAY_SIZE(test_data_array); i++) {
312
if (test_data_array[i].stream_only)
313
continue;
314
315
test_server(opts, &test_data_array[i], true);
316
}
317
}
318
319
void test_stream_msgzcopy_empty_errq_client(const struct test_opts *opts)
320
{
321
struct msghdr msg = { 0 };
322
char cmsg_data[128];
323
ssize_t res;
324
int fd;
325
326
fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
327
if (fd < 0) {
328
perror("connect");
329
exit(EXIT_FAILURE);
330
}
331
332
msg.msg_control = cmsg_data;
333
msg.msg_controllen = sizeof(cmsg_data);
334
335
res = recvmsg(fd, &msg, MSG_ERRQUEUE);
336
if (res != -1) {
337
fprintf(stderr, "expected 'recvmsg(2)' failure, got %zi\n",
338
res);
339
exit(EXIT_FAILURE);
340
}
341
342
control_writeln("DONE");
343
close(fd);
344
}
345
346
void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts)
347
{
348
int fd;
349
350
fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
351
if (fd < 0) {
352
perror("accept");
353
exit(EXIT_FAILURE);
354
}
355
356
control_expectln("DONE");
357
close(fd);
358
}
359
360