vsock/test: add test for small packets under pressure

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 <sgarzare@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
Link: https://patch.msgid.link/20260708102904.50732-3-sgarzare@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Stefano Garzarella 2026-07-08 12:29:04 +02:00 committed by Paolo Abeni
parent 2a12c05aef
commit 30c82aa0a8

View File

@ -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,
},
{},
};