From: Stefano Garzarella <[email protected]>

Add a test that sends 2 MB of data using randomly sized small packets
(129-512 bytes) over a SOCK_STREAM connection. Packets above
GOOD_COPY_LEN (128) bypass the in-place coalescing in recv_enqueue(),
forcing each one into its own skb.

Without receive queue collapsing, the per-skb overhead eventually
exceeds buf_alloc and the connection is reset. The test verifies
that all data arrives and that content integrity is preserved.

Signed-off-by: Stefano Garzarella <[email protected]>
---
 tools/testing/vsock/vsock_test.c | 87 ++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index 76be0e4a7f0e..b4ff9f946565 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -2347,6 +2347,88 @@ static void test_stream_tx_credit_bounds_server(const 
struct test_opts *opts)
        close(fd);
 }
 
+/* Test that many small packets don't cause a connection reset under pressure
+ * and that data integrity is preserved.  Packet sizes vary randomly between
+ * 129 and 512 bytes, above GOOD_COPY_LEN (128) to bypass in-place coalescing
+ * in recv_enqueue, forcing each one into its own skb.  Without receive queue
+ * collapsing, the per-skb overhead eventually exceeds buf_alloc and the
+ * connection is reset.
+ */
+#define COLLAPSE_PKT_MIN 129
+#define COLLAPSE_PKT_MAX 512
+#define COLLAPSE_TOTAL (2 * 1024 * 1024)
+
+static void test_stream_collapse_client(const struct test_opts *opts)
+{
+       unsigned char *data;
+       unsigned long hash;
+       size_t offset = 0;
+       int i, fd;
+
+       data = malloc(COLLAPSE_TOTAL);
+       if (!data) {
+               perror("malloc");
+               exit(EXIT_FAILURE);
+       }
+
+       for (i = 0; i < COLLAPSE_TOTAL; i++)
+               data[i] = rand() & 0xff;
+
+       fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+       if (fd < 0) {
+               perror("connect");
+               exit(EXIT_FAILURE);
+       }
+
+       while (offset < COLLAPSE_TOTAL) {
+               size_t pkt_size = COLLAPSE_PKT_MIN +
+                       rand() % (COLLAPSE_PKT_MAX - COLLAPSE_PKT_MIN + 1);
+
+               pkt_size = min(pkt_size, COLLAPSE_TOTAL - offset);
+
+               send_buf(fd, data + offset, pkt_size, 0, pkt_size);
+               offset += pkt_size;
+       }
+
+       hash = hash_djb2(data, COLLAPSE_TOTAL);
+       control_writeulong(hash);
+
+       free(data);
+       close(fd);
+}
+
+static void test_stream_collapse_server(const struct test_opts *opts)
+{
+       unsigned long hash, remote_hash;
+       unsigned char *data;
+       int fd;
+
+       data = malloc(COLLAPSE_TOTAL);
+       if (!data) {
+               perror("malloc");
+               exit(EXIT_FAILURE);
+       }
+
+       fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+       if (fd < 0) {
+               perror("accept");
+               exit(EXIT_FAILURE);
+       }
+
+       recv_buf(fd, data, COLLAPSE_TOTAL, 0, COLLAPSE_TOTAL);
+
+       hash = hash_djb2(data, COLLAPSE_TOTAL);
+       remote_hash = control_readulong();
+       if (hash != remote_hash) {
+               fprintf(stderr, "hash mismatch: local %lu remote %lu\n",
+                       hash, remote_hash);
+               exit(EXIT_FAILURE);
+       }
+
+       free(data);
+       close(fd);
+}
+
 static struct test_case test_cases[] = {
        {
                .name = "SOCK_STREAM connection reset",
@@ -2546,6 +2628,11 @@ static struct test_case test_cases[] = {
                .run_client = test_stream_msg_peek_client,
                .run_server = test_stream_peek_after_recv_server,
        },
+       {
+               .name = "SOCK_STREAM small packets backpressure",
+               .run_client = test_stream_collapse_client,
+               .run_server = test_stream_collapse_server,
+       },
        {},
 };
 
-- 
2.54.0


Reply via email to