Skip to content

Commit

Permalink
vsock/test: two tests to check credit update logic
Browse files Browse the repository at this point in the history
Both tests are almost same, only differs in two 'if' conditions, so
implemented in a single function. Tests check, that credit update
message is sent:

1) During setting SO_RCVLOWAT value of the socket.
2) When number of 'rx_bytes' become smaller than SO_RCVLOWAT value.

Signed-off-by: Arseniy Krasnov <[email protected]>
Reviewed-by: Stefano Garzarella <[email protected]>
Acked-by: Michael S. Tsirkin <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Arseniy Krasnov authored and davem330 committed Dec 15, 2023
1 parent 0fe1798 commit 542e893
Showing 1 changed file with 175 additions and 0 deletions.
175 changes: 175 additions & 0 deletions tools/testing/vsock/vsock_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,171 @@ static void test_double_bind_connect_client(const struct test_opts *opts)
}
}

#define RCVLOWAT_CREDIT_UPD_BUF_SIZE (1024 * 128)
/* This define is the same as in 'include/linux/virtio_vsock.h':
* it is used to decide when to send credit update message during
* reading from rx queue of a socket. Value and its usage in
* kernel is important for this test.
*/
#define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (1024 * 64)

static void test_stream_rcvlowat_def_cred_upd_client(const struct test_opts *opts)
{
size_t buf_size;
void *buf;
int fd;

fd = vsock_stream_connect(opts->peer_cid, 1234);
if (fd < 0) {
perror("connect");
exit(EXIT_FAILURE);
}

/* Send 1 byte more than peer's buffer size. */
buf_size = RCVLOWAT_CREDIT_UPD_BUF_SIZE + 1;

buf = malloc(buf_size);
if (!buf) {
perror("malloc");
exit(EXIT_FAILURE);
}

/* Wait until peer sets needed buffer size. */
recv_byte(fd, 1, 0);

if (send(fd, buf, buf_size, 0) != buf_size) {
perror("send failed");
exit(EXIT_FAILURE);
}

free(buf);
close(fd);
}

static void test_stream_credit_update_test(const struct test_opts *opts,
bool low_rx_bytes_test)
{
size_t recv_buf_size;
struct pollfd fds;
size_t buf_size;
void *buf;
int fd;

fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
if (fd < 0) {
perror("accept");
exit(EXIT_FAILURE);
}

buf_size = RCVLOWAT_CREDIT_UPD_BUF_SIZE;

if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
&buf_size, sizeof(buf_size))) {
perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
exit(EXIT_FAILURE);
}

if (low_rx_bytes_test) {
/* Set new SO_RCVLOWAT here. This enables sending credit
* update when number of bytes if our rx queue become <
* SO_RCVLOWAT value.
*/
recv_buf_size = 1 + VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;

if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
&recv_buf_size, sizeof(recv_buf_size))) {
perror("setsockopt(SO_RCVLOWAT)");
exit(EXIT_FAILURE);
}
}

/* Send one dummy byte here, because 'setsockopt()' above also
* sends special packet which tells sender to update our buffer
* size. This 'send_byte()' will serialize such packet with data
* reads in a loop below. Sender starts transmission only when
* it receives this single byte.
*/
send_byte(fd, 1, 0);

buf = malloc(buf_size);
if (!buf) {
perror("malloc");
exit(EXIT_FAILURE);
}

/* Wait until there will be 128KB of data in rx queue. */
while (1) {
ssize_t res;

res = recv(fd, buf, buf_size, MSG_PEEK);
if (res == buf_size)
break;

if (res <= 0) {
fprintf(stderr, "unexpected 'recv()' return: %zi\n", res);
exit(EXIT_FAILURE);
}
}

/* There is 128KB of data in the socket's rx queue, dequeue first
* 64KB, credit update is sent if 'low_rx_bytes_test' == true.
* Otherwise, credit update is sent in 'if (!low_rx_bytes_test)'.
*/
recv_buf_size = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
recv_buf(fd, buf, recv_buf_size, 0, recv_buf_size);

if (!low_rx_bytes_test) {
recv_buf_size++;

/* Updating SO_RCVLOWAT will send credit update. */
if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
&recv_buf_size, sizeof(recv_buf_size))) {
perror("setsockopt(SO_RCVLOWAT)");
exit(EXIT_FAILURE);
}
}

fds.fd = fd;
fds.events = POLLIN | POLLRDNORM | POLLERR |
POLLRDHUP | POLLHUP;

/* This 'poll()' will return once we receive last byte
* sent by client.
*/
if (poll(&fds, 1, -1) < 0) {
perror("poll");
exit(EXIT_FAILURE);
}

if (fds.revents & POLLERR) {
fprintf(stderr, "'poll()' error\n");
exit(EXIT_FAILURE);
}

if (fds.revents & (POLLIN | POLLRDNORM)) {
recv_buf(fd, buf, recv_buf_size, MSG_DONTWAIT, recv_buf_size);
} else {
/* These flags must be set, as there is at
* least 64KB of data ready to read.
*/
fprintf(stderr, "POLLIN | POLLRDNORM expected\n");
exit(EXIT_FAILURE);
}

free(buf);
close(fd);
}

static void test_stream_cred_upd_on_low_rx_bytes(const struct test_opts *opts)
{
test_stream_credit_update_test(opts, true);
}

static void test_stream_cred_upd_on_set_rcvlowat(const struct test_opts *opts)
{
test_stream_credit_update_test(opts, false);
}

static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
Expand Down Expand Up @@ -1342,6 +1507,16 @@ static struct test_case test_cases[] = {
.run_client = test_double_bind_connect_client,
.run_server = test_double_bind_connect_server,
},
{
.name = "SOCK_STREAM virtio credit update + SO_RCVLOWAT",
.run_client = test_stream_rcvlowat_def_cred_upd_client,
.run_server = test_stream_cred_upd_on_set_rcvlowat,
},
{
.name = "SOCK_STREAM virtio credit update + low rx_bytes",
.run_client = test_stream_rcvlowat_def_cred_upd_client,
.run_server = test_stream_cred_upd_on_low_rx_bytes,
},
{},
};

Expand Down

0 comments on commit 542e893

Please sign in to comment.