From patchwork Fri Jun 21 19:25:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 13708025 X-Patchwork-Delegate: kuba@kernel.org Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B82F168482; Fri, 21 Jun 2024 19:37:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=37.18.73.165 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718998662; cv=none; b=RwwMT03JCbwJKgySdAMTiGrbQLVA8VDsj10xuP7wCk1GLFfSbcCtrX1HHBU3jd5kfzTWDi6JNF3o0Pxq4xhEuzj6NVCJxxGWIEseDfv8SUIscoOySOcwrY6yvo+p+jvilVQXZppLnzU55kD309Yn7V02AWDD2xTefu7FtyW37/4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718998662; c=relaxed/simple; bh=mRjpXsNEbhi2mQjhG5KP0CF8PUCU4S3lTnB8pLYCUI8=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Qk85b7YsJf+Pl3UhIlIyWKgIHoEjbUnupXHmug/CThdyjOLHNUTx0NPfPngrBgjtwTnJCQg9TeXnmcGKx/i0XfYTF/t2CXGPPv77EEZg0Llp9TYwpxVSYxyag/MPam8ExeciFlw3Hg+B9Tg7PZaqQWhLKfNHezFQjpdwnaWulJw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=salutedevices.com; spf=pass smtp.mailfrom=salutedevices.com; dkim=pass (2048-bit key) header.d=salutedevices.com header.i=@salutedevices.com header.b=PhsCqQCv; arc=none smtp.client-ip=37.18.73.165 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=salutedevices.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=salutedevices.com header.i=@salutedevices.com header.b="PhsCqQCv" Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id D544D100006; Fri, 21 Jun 2024 22:37:29 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru D544D100006 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1718998649; bh=VT5gIGGs+UzYkF+C6lwdSpMrf/K2IcA9ciJlIDD0xY4=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=PhsCqQCvVzZzrzh9RSBmv47TVeVeQV4GYLPcBFu3MyZrSpEI31imYUizxOpkNMxgw IhcQJKVWoPAGxh3LKjerkjy724c9y8DUhyVEzGsb0KL2qpv4BVhaJNMRMxqF62nyBI eRWzhrhO267I7LbxvgDNxZcCV06vMwzoQwj4RUcytnRrrBUS8FuqNPhFlAB6lWr6B9 ca1L62oxJ6pP8NU/bPjrmECLwjB2Xi0TS+SN1K2Zj2ivd0//5d7b26+oxCVWbHG21Y eRmrVeREXaSbRLyaJN6nmj6qmKUNZSK+EyJ+VLZJfoiAYTxqvAhKn6ln8W2owjyuH5 NhmpK0qQLELpg== Received: from smtp.sberdevices.ru (p-i-exch-sc-m02.sberdevices.ru [172.16.192.103]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Fri, 21 Jun 2024 22:37:29 +0300 (MSK) Received: from localhost.localdomain (100.64.160.123) by p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Fri, 21 Jun 2024 22:37:28 +0300 From: Arseniy Krasnov To: Stefan Hajnoczi , Stefano Garzarella , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Jason Wang , Bobby Eshleman CC: , , , , , , Subject: [RFC PATCH v1 1/2] virtio/vsock: rework deferred credit update logic Date: Fri, 21 Jun 2024 22:25:40 +0300 Message-ID: <20240621192541.2082657-2-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.35.0 In-Reply-To: <20240621192541.2082657-1-avkrasnov@salutedevices.com> References: <20240621192541.2082657-1-avkrasnov@salutedevices.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) To p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) X-KSMG-Rule-ID: 10 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 186064 [Jun 21 2024] X-KSMG-AntiSpam-Version: 6.1.0.4 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 20 0.3.20 743589a8af6ec90b529f2124c2bbfc3ce1d2f20f, {Tracking_from_domain_doesnt_match_to}, smtp.sberdevices.ru:7.1.1,5.0.1;d41d8cd98f00b204e9800998ecf8427e.com:7.1.1;127.0.0.199:7.1.2;100.64.160.123:7.1.2;salutedevices.com:7.1.1, FromAlignment: s, ApMailHostAddress: 100.64.160.123 X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2024/06/21 16:35:00 #25651590 X-KSMG-AntiVirus-Status: Clean, skipped X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Previous calculation of 'free_space' was wrong (but worked as expected in most cases, see below), because it didn't account number of bytes in rx queue. Let's rework 'free_space' calculation in the following way: as this value is considered free space at rx side from tx point of view, it must be equal to return value of 'virtio_transport_get_credit()' at tx side. This function uses 'tx_cnt' counter and 'peer_fwd_cnt': first is number of transmitted bytes (without wrap), second is last 'fwd_cnt' value received from rx. So let's use same approach at rx side during 'free_space' calculation: add 'rx_cnt' counter which is number of received bytes (also without wrap) and subtract 'last_fwd_cnt' from it. Now we have: 1) 'rx_cnt' == 'tx_cnt' at both sides. 2) 'last_fwd_cnt' == 'peer_fwd_cnt' - because first is last 'fwd_cnt' sent to tx, while second is last 'fwd_cnt' received from rx. Now 'free_space' is handled correctly and also we don't need 'low_rx_bytes' flag - this was more like a hack. Previous calculation of 'free_space' worked (in 99% cases), because if we take a look on behaviour of both expressions (new and previous): '(rx_cnt - last_fwd_cnt)' and '(fwd_cnt - last_fwd_cnt)' Both of them always grows up, with almost same "speed": only difference is that 'rx_cnt' is incremented earlier during packet is received, while 'fwd_cnt' in incremented when packet is read by user. So if 'rx_cnt' grows "faster", then resulting 'free_space' become smaller also, so we send credit updates a little bit more, but: * 'free_space' calculation based on 'rx_cnt' gives the same value, which tx sees as free space at rx side, so original idea of 'free_space' is now implemented as planned. * Hack with 'low_rx_bytes' now is not needed. Also here is some performance comparison between both versions of 'free_space' calculation: *------*----------*----------* | | 'rx_cnt' | previous | *------*----------*----------* |H -> G| 8.42 | 7.82 | *------*----------*----------* |G -> H| 11.6 | 12.1 | *------*----------*----------* As benchmark 'vsock-iperf' with default arguments was used. There is no significant performance difference before and after this patch. Signed-off-by: Arseniy Krasnov --- include/linux/virtio_vsock.h | 1 + net/vmw_vsock/virtio_transport_common.c | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h index c82089dee0c8..3579491c411e 100644 --- a/include/linux/virtio_vsock.h +++ b/include/linux/virtio_vsock.h @@ -135,6 +135,7 @@ struct virtio_vsock_sock { u32 peer_buf_alloc; /* Protected by rx_lock */ + u32 rx_cnt; u32 fwd_cnt; u32 last_fwd_cnt; u32 rx_bytes; diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 16ff976a86e3..1d4e2328e06e 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -441,6 +441,7 @@ static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, return false; vvs->rx_bytes += len; + vvs->rx_cnt += len; return true; } @@ -558,7 +559,6 @@ virtio_transport_stream_do_dequeue(struct vsock_sock *vsk, size_t bytes, total = 0; struct sk_buff *skb; u32 fwd_cnt_delta; - bool low_rx_bytes; int err = -EFAULT; u32 free_space; @@ -603,9 +603,7 @@ virtio_transport_stream_do_dequeue(struct vsock_sock *vsk, } fwd_cnt_delta = vvs->fwd_cnt - vvs->last_fwd_cnt; - free_space = vvs->buf_alloc - fwd_cnt_delta; - low_rx_bytes = (vvs->rx_bytes < - sock_rcvlowat(sk_vsock(vsk), 0, INT_MAX)); + free_space = vvs->buf_alloc - (vvs->rx_cnt - vvs->last_fwd_cnt); spin_unlock_bh(&vvs->rx_lock); @@ -619,7 +617,7 @@ virtio_transport_stream_do_dequeue(struct vsock_sock *vsk, * number of bytes in rx queue is not enough to wake up reader. */ if (fwd_cnt_delta && - (free_space < VIRTIO_VSOCK_MAX_PKT_BUF_SIZE || low_rx_bytes)) + (free_space < VIRTIO_VSOCK_MAX_PKT_BUF_SIZE)) virtio_transport_send_credit_update(vsk); return total; From patchwork Fri Jun 21 19:25:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 13708026 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0553179203; Fri, 21 Jun 2024 19:37:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.89.224.132 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718998663; cv=none; b=fVFKOMo/PEqnMiVs9JP9A5ttTaL/eFTiWaek04SleEef3hYxNy5zhBTRaaCCtHPD9Fm8Qv1c0n8zU5BSyF66z/0SUy7Pr0bJeAINu8dFa30dijYm5rRk6jhFerkLOL0OR7vr3pSOQmV7hsT7RH6d9k2015Baf4GYjIHF7YBNJCo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718998663; c=relaxed/simple; bh=b0MVT8obBsnnfcUJguz716T6dfpUAaaH84e9QWzNd5g=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=S+dJUwNawjgGz4vrrfWFrRzpkoNxyXcXcXVqPaHnpLPX+uH4Z/yx8i4oT2/tTRcoeXIo5ZpciRoNhjqztbqHazaO/Yo/4cfc85X+xv5TTfFBTR4RbgBGGCp0FnsAHuizyGG//uhK2D8LKJwgdDDoCUOGmhV7zCUre7bGAZiSL38= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=salutedevices.com; spf=pass smtp.mailfrom=salutedevices.com; dkim=pass (2048-bit key) header.d=salutedevices.com header.i=@salutedevices.com header.b=fBhyssEz; arc=none smtp.client-ip=45.89.224.132 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=salutedevices.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=salutedevices.com header.i=@salutedevices.com header.b="fBhyssEz" Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id BEF2812000A; Fri, 21 Jun 2024 22:37:30 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru BEF2812000A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1718998650; bh=+imAiTZlutgnKCi/zzqvLyHYlTuHBPLtJT+Rnuz2Hvc=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=fBhyssEzoZLN2D4fZNYfH5S1/uBUptuOsOVHT3eNY1d5/KFOYQN2WmRxlXePaZVC8 k9kh7mRLj4vR9P58xw7LbFpQ880j5uhCwEOz+n2mz2/HnRY3gfz21XotJOO0lPT8Iw QwrLqPhNX+F9J/e8InBJtol+IgE9YKPq7jBsG20nFaa9tqmZY2IJDfr9Q0DFdyqUp4 mIMg6rkJxO0XaASLuS7qg2WZ8xMJquQrofqwh2k/fcZXCeu6rqkVeAR0m1ZeVNJ+Wx 1IlkudzCRypXPR3daW+08Of4SeakT8eysWRzcuQvqVlvX4y391Xny4ebaH6mG7b0LZ bw2dM6WKuJdHg== Received: from smtp.sberdevices.ru (p-i-exch-sc-m02.sberdevices.ru [172.16.192.103]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Fri, 21 Jun 2024 22:37:30 +0300 (MSK) Received: from localhost.localdomain (100.64.160.123) by p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Fri, 21 Jun 2024 22:37:29 +0300 From: Arseniy Krasnov To: Stefan Hajnoczi , Stefano Garzarella , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Michael S. Tsirkin" , Jason Wang , Bobby Eshleman CC: , , , , , , Subject: [RFC PATCH v1 2/2] vsock/test: add test for deferred credit update Date: Fri, 21 Jun 2024 22:25:41 +0300 Message-ID: <20240621192541.2082657-3-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.35.0 In-Reply-To: <20240621192541.2082657-1-avkrasnov@salutedevices.com> References: <20240621192541.2082657-1-avkrasnov@salutedevices.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-ClientProxiedBy: p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) To p-i-exch-sc-m02.sberdevices.ru (172.16.192.103) X-KSMG-Rule-ID: 10 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 186064 [Jun 21 2024] X-KSMG-AntiSpam-Version: 6.1.0.4 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 20 0.3.20 743589a8af6ec90b529f2124c2bbfc3ce1d2f20f, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2;smtp.sberdevices.ru:7.1.1,5.0.1;100.64.160.123:7.1.2;d41d8cd98f00b204e9800998ecf8427e.com:7.1.1;salutedevices.com:7.1.1, FromAlignment: s, ApMailHostAddress: 100.64.160.123 X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2024/06/21 16:35:00 #25651590 X-KSMG-AntiVirus-Status: Clean, skipped X-Patchwork-State: RFC This test checks, that we send exactly expected number of credit update packets during deferred credit update optimization. Test work in client/server modes: 1) Client just connects to server and send 256Kb of data. 256Kb is chosen because it is default space for vsock peer. After transmission client waits until server performs checks of this test. 2) Server waits for vsock connection and also open raw socket binded to vsock monitor interface. Then server waits until there will be 256Kb of data in its rx queue (by reading data from stream socket with MSG_PEEK flag). Then server starts reading data from stream socket - it reads entire socket, also reading packets from raw vsock socket, to check that there is OP_RW packets. After data read is done, it checks raw socket again, by waiting exact amount of CREDIT_UPDATE packets. Finally it checks that rx queue of raw socket is empty using read with timeout. Some notes about this test: * It is only for virtio transport. * It relies on sizes of virtio rx buffers for guest and host, to handle raw packets properly (4Kb for guest and 64Kb for host). * It relies on free space limit for deferred credit update - currently it is 64Kb. * It needs permissions to open raw sockets. Signed-off-by: Arseniy Krasnov --- tools/testing/vsock/.gitignore | 1 + tools/testing/vsock/Makefile | 2 + tools/testing/vsock/virtio_vsock_test.c | 369 ++++++++++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 tools/testing/vsock/virtio_vsock_test.c diff --git a/tools/testing/vsock/.gitignore b/tools/testing/vsock/.gitignore index d9f798713cd7..74d13a38cf43 100644 --- a/tools/testing/vsock/.gitignore +++ b/tools/testing/vsock/.gitignore @@ -4,3 +4,4 @@ vsock_test vsock_diag_test vsock_perf vsock_uring_test +virtio_vsock_test diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index a7f56a09ca9f..e04d69903687 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -8,6 +8,8 @@ vsock_perf: vsock_perf.o msg_zerocopy_common.o vsock_uring_test: LDLIBS = -luring vsock_uring_test: control.o util.o vsock_uring_test.o timeout.o msg_zerocopy_common.o +virtio_vsock_test: virtio_vsock_test.o util.o timeout.o control.o + CFLAGS += -g -O2 -Werror -Wall -I. -I../../include -I../../../usr/include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE .PHONY: all test clean clean: diff --git a/tools/testing/vsock/virtio_vsock_test.c b/tools/testing/vsock/virtio_vsock_test.c new file mode 100644 index 000000000000..4320dbc31ecc --- /dev/null +++ b/tools/testing/vsock/virtio_vsock_test.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * virtio_vsock_test - vsock.ko test suite for VirtIO transport. + * + * Copyright (C) 2024 SaluteDevices. + * + * Author: Arseniy Krasnov + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "control.h" +#include "util.h" + +#define RAW_SOCK_RCV_BUF (1024 * 1024) + +#define TOTAL_TX_BYTES (1024 * 256) + +#define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (1024 * 64) + +#define VIRTIO_VSOCK_GUEST_RX_BUF_SIZE (4096) +#define VIRTIO_VSOCK_HOST_RX_BUF_SIZE (1024 * 64) + +static const char *interface; + +static void test_stream_deferred_credit_update_client(const struct test_opts *opts) +{ + unsigned char buf[VIRTIO_VSOCK_MAX_PKT_BUF_SIZE]; + int fd; + int i; + + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + control_expectln("SERVERREADY"); + memset(buf, 0, sizeof(buf)); + + for (i = 0; i < TOTAL_TX_BYTES / VIRTIO_VSOCK_MAX_PKT_BUF_SIZE; i++) { + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { + perror("write"); + exit(EXIT_FAILURE); + } + } + + control_writeln("CLIENTDONE"); + control_expectln("SERVERDONE"); + + close(fd); +} + +static int create_raw_sock(const char *ifname) +{ + struct sockaddr_ll addr_ll; + struct ifreq ifr; + size_t rcv_buf; + int fd; + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), + "%s", ifname); + + rcv_buf = RAW_SOCK_RCV_BUF; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcv_buf, + sizeof(rcv_buf))) { + perror("setsockopt(SO_RCVBUFFORCE)"); + exit(EXIT_FAILURE); + } + + if (ioctl(fd, SIOCGIFINDEX, &ifr)) { + perror("ioctl(SIOCGIFINDEX)"); + exit(EXIT_FAILURE); + } + + memset(&addr_ll, 0, sizeof(addr_ll)); + addr_ll.sll_family = AF_PACKET; + addr_ll.sll_ifindex = ifr.ifr_ifindex; + addr_ll.sll_protocol = htons(ETH_P_ALL); + + if (bind(fd, (struct sockaddr *)&addr_ll, sizeof(struct sockaddr_ll)) < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + return fd; +} + + +static void check_raw_packet(int raw_fd, uint16_t expected_op) +{ + struct af_vsockmon_hdr *mhdr; + struct virtio_vsock_hdr *hdr; + unsigned char buf[VIRTIO_VSOCK_MAX_PKT_BUF_SIZE + + sizeof(*mhdr) + sizeof(*hdr)] = { 0 }; + ssize_t res; + + res = read(raw_fd, buf, sizeof(buf)); + + if (res == -1) { + if (expected_op == 0xffff && errno == EAGAIN) + return; + + fprintf(stderr, "Unexpected raw read: %i\n", errno); + exit(EXIT_FAILURE); + } + + mhdr = (struct af_vsockmon_hdr *)buf; + hdr = (struct virtio_vsock_hdr *)(mhdr + 1); + + if (hdr->op != expected_op) { + fprintf(stderr, "Unexpected op: %hhu, but %hhu expected\n", + hdr->op, expected_op); + exit(EXIT_FAILURE); + } +} + +static void test_stream_deferred_credit_update_server(const struct test_opts *opts) +{ + char buf[TOTAL_TX_BYTES] = { 0 }; + int raw_fd, fd, rx_packet_size, i; + int credit_update_pkts; + struct timeval tv; + size_t total_recv; + + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + raw_fd = create_raw_sock(interface); + + tv.tv_sec = 2; + tv.tv_usec = 0; + + if (setsockopt(raw_fd, SOL_SOCKET, SO_RCVTIMEO, + (void *)&tv, sizeof(tv))) { + perror("setsockopt(SO_RCVTIMEO)"); + exit(EXIT_FAILURE); + } + + control_writeln("SERVERREADY"); + control_expectln("CLIENTDONE"); + + total_recv = 0; + + /* Wait, until we receive whole data. */ + while (1) { + ssize_t ret; + + ret = recv(fd, buf, sizeof(buf), MSG_PEEK); + if (ret == sizeof(buf)) + break; + + if (ret <= 0) { + fprintf(stderr, "unexpected 'recv()' return: %zi\n", ret); + exit(EXIT_FAILURE); + } + } + + if (opts->peer_cid == VMADDR_CID_HOST) + rx_packet_size = VIRTIO_VSOCK_GUEST_RX_BUF_SIZE; + else + rx_packet_size = VIRTIO_VSOCK_HOST_RX_BUF_SIZE; + + while (1) { + ssize_t ret; + + ret = read(fd, buf, rx_packet_size); + if (ret <= 0) { + perror("read"); + exit(EXIT_FAILURE); + } + + check_raw_packet(raw_fd, VIRTIO_VSOCK_OP_RW); + + total_recv += ret; + + if (total_recv >= TOTAL_TX_BYTES) + break; + } + + if (total_recv != TOTAL_TX_BYTES) { + fprintf(stderr, "Invalid number of received bytes: %zu", + total_recv); + exit(EXIT_FAILURE); + } + + credit_update_pkts = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE / rx_packet_size; + for (i = 0; i < credit_update_pkts; i++) + check_raw_packet(raw_fd, VIRTIO_VSOCK_OP_CREDIT_UPDATE); + + /* Check that there are no new packets. */ + check_raw_packet(raw_fd, 0xffff); + + control_writeln("SERVERDONE"); + + close(fd); + close(raw_fd); +} + +static struct test_case test_cases[] = { + { + .name = "SOCK_STREAM deferred credit update", + .run_client = test_stream_deferred_credit_update_client, + .run_server = test_stream_deferred_credit_update_server, + }, + {} +}; + +static const char optstring[] = ""; +static const struct option longopts[] = { + { + .name = "control-host", + .has_arg = required_argument, + .val = 'H', + }, + { + .name = "control-port", + .has_arg = required_argument, + .val = 'P', + }, + { + .name = "mode", + .has_arg = required_argument, + .val = 'm', + }, + { + .name = "peer-cid", + .has_arg = required_argument, + .val = 'p', + }, + { + .name = "peer-port", + .has_arg = required_argument, + .val = 'q', + }, + { + .name = "interface", + .has_arg = required_argument, + .val = 'i', + }, + { + .name = "help", + .has_arg = no_argument, + .val = '?', + }, + {}, +}; + +static void usage(void) +{ + fprintf(stderr, "Usage: virtio_vsock_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid= [--peer-port=] [--list] [--interface=]\n" + "\n" + " Server: virtio_vsock_test --control-port=1234 --mode=server --peer-cid=3 --interface=vsockmon0\n" + " Client: virtio_vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" + "\n" + "Run AF_VSOCK tests, specific for virtio transport. Requires\n" + "permissions to open raw socket.\n" + "\n" + "A TCP control socket connection is used to coordinate tests\n" + "between the client and the server. The server requires a\n" + "listen address and the client requires an address to\n" + "connect to.\n" + "\n" + "The CID of the other side must be given with --peer-cid=.\n" + "During the test, two AF_VSOCK ports will be used: the port\n" + "specified with --peer-port= (or the default port)\n" + "and the next one.\n" + "\n" + "Options:\n" + " --help This help message\n" + " --control-host Server IP address to connect to\n" + " --control-port Server port to listen on/connect to\n" + " --mode client|server Server or client mode\n" + " --peer-cid CID of the other side\n" + " --peer-port AF_VSOCK port used for the test [default: %d]\n" + " --interface \n", + DEFAULT_PEER_PORT + ); +} + +int main(int argc, char *argv[]) +{ + const char *control_host = NULL; + const char *control_port = NULL; + struct test_opts opts = { + .mode = TEST_MODE_UNSET, + .peer_cid = VMADDR_CID_ANY, + .peer_port = DEFAULT_PEER_PORT, + }; + + for (;;) { + int opt = getopt_long(argc, argv, optstring, longopts, NULL); + + if (opt == -1) + break; + + switch (opt) { + case 'H': + control_host = optarg; + break; + case 'P': + control_port = optarg; + break; + case 'm': + if (strcmp(optarg, "client") == 0) + opts.mode = TEST_MODE_CLIENT; + else if (strcmp(optarg, "server") == 0) + opts.mode = TEST_MODE_SERVER; + else { + fprintf(stderr, "--mode must be \"client\" or \"server\"\n"); + return EXIT_FAILURE; + } + break; + case 'p': + opts.peer_cid = parse_cid(optarg); + break; + case 'q': + opts.peer_port = parse_port(optarg); + break; + case 'i': + interface = optarg; + default: + usage(); + } + } + + if (!control_host) { + if (opts.mode != TEST_MODE_SERVER) + usage(); + control_host = "0.0.0.0"; + } + + if (!interface) + interface = "vsockmon0"; + + control_init(control_host, control_port, + opts.mode == TEST_MODE_SERVER); + + run_tests(test_cases, &opts); + + return EXIT_SUCCESS; +}