Message ID | 20231205064806.2851305-5-avkrasnov@salutedevices.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | send credit update during setting SO_RCVLOWAT | expand |
On Tue, Dec 05, 2023 at 09:48:06AM +0300, Arseniy Krasnov wrote: >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 <avkrasnov@salutedevices.com> >--- > Changelog: > v1 -> v2: > * Update commit message by removing 'This patch adds XXX' manner. > * Update commit message by adding details about dependency for this > test from kernel internal define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE. > * Add comment for this dependency in 'vsock_test.c' where this define > is duplicated. > v2 -> v3: > * Replace synchronization based on control TCP socket with vsock > data socket - this is needed to allow sender transmit data only > when new buffer size of receiver is visible to sender. Otherwise > there is race and test fails sometimes. > v3 -> v4: > * Replace 'recv_buf()' to 'recv(MSG_DONTWAIT)' in last read operation > in server part. This is needed to ensure that 'poll()' wake up us > when number of bytes ready to read is equal to SO_RCVLOWAT value. > v4 -> v5: > * Use 'recv_buf(MSG_DONTWAIT)' instead of 'recv(MSG_DONTWAIT)'. > v5 -> v6: > * Add second test which checks, that credit update is sent during > reading data from socket. > * Update commit message. > > tools/testing/vsock/vsock_test.c | 175 +++++++++++++++++++++++++++++++ > 1 file changed, 175 insertions(+) Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> > >diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c >index 01fa816868bc..66246d81d654 100644 >--- a/tools/testing/vsock/vsock_test.c >+++ b/tools/testing/vsock/vsock_test.c >@@ -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", >@@ -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, >+ }, > {}, > }; > >-- >2.25.1 >
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 01fa816868bc..66246d81d654 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -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", @@ -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, + }, {}, };
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 <avkrasnov@salutedevices.com> --- Changelog: v1 -> v2: * Update commit message by removing 'This patch adds XXX' manner. * Update commit message by adding details about dependency for this test from kernel internal define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE. * Add comment for this dependency in 'vsock_test.c' where this define is duplicated. v2 -> v3: * Replace synchronization based on control TCP socket with vsock data socket - this is needed to allow sender transmit data only when new buffer size of receiver is visible to sender. Otherwise there is race and test fails sometimes. v3 -> v4: * Replace 'recv_buf()' to 'recv(MSG_DONTWAIT)' in last read operation in server part. This is needed to ensure that 'poll()' wake up us when number of bytes ready to read is equal to SO_RCVLOWAT value. v4 -> v5: * Use 'recv_buf(MSG_DONTWAIT)' instead of 'recv(MSG_DONTWAIT)'. v5 -> v6: * Add second test which checks, that credit update is sent during reading data from socket. * Update commit message. tools/testing/vsock/vsock_test.c | 175 +++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+)