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 <avkras...@salutedevices.com>
Reviewed-by: Stefano Garzarella <sgarz...@redhat.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(+)

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


Reply via email to