Skip to content

Commit 5e02c7d

Browse files
Arseniy KrasnovNipaLocal
authored andcommitted
vsock/test: SO_RCVLOWAT + deferred credit update test
Test which checks, that updating SO_RCVLOWAT value also sends credit update message. Otherwise mutual hungup may happen when receiver didn't send credit update and then calls 'poll()' with non default SO_RCVLOWAT value (e.g. waiting enough bytes to read), while sender waits for free space at receiver's side. Important thing is that this test relies on kernel's define for maximum packet size for virtio transport and this value is not exported to user: VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (this define is used to control moment when to send credit update message). If this value or its usage will be changed in kernel - this test may become useless/broken. Signed-off-by: Arseniy Krasnov <avkrasnov@salutedevices.com> Signed-off-by: NipaLocal <nipa@local>
1 parent 11f40a7 commit 5e02c7d

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

tools/testing/vsock/vsock_test.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,143 @@ static void test_double_bind_connect_client(const struct test_opts *opts)
12321232
}
12331233
}
12341234

1235+
#define RCVLOWAT_CREDIT_UPD_BUF_SIZE (1024 * 128)
1236+
/* This define is the same as in 'include/linux/virtio_vsock.h':
1237+
* it is used to decide when to send credit update message during
1238+
* reading from rx queue of a socket. Value and its usage in
1239+
* kernel is important for this test.
1240+
*/
1241+
#define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (1024 * 64)
1242+
1243+
static void test_stream_rcvlowat_def_cred_upd_client(const struct test_opts *opts)
1244+
{
1245+
size_t buf_size;
1246+
void *buf;
1247+
int fd;
1248+
1249+
fd = vsock_stream_connect(opts->peer_cid, 1234);
1250+
if (fd < 0) {
1251+
perror("connect");
1252+
exit(EXIT_FAILURE);
1253+
}
1254+
1255+
/* Send 1 byte more than peer's buffer size. */
1256+
buf_size = RCVLOWAT_CREDIT_UPD_BUF_SIZE + 1;
1257+
1258+
buf = malloc(buf_size);
1259+
if (!buf) {
1260+
perror("malloc");
1261+
exit(EXIT_FAILURE);
1262+
}
1263+
1264+
/* Wait until peer sets needed buffer size. */
1265+
recv_byte(fd, 1, 0);
1266+
1267+
if (send(fd, buf, buf_size, 0) != buf_size) {
1268+
perror("send failed");
1269+
exit(EXIT_FAILURE);
1270+
}
1271+
1272+
free(buf);
1273+
close(fd);
1274+
}
1275+
1276+
static void test_stream_rcvlowat_def_cred_upd_server(const struct test_opts *opts)
1277+
{
1278+
size_t recv_buf_size;
1279+
struct pollfd fds;
1280+
size_t buf_size;
1281+
void *buf;
1282+
int fd;
1283+
1284+
fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
1285+
if (fd < 0) {
1286+
perror("accept");
1287+
exit(EXIT_FAILURE);
1288+
}
1289+
1290+
buf_size = RCVLOWAT_CREDIT_UPD_BUF_SIZE;
1291+
1292+
if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
1293+
&buf_size, sizeof(buf_size))) {
1294+
perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
1295+
exit(EXIT_FAILURE);
1296+
}
1297+
1298+
/* Send one dummy byte here, because 'setsockopt()' above also
1299+
* sends special packet which tells sender to update our buffer
1300+
* size. This 'send_byte()' will serialize such packet with data
1301+
* reads in a loop below. Sender starts transmission only when
1302+
* it receives this single byte.
1303+
*/
1304+
send_byte(fd, 1, 0);
1305+
1306+
buf = malloc(buf_size);
1307+
if (!buf) {
1308+
perror("malloc");
1309+
exit(EXIT_FAILURE);
1310+
}
1311+
1312+
/* Wait until there will be 128KB of data in rx queue. */
1313+
while (1) {
1314+
ssize_t res;
1315+
1316+
res = recv(fd, buf, buf_size, MSG_PEEK);
1317+
if (res == buf_size)
1318+
break;
1319+
1320+
if (res <= 0) {
1321+
fprintf(stderr, "unexpected 'recv()' return: %zi\n", res);
1322+
exit(EXIT_FAILURE);
1323+
}
1324+
}
1325+
1326+
/* There is 128KB of data in the socket's rx queue,
1327+
* dequeue first 64KB, credit update is not sent.
1328+
*/
1329+
recv_buf_size = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
1330+
recv_buf(fd, buf, recv_buf_size, 0, recv_buf_size);
1331+
recv_buf_size++;
1332+
1333+
/* Updating SO_RCVLOWAT will send credit update. */
1334+
if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
1335+
&recv_buf_size, sizeof(recv_buf_size))) {
1336+
perror("setsockopt(SO_RCVLOWAT)");
1337+
exit(EXIT_FAILURE);
1338+
}
1339+
1340+
memset(&fds, 0, sizeof(fds));
1341+
fds.fd = fd;
1342+
fds.events = POLLIN | POLLRDNORM | POLLERR |
1343+
POLLRDHUP | POLLHUP;
1344+
1345+
/* This 'poll()' will return once we receive last byte
1346+
* sent by client.
1347+
*/
1348+
if (poll(&fds, 1, -1) < 0) {
1349+
perror("poll");
1350+
exit(EXIT_FAILURE);
1351+
}
1352+
1353+
if (fds.revents & POLLERR) {
1354+
fprintf(stderr, "'poll()' error\n");
1355+
exit(EXIT_FAILURE);
1356+
}
1357+
1358+
if (fds.revents & (POLLIN | POLLRDNORM)) {
1359+
recv_buf(fd, buf, recv_buf_size, MSG_DONTWAIT, recv_buf_size);
1360+
} else {
1361+
/* These flags must be set, as there is at
1362+
* least 64KB of data ready to read.
1363+
*/
1364+
fprintf(stderr, "POLLIN | POLLRDNORM expected\n");
1365+
exit(EXIT_FAILURE);
1366+
}
1367+
1368+
free(buf);
1369+
close(fd);
1370+
}
1371+
12351372
static struct test_case test_cases[] = {
12361373
{
12371374
.name = "SOCK_STREAM connection reset",
@@ -1342,6 +1479,11 @@ static struct test_case test_cases[] = {
13421479
.run_client = test_double_bind_connect_client,
13431480
.run_server = test_double_bind_connect_server,
13441481
},
1482+
{
1483+
.name = "SOCK_STREAM virtio SO_RCVLOWAT + deferred cred update",
1484+
.run_client = test_stream_rcvlowat_def_cred_upd_client,
1485+
.run_server = test_stream_rcvlowat_def_cred_upd_server,
1486+
},
13451487
{},
13461488
};
13471489

0 commit comments

Comments
 (0)