public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Stefano Brivio <sbrivio@redhat.com>
To: Laurent Vivier <lvivier@redhat.com>
Cc: passt-dev@passt.top
Subject: Re: [PATCH v3 4/4] vhost-user: add vhost-user
Date: Fri, 23 Aug 2024 14:32:36 +0200	[thread overview]
Message-ID: <20240823143236.25ca0ac7@elisabeth> (raw)
In-Reply-To: <20240815155024.827956-5-lvivier@redhat.com>

Second part of review:

On Thu, 15 Aug 2024 17:50:23 +0200
Laurent Vivier <lvivier@redhat.com> wrote:

> +int tcp_vu_data_from_sock(struct ctx *c, struct tcp_tap_conn *conn)

Missing function comment. This one is especially relevant because I'm
not sure where the boundary is between this and tcp_vu_sock_recv().

> +{
> +	uint32_t wnd_scaled = conn->wnd_from_tap << conn->ws_from_tap;
> +	struct vu_dev *vdev = c->vdev;
> +	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
> +	const struct flowside *tapside = TAPFLOW(conn);
> +	uint16_t mss = MSS_GET(conn);
> +	size_t l2_hdrlen, fillsize;
> +	int i, iov_cnt, iov_used;
> +	int v4 = CONN_V4(conn);
> +	uint32_t already_sent;
> +	const uint16_t *check;
> +	struct iovec *first;
> +	int segment_size;
> +	int num_buffers;
> +	ssize_t len;
> +
> +	if (!vu_queue_enabled(vq) || !vu_queue_started(vq)) {
> +		flow_err(conn,
> +			 "Got packet, but no available descriptors on RX virtq.");

That seems to rather describe the case where tcp_vu_sock_recv() gets
< 0 from vu_queue_pop().

Strictly speaking, here, there are no descriptors available either, but
the message looks misleading. What about:

		flow_err(conn, "Got packet, but RX virtqueue not usable yet");

?

> +		return 0;
> +	}
> +
> +	already_sent = conn->seq_to_tap - conn->seq_ack_from_tap;
> +
> +	if (SEQ_LT(already_sent, 0)) {
> +		/* RFC 761, section 2.1. */
> +		flow_trace(conn, "ACK sequence gap: ACK for %u, sent: %u",
> +			   conn->seq_ack_from_tap, conn->seq_to_tap);
> +		conn->seq_to_tap = conn->seq_ack_from_tap;
> +		already_sent = 0;
> +	}
> +
> +	if (!wnd_scaled || already_sent >= wnd_scaled) {
> +		conn_flag(c, conn, STALLED);
> +		conn_flag(c, conn, ACK_FROM_TAP_DUE);
> +		return 0;
> +	}
> +
> +	/* Set up buffer descriptors we'll fill completely and partially. */
> +
> +	fillsize = wnd_scaled;
> +
> +	iov_vu[0].iov_base = tcp_buf_discard;

This should now be conditional to if (!peek_offset_cap), see the
(updated) tcp_buf_data_from_sock().

> +	iov_vu[0].iov_len = already_sent;
> +	fillsize -= already_sent;
> +
> +	iov_cnt = tcp_vu_sock_recv(c, conn, v4, fillsize, mss, &len);
> +	if (iov_cnt <= 0)
> +		return iov_cnt;
> +
> +	len -= already_sent;
> +	if (len <= 0) {
> +		conn_flag(c, conn, STALLED);
> +		vu_queue_rewind(vq, iov_cnt);
> +		return 0;
> +	}
> +
> +	conn_flag(c, conn, ~STALLED);
> +
> +	/* Likely, some new data was acked too. */
> +	tcp_update_seqack_wnd(c, conn, 0, NULL);
> +
> +	/* initialize headers */
> +	l2_hdrlen = tcp_vu_l2_hdrlen(vdev, !v4);
> +	iov_used = 0;
> +	num_buffers = 0;
> +	check = NULL;
> +	segment_size = 0;
> +	for (i = 0; i < iov_cnt && len; i++) {
> +
> +		if (segment_size == 0)
> +			first = &iov_vu[i + 1];

I don't understand why we have this loop on top of the loop from
tcp_vu_sock_recv(). I mean, it works, but this is a bit obscure
to me. I didn't really manage to review this function.

> +
> +		if (iov_vu[i + 1].iov_len > (size_t)len)
> +			iov_vu[i + 1].iov_len = len;
> +
> +		len -= iov_vu[i + 1].iov_len;
> +		iov_used++;
> +
> +		segment_size += iov_vu[i + 1].iov_len;
> +		num_buffers++;
> +
> +		if (segment_size >= mss || len == 0 ||
> +		    i + 1 == iov_cnt || vdev->hdrlen != sizeof(struct virtio_net_hdr_mrg_rxbuf)) {
> +			struct virtio_net_hdr_mrg_rxbuf *vh;
> +			size_t l4len;
> +
> +			if (i + 1 == iov_cnt)
> +				check = NULL;
> +
> +			/* restore first iovec base: point to vnet header */
> +			first->iov_base = (char *)first->iov_base - l2_hdrlen;
> +			first->iov_len = first->iov_len + l2_hdrlen;
> +
> +			vh = first->iov_base;
> +
> +			vh->hdr = vu_header;
> +			if (vdev->hdrlen == sizeof(struct virtio_net_hdr_mrg_rxbuf))
> +				vh->num_buffers = htole16(num_buffers);
> +
> +			l4len = tcp_vu_prepare(c, conn, first, segment_size, &check);
> +
> +			tcp_vu_pcap(c, tapside, first, num_buffers, l4len);
> +
> +			conn->seq_to_tap += segment_size;
> +
> +			segment_size = 0;
> +			num_buffers = 0;
> +		}
> +	}
> +
> +	/* release unused buffers */
> +	vu_queue_rewind(vq, iov_cnt - iov_used);
> +
> +	/* send packets */
> +	vu_send_frame(vdev, vq, elem, &iov_vu[1], iov_used);
> +
> +	conn_flag(c, conn, ACK_FROM_TAP_DUE);
> +
> +	return 0;
> +}
> diff --git a/tcp_vu.h b/tcp_vu.h
> new file mode 100644
> index 000000000000..99daba5b34ed
> --- /dev/null
> +++ b/tcp_vu.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + * Copyright Red Hat
> + * Author: Laurent Vivier <lvivier@redhat.com>
> + */
> +
> +#ifndef TCP_VU_H
> +#define TCP_VU_H
> +
> +int tcp_vu_send_flag(struct ctx *c, struct tcp_tap_conn *conn, int flags);
> +int tcp_vu_data_from_sock(struct ctx *c, struct tcp_tap_conn *conn);
> +
> +#endif  /*TCP_VU_H */
> diff --git a/udp.c b/udp.c
> index 7731257292e1..4d2afc62478a 100644
> --- a/udp.c
> +++ b/udp.c
> @@ -109,8 +109,7 @@
>  #include "pcap.h"
>  #include "log.h"
>  #include "flow_table.h"
> -
> -#define UDP_MAX_FRAMES		32  /* max # of frames to receive at once */
> +#include "udp_internal.h"
>  
>  /* "Spliced" sockets indexed by bound port (host order) */
>  static int udp_splice_ns  [IP_VERSIONS][NUM_PORTS];
> @@ -118,20 +117,8 @@ static int udp_splice_init[IP_VERSIONS][NUM_PORTS];
>  
>  /* Static buffers */
>  
> -/**
> - * struct udp_payload_t - UDP header and data for inbound messages
> - * @uh:		UDP header
> - * @data:	UDP data
> - */
> -static struct udp_payload_t {
> -	struct udphdr uh;
> -	char data[USHRT_MAX - sizeof(struct udphdr)];
> -#ifdef __AVX2__
> -} __attribute__ ((packed, aligned(32)))
> -#else
> -} __attribute__ ((packed, aligned(__alignof__(unsigned int))))
> -#endif
> -udp_payload[UDP_MAX_FRAMES];
> +/* UDP header and data for inbound messages */
> +static struct udp_payload_t udp_payload[UDP_MAX_FRAMES];
>  
>  /* Ethernet header for IPv4 frames */
>  static struct ethhdr udp4_eth_hdr;
> @@ -311,6 +298,7 @@ static void udp_splice_send(const struct ctx *c, size_t start, size_t n,
>  
>  /**
>   * udp_update_hdr4() - Update headers for one IPv4 datagram
> + * @c:		Execution context
>   * @ip4h:	Pre-filled IPv4 header (except for tot_len and saddr)
>   * @bp:		Pointer to udp_payload_t to update
>   * @toside:	Flowside for destination side
> @@ -318,8 +306,9 @@ static void udp_splice_send(const struct ctx *c, size_t start, size_t n,
>   *
>   * Return: size of IPv4 payload (UDP header + data)
>   */
> -static size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp,
> -			      const struct flowside *toside, size_t dlen)
> +size_t udp_update_hdr4(const struct ctx *c,
> +		       struct iphdr *ip4h, struct udp_payload_t *bp,
> +		       const struct flowside *toside, size_t dlen)
>  {
>  	const struct in_addr *src = inany_v4(&toside->faddr);
>  	const struct in_addr *dst = inany_v4(&toside->eaddr);
> @@ -336,13 +325,17 @@ static size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp,
>  	bp->uh.source = htons(toside->fport);
>  	bp->uh.dest = htons(toside->eport);
>  	bp->uh.len = htons(l4len);
> -	csum_udp4(&bp->uh, *src, *dst, bp->data, dlen);
> +	if (c->mode != MODE_VU)
> +		csum_udp4(&bp->uh, *src, *dst, bp->data, dlen);
> +	else
> +		bp->uh.check = 0;
>  
>  	return l4len;
>  }
>  
>  /**
>   * udp_update_hdr6() - Update headers for one IPv6 datagram
> + * @c:		Execution context
>   * @ip6h:	Pre-filled IPv6 header (except for payload_len and addresses)
>   * @bp:		Pointer to udp_payload_t to update
>   * @toside:	Flowside for destination side
> @@ -350,8 +343,9 @@ static size_t udp_update_hdr4(struct iphdr *ip4h, struct udp_payload_t *bp,
>   *
>   * Return: size of IPv6 payload (UDP header + data)
>   */
> -static size_t udp_update_hdr6(struct ipv6hdr *ip6h, struct udp_payload_t *bp,
> -			      const struct flowside *toside, size_t dlen)
> +size_t udp_update_hdr6(const struct ctx *c,
> +		       struct ipv6hdr *ip6h, struct udp_payload_t *bp,
> +		       const struct flowside *toside, size_t dlen)
>  {
>  	uint16_t l4len = dlen + sizeof(bp->uh);
>  
> @@ -365,19 +359,24 @@ static size_t udp_update_hdr6(struct ipv6hdr *ip6h, struct udp_payload_t *bp,
>  	bp->uh.source = htons(toside->fport);
>  	bp->uh.dest = htons(toside->eport);
>  	bp->uh.len = ip6h->payload_len;
> -	csum_udp6(&bp->uh, &toside->faddr.a6, &toside->eaddr.a6, bp->data, dlen);
> +	if (c->mode != MODE_VU)

Curly brackets for multi-line statement.

> +		csum_udp6(&bp->uh, &toside->faddr.a6, &toside->eaddr.a6,
> +			  bp->data, dlen);
> +	else
> +		bp->uh.check = 0xffff; /* zero checksum is invalid with IPv6 */

0xffff is the value virtio requires in case we want to ignore the
checksum, right? Or would any non-zero value work? The comment should
say that.

>  
>  	return l4len;
>  }
>  
>  /**
>   * udp_tap_prepare() - Convert one datagram into a tap frame
> + * @c:		Execution context
>   * @mmh:	Receiving mmsghdr array
>   * @idx:	Index of the datagram to prepare
>   * @toside:	Flowside for destination side
>   */
> -static void udp_tap_prepare(const struct mmsghdr *mmh, unsigned idx,
> -			    const struct flowside *toside)
> +static void udp_tap_prepare(const struct ctx *c, const struct mmsghdr *mmh,
> +			    unsigned idx, const struct flowside *toside)
>  {
>  	struct iovec (*tap_iov)[UDP_NUM_IOVS] = &udp_l2_iov[idx];
>  	struct udp_payload_t *bp = &udp_payload[idx];
> @@ -385,13 +384,15 @@ static void udp_tap_prepare(const struct mmsghdr *mmh, unsigned idx,
>  	size_t l4len;
>  
>  	if (!inany_v4(&toside->eaddr) || !inany_v4(&toside->faddr)) {
> -		l4len = udp_update_hdr6(&bm->ip6h, bp, toside, mmh[idx].msg_len);
> +		l4len = udp_update_hdr6(c, &bm->ip6h, bp, toside,
> +					mmh[idx].msg_len);
>  		tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip6h) +
>  			       sizeof(udp6_eth_hdr));
>  		(*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp6_eth_hdr);
>  		(*tap_iov)[UDP_IOV_IP] = IOV_OF_LVALUE(bm->ip6h);
>  	} else {
> -		l4len = udp_update_hdr4(&bm->ip4h, bp, toside, mmh[idx].msg_len);
> +		l4len = udp_update_hdr4(c, &bm->ip4h, bp, toside,
> +					mmh[idx].msg_len);
>  		tap_hdr_update(&bm->taph, l4len + sizeof(bm->ip4h) +
>  			       sizeof(udp4_eth_hdr));
>  		(*tap_iov)[UDP_IOV_ETH] = IOV_OF_LVALUE(udp4_eth_hdr);
> @@ -408,7 +409,7 @@ static void udp_tap_prepare(const struct mmsghdr *mmh, unsigned idx,
>   *
>   * #syscalls recvmsg
>   */
> -static bool udp_sock_recverr(int s)
> +bool udp_sock_recverr(int s)
>  {
>  	const struct sock_extended_err *ee;
>  	const struct cmsghdr *hdr;
> @@ -495,7 +496,7 @@ static int udp_sock_recv(const struct ctx *c, int s, uint32_t events,
>  }
>  
>  /**
> - * udp_listen_sock_handler() - Handle new data from socket
> + * udp_buf_listen_sock_handler() - Handle new data from socket
>   * @c:		Execution context
>   * @ref:	epoll reference
>   * @events:	epoll events bitmap
> @@ -503,8 +504,8 @@ static int udp_sock_recv(const struct ctx *c, int s, uint32_t events,
>   *
>   * #syscalls recvmmsg
>   */
> -void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> -			     uint32_t events, const struct timespec *now)
> +void udp_buf_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				 uint32_t events, const struct timespec *now)
>  {
>  	struct mmsghdr *mmh_recv = ref.udp.v6 ? udp6_mh_recv : udp4_mh_recv;
>  	int n, i;
> @@ -527,7 +528,7 @@ void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
>  			if (pif_is_socket(batchpif)) {
>  				udp_splice_prepare(mmh_recv, i);
>  			} else if (batchpif == PIF_TAP) {
> -				udp_tap_prepare(mmh_recv, i,
> +				udp_tap_prepare(c, mmh_recv, i,
>  						flowside_at_sidx(batchsidx));
>  			}
>  
> @@ -561,7 +562,7 @@ void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
>  }
>  
>  /**
> - * udp_reply_sock_handler() - Handle new data from flow specific socket
> + * udp_buf_reply_sock_handler() - Handle new data from flow specific socket
>   * @c:		Execution context
>   * @ref:	epoll reference
>   * @events:	epoll events bitmap
> @@ -569,8 +570,8 @@ void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
>   *
>   * #syscalls recvmmsg
>   */
> -void udp_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> -			    uint32_t events, const struct timespec *now)
> +void udp_buf_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				uint32_t events, const struct timespec *now)
>  {
>  	const struct flowside *fromside = flowside_at_sidx(ref.flowside);
>  	flow_sidx_t tosidx = flow_sidx_opposite(ref.flowside);
> @@ -594,7 +595,7 @@ void udp_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
>  		if (pif_is_socket(topif))
>  			udp_splice_prepare(mmh_recv, i);
>  		else if (topif == PIF_TAP)
> -			udp_tap_prepare(mmh_recv, i, toside);
> +			udp_tap_prepare(c, mmh_recv, i, toside);
>  	}
>  
>  	if (pif_is_socket(topif)) {
> diff --git a/udp.h b/udp.h
> index fb42e1c50d70..77b29260e8d1 100644
> --- a/udp.h
> +++ b/udp.h
> @@ -9,10 +9,10 @@
>  #define UDP_TIMER_INTERVAL		1000 /* ms */
>  
>  void udp_portmap_clear(void);
> -void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> -			     uint32_t events, const struct timespec *now);
> -void udp_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> -			    uint32_t events, const struct timespec *now);
> +void udp_buf_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				 uint32_t events, const struct timespec *now);
> +void udp_buf_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				uint32_t events, const struct timespec *now);
>  int udp_tap_handler(const struct ctx *c, uint8_t pif,
>  		    sa_family_t af, const void *saddr, const void *daddr,
>  		    const struct pool *p, int idx, const struct timespec *now);
> diff --git a/udp_internal.h b/udp_internal.h
> new file mode 100644
> index 000000000000..7dd45753698f
> --- /dev/null
> +++ b/udp_internal.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later

// SPDX...

> + * Copyright (c) 2021 Red Hat GmbH
> + * Author: Stefano Brivio <sbrivio@redhat.com>
> + */
> +
> +#ifndef UDP_INTERNAL_H
> +#define UDP_INTERNAL_H
> +
> +#include "tap.h" /* needed by udp_meta_t */
> +
> +#define UDP_MAX_FRAMES		32  /* max # of frames to receive at once */
> +
> +/**
> + * struct udp_payload_t - UDP header and data for inbound messages
> + * @uh:		UDP header
> + * @data:	UDP data
> + */
> +struct udp_payload_t {
> +	struct udphdr uh;
> +	char data[USHRT_MAX - sizeof(struct udphdr)];
> +#ifdef __AVX2__
> +} __attribute__ ((packed, aligned(32)));
> +#else
> +} __attribute__ ((packed, aligned(__alignof__(unsigned int))));
> +#endif
> +
> +size_t udp_update_hdr4(const struct ctx *c,
> +		       struct iphdr *ip4h, struct udp_payload_t *bp,
> +		       const struct flowside *toside, size_t dlen);
> +size_t udp_update_hdr6(const struct ctx *c,
> +                       struct ipv6hdr *ip6h, struct udp_payload_t *bp,
> +                       const struct flowside *toside, size_t dlen);
> +bool udp_sock_recverr(int s);
> +#endif /* UDP_INTERNAL_H */
> diff --git a/udp_vu.c b/udp_vu.c
> new file mode 100644
> index 000000000000..f9e7afcf4ddb
> --- /dev/null
> +++ b/udp_vu.c
> @@ -0,0 +1,338 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later

// SPDX...

> + * Copyright Red Hat
> + * Author: Laurent Vivier <lvivier@redhat.com>
> + *
> + * udp_vu.c - UDP L2 vhost-user management functions
> + */
> +
> +#include <unistd.h>
> +#include <assert.h>
> +#include <net/ethernet.h>
> +#include <net/if.h>
> +#include <netinet/in.h>
> +#include <netinet/ip.h>
> +#include <netinet/udp.h>
> +#include <stdint.h>
> +#include <stddef.h>
> +#include <sys/uio.h>
> +#include <linux/virtio_net.h>
> +
> +#include "checksum.h"
> +#include "util.h"
> +#include "ip.h"
> +#include "siphash.h"
> +#include "inany.h"
> +#include "passt.h"
> +#include "pcap.h"
> +#include "log.h"
> +#include "vhost_user.h"
> +#include "udp_internal.h"
> +#include "flow.h"
> +#include "flow_table.h"
> +#include "udp_flow.h"
> +#include "udp_vu.h"
> +#include "vu_common.h"
> +
> +/* vhost-user */
> +static const struct virtio_net_hdr vu_header = {
> +	.flags = VIRTIO_NET_HDR_F_DATA_VALID,
> +	.gso_type = VIRTIO_NET_HDR_GSO_NONE,
> +};
> +
> +static struct iovec     iov_vu		[VIRTQUEUE_MAX_SIZE];
> +static struct vu_virtq_element	elem		[VIRTQUEUE_MAX_SIZE];
> +static struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
> +static int in_sg_count;
> +
> +static size_t udp_vu_l2_hdrlen(const struct vu_dev *vdev, bool v6)

Function comment missing.

> +{
> +	size_t l2_hdrlen;
> +
> +	l2_hdrlen = vdev->hdrlen + sizeof(struct ethhdr) +
> +		    sizeof(struct udphdr);
> +
> +	if (v6)
> +		l2_hdrlen += sizeof(struct ipv6hdr);
> +	else
> +		l2_hdrlen += sizeof(struct iphdr);
> +
> +	return l2_hdrlen;
> +}
> +
> +static int udp_vu_sock_recv(const struct ctx *c, union sockaddr_inany *s_in,
> +			    int s, uint32_t events, bool v6, ssize_t *data_len)

Function comment missing.

> +{
> +	struct vu_dev *vdev = c->vdev;
> +	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
> +	int virtqueue_max, iov_cnt, idx, iov_used;
> +	size_t fillsize, size, off, l2_hdrlen;
> +	struct virtio_net_hdr_mrg_rxbuf *vh;
> +	struct msghdr msg  = { 0 };
> +	char *base;
> +
> +	ASSERT(!c->no_udp);
> +
> +	/* Clear any errors first */
> +	if (events & EPOLLERR) {
> +		while (udp_sock_recverr(s))
> +			;
> +	}
> +
> +	if (!(events & EPOLLIN))
> +		return 0;
> +
> +	/* compute L2 header length */
> +
> +	if (vu_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
> +		virtqueue_max = VIRTQUEUE_MAX_SIZE;
> +	else
> +		virtqueue_max = 1;
> +
> +	l2_hdrlen = udp_vu_l2_hdrlen(vdev, v6);
> +
> +	msg.msg_name = s_in;
> +	msg.msg_namelen = sizeof(union sockaddr_inany);
> +
> +	fillsize = USHRT_MAX;
> +	iov_cnt = 0;
> +	in_sg_count = 0;
> +	while (fillsize && iov_cnt < virtqueue_max &&
> +			in_sg_count < ARRAY_SIZE(in_sg)) {

This is much easier to understand compared to the TCP version, by the
way, but of course the TCP version needs to be a bit more complicated
because of the segmentation.

Both iov_cnt and in_sg_count are indices rather than... counts, right?
What about iov_idx (or i) and in_sg_idx?

> +		int ret;
> +
> +		elem[iov_cnt].out_num = 0;
> +		elem[iov_cnt].out_sg = NULL;
> +		elem[iov_cnt].in_num = ARRAY_SIZE(in_sg) - in_sg_count;
> +		elem[iov_cnt].in_sg = &in_sg[in_sg_count];
> +		ret = vu_queue_pop(vdev, vq, &elem[iov_cnt]);
> +		if (ret < 0)
> +			break;
> +		in_sg_count += elem[iov_cnt].in_num;
> +
> +		if (elem[iov_cnt].in_num < 1) {
> +			err("virtio-net receive queue contains no in buffers");
> +			vu_queue_rewind(vq, iov_cnt);
> +			return 0;
> +		}
> +		ASSERT(elem[iov_cnt].in_num == 1);
> +		ASSERT(elem[iov_cnt].in_sg[0].iov_len >= l2_hdrlen);
> +
> +		if (iov_cnt == 0) {
> +			base = elem[iov_cnt].in_sg[0].iov_base;
> +			size = elem[iov_cnt].in_sg[0].iov_len;
> +
> +			/* keep space for the headers */
> +			iov_vu[0].iov_base = base + l2_hdrlen;
> +			iov_vu[0].iov_len = size - l2_hdrlen;
> +		} else {
> +			iov_vu[iov_cnt].iov_base = elem[iov_cnt].in_sg[0].iov_base;
> +			iov_vu[iov_cnt].iov_len = elem[iov_cnt].in_sg[0].iov_len;
> +		}
> +
> +		if (iov_vu[iov_cnt].iov_len > fillsize)
> +			iov_vu[iov_cnt].iov_len = fillsize;
> +
> +		fillsize -= iov_vu[iov_cnt].iov_len;
> +
> +		iov_cnt++;
> +	}
> +	if (iov_cnt == 0)
> +		return 0;
> +
> +	msg.msg_iov = iov_vu;
> +	msg.msg_iovlen = iov_cnt;
> +
> +	*data_len = recvmsg(s, &msg, 0);

Is recvmsg() instead of recvmmsg() by choice/constraint, or just to keep
the initial implementation simpler?

> +	if (*data_len < 0) {
> +		vu_queue_rewind(vq, iov_cnt);
> +		return 0;
> +	}
> +
> +	/* restore original values */
> +	iov_vu[0].iov_base = base;
> +	iov_vu[0].iov_len = size;
> +
> +	/* count the numbers of buffer filled by recvmsg() */
> +	idx = iov_skip_bytes(iov_vu, iov_cnt, l2_hdrlen + *data_len,
> +			     &off);
> +	/* adjust last iov length */
> +	if (idx < iov_cnt)
> +		iov_vu[idx].iov_len = off;
> +	iov_used = idx + !!off;
> +
> +	/* release unused buffers */
> +	vu_queue_rewind(vq, iov_cnt - iov_used);
> +
> +	vh = (struct virtio_net_hdr_mrg_rxbuf *)base;
> +	vh->hdr = vu_header;
> +	if (vdev->hdrlen == sizeof(struct virtio_net_hdr_mrg_rxbuf))
> +		vh->num_buffers = htole16(iov_used);
> +
> +	return iov_used;
> +}
> +
> +static size_t udp_vu_prepare(const struct ctx *c,
> +			     const struct flowside *toside, ssize_t data_len)

Function comment missing.

> +{
> +	const struct vu_dev *vdev = c->vdev;
> +	struct ethhdr *eh;
> +	size_t l4len;
> +
> +	/* ethernet header */
> +	eh = vu_eth(vdev, iov_vu[0].iov_base);
> +
> +	memcpy(eh->h_dest, c->mac_guest, sizeof(eh->h_dest));
> +	memcpy(eh->h_source, c->mac, sizeof(eh->h_source));
> +
> +	/* initialize header */
> +	if (inany_v4(&toside->eaddr) && inany_v4(&toside->faddr)) {
> +		struct iphdr *iph = vu_ip(vdev, iov_vu[0].iov_base);
> +		struct udp_payload_t *bp = vu_payloadv4(vdev,
> +							    iov_vu[0].iov_base);
> +
> +		eh->h_proto = htons(ETH_P_IP);
> +
> +		*iph = (struct iphdr)L2_BUF_IP4_INIT(IPPROTO_UDP);
> +
> +		l4len = udp_update_hdr4(c, iph, bp, toside, data_len);
> +	} else {
> +		struct ipv6hdr *ip6h = vu_ip(vdev, iov_vu[0].iov_base);
> +		struct udp_payload_t *bp = vu_payloadv6(vdev,
> +							    iov_vu[0].iov_base);
> +
> +		eh->h_proto = htons(ETH_P_IPV6);
> +
> +		*ip6h = (struct ipv6hdr)L2_BUF_IP6_INIT(IPPROTO_UDP);
> +
> +		l4len = udp_update_hdr6(c, ip6h, bp, toside, data_len);
> +	}
> +
> +	return l4len;
> +}
> +
> +static void udp_vu_pcap(const struct ctx *c, const struct flowside *toside,
> +			size_t l4len, int iov_used)

Function comment missing.

> +{
> +	const struct in_addr *src = inany_v4(&toside->faddr);
> +	const struct in_addr *dst = inany_v4(&toside->eaddr);
> +	const struct vu_dev *vdev = c->vdev;
> +	char *base = iov_vu[0].iov_base;
> +	size_t size = iov_vu[0].iov_len;
> +	struct udp_payload_t *bp;
> +	uint32_t sum;
> +
> +	if (!*c->pcap)
> +		return;
> +
> +	if (src && dst) {

If we call them src4 and dst4, this logic becomes easier to understand.

> +		bp = vu_payloadv4(vdev, base);
> +		sum = proto_ipv4_header_psum(l4len, IPPROTO_UDP, *src, *dst);
> +	} else {
> +		bp = vu_payloadv6(vdev, base);
> +		sum = proto_ipv6_header_psum(l4len, IPPROTO_UDP,
> +					     &toside->faddr.a6,
> +					     &toside->eaddr.a6);
> +		bp->uh.check = 0; /* by default, set to 0xffff */
> +	}
> +
> +	iov_vu[0].iov_base = &bp->uh;
> +	iov_vu[0].iov_len = size - ((char *)iov_vu[0].iov_base - base);
> +
> +	bp->uh.check = csum_iov(iov_vu, iov_used, sum);
> +
> +	/* set iov for pcap logging */
> +	iov_vu[0].iov_base = base + vdev->hdrlen;
> +	iov_vu[0].iov_len = size - vdev->hdrlen;
> +	pcap_iov(iov_vu, iov_used);
> +
> +	/* restore iov_vu[0] */
> +	iov_vu[0].iov_base = base;
> +	iov_vu[0].iov_len = size;
> +}
> +
> +void udp_vu_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				uint32_t events, const struct timespec *now)

Function comment missing.

> +{
> +	struct vu_dev *vdev = c->vdev;
> +	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
> +	bool v6 = ref.udp.v6;
> +	int i;
> +
> +	for (i = 0; i < UDP_MAX_FRAMES; i++) {
> +		union sockaddr_inany s_in;
> +		flow_sidx_t batchsidx;
> +		uint8_t batchpif;
> +		ssize_t data_len;
> +		int iov_used;
> +
> +		iov_used = udp_vu_sock_recv(c, &s_in, ref.fd,
> +					    events, v6, &data_len);
> +		if (iov_used <= 0)
> +			return;
> +
> +		batchsidx = udp_flow_from_sock(c, ref, &s_in, now);
> +		batchpif = pif_at_sidx(batchsidx);
> +
> +		if (batchpif == PIF_TAP) {
> +			size_t l4len;
> +
> +			l4len = udp_vu_prepare(c, flowside_at_sidx(batchsidx),
> +					       data_len);
> +			udp_vu_pcap(c, flowside_at_sidx(batchsidx), l4len,
> +				    iov_used);
> +			vu_send_frame(vdev, vq, elem, iov_vu, iov_used);
> +		} else if (flow_sidx_valid(batchsidx)) {
> +			flow_sidx_t fromsidx = flow_sidx_opposite(batchsidx);
> +			struct udp_flow *uflow = udp_at_sidx(batchsidx);
> +
> +			flow_err(uflow,
> +				 "No support for forwarding UDP from %s to %s",
> +				 pif_name(pif_at_sidx(fromsidx)),
> +				 pif_name(batchpif));
> +		} else {
> +			debug("Discarding 1 datagram without flow");
> +		}
> +	}
> +}
> +
> +void udp_vu_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> +			        uint32_t events, const struct timespec *now)

Function comment missing.

> +{
> +	flow_sidx_t tosidx = flow_sidx_opposite(ref.flowside);
> +	const struct flowside *toside = flowside_at_sidx(tosidx);
> +	struct vu_dev *vdev = c->vdev;
> +	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
> +	struct udp_flow *uflow = udp_at_sidx(ref.flowside);
> +	uint8_t topif = pif_at_sidx(tosidx);
> +	bool v6 = ref.udp.v6;
> +	int i;
> +
> +	ASSERT(!c->no_udp && uflow);

Pre-existing (in udp_buf_reply_sock_handler()), but I'd rather keep this
as two assertions, so that, should one ever trigger, we know which case
it is.

> +
> +	for (i = 0; i < UDP_MAX_FRAMES; i++) {
> +		union sockaddr_inany s_in;
> +		ssize_t data_len;
> +		int iov_used;
> +
> +		iov_used = udp_vu_sock_recv(c, &s_in, ref.fd,
> +					    events, v6, &data_len);
> +		if (iov_used <= 0)
> +			return;
> +		flow_trace(uflow, "Received 1 datagram on reply socket");
> +		uflow->ts = now->tv_sec;
> +
> +		if (topif == PIF_TAP) {
> +			size_t l4len;
> +
> +			l4len = udp_vu_prepare(c, toside, data_len);
> +			udp_vu_pcap(c, toside, l4len, iov_used);
> +			vu_send_frame(vdev, vq, elem, iov_vu, iov_used);
> +		} else {
> +			uint8_t frompif = pif_at_sidx(ref.flowside);
> +
> +			flow_err(uflow,
> +				 "No support for forwarding UDP from %s to %s",
> +				 pif_name(frompif), pif_name(topif));
> +		}
> +	}
> +}
> diff --git a/udp_vu.h b/udp_vu.h
> new file mode 100644
> index 000000000000..0db7558914d9
> --- /dev/null
> +++ b/udp_vu.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + * Copyright Red Hat
> + * Author: Laurent Vivier <lvivier@redhat.com>
> + */
> +
> +#ifndef UDP_VU_H
> +#define UDP_VU_H
> +
> +void udp_vu_listen_sock_handler(const struct ctx *c, union epoll_ref ref,
> +				uint32_t events, const struct timespec *now);
> +void udp_vu_reply_sock_handler(const struct ctx *c, union epoll_ref ref,
> +			       uint32_t events, const struct timespec *now);
> +#endif /* UDP_VU_H */
> diff --git a/vhost_user.c b/vhost_user.c
> index c4cd25fae84e..e65b550774b7 100644
> --- a/vhost_user.c
> +++ b/vhost_user.c
> @@ -52,7 +52,6 @@
>   * 			     this is part of the vhost-user backend
>   * 			     convention.
>   */
> -/* cppcheck-suppress unusedFunction */
>  void vu_print_capabilities(void)
>  {
>  	info("{");
> @@ -163,8 +162,7 @@ static void vmsg_close_fds(const struct vhost_user_msg *vmsg)
>   */
>  static void vu_remove_watch(const struct vu_dev *vdev, int fd)
>  {
> -	(void)vdev;
> -	(void)fd;
> +	epoll_ctl(vdev->context->epollfd, EPOLL_CTL_DEL, fd, NULL);
>  }
>  
>  /**
> @@ -426,7 +424,6 @@ static bool map_ring(struct vu_dev *vdev, struct vu_virtq *vq)
>   *
>   * Return: 0 if the zone in a mapped memory region, -1 otherwise
>   */
> -/* cppcheck-suppress unusedFunction */
>  int vu_packet_check_range(void *buf, size_t offset, size_t len,
>  			  const char *start)
>  {
> @@ -517,6 +514,14 @@ static bool vu_set_mem_table_exec(struct vu_dev *vdev,
>  		}
>  	}
>  
> +	/* As vu_packet_check_range() has no access to the number of
> +	 * memory regions, mark the end of the array with mmap_addr = 0
> +	 */
> +	ASSERT(vdev->nregions < VHOST_USER_MAX_RAM_SLOTS - 1);
> +	vdev->regions[vdev->nregions].mmap_addr = 0;
> +
> +	tap_sock_update_buf(vdev->regions, 0);
> +
>  	return false;
>  }
>  
> @@ -637,8 +642,12 @@ static bool vu_get_vring_base_exec(struct vu_dev *vdev,
>   */
>  static void vu_set_watch(const struct vu_dev *vdev, int fd)
>  {
> -	(void)vdev;
> -	(void)fd;
> +	union epoll_ref ref = { .type = EPOLL_TYPE_VHOST_KICK, .fd = fd };
> +	struct epoll_event ev = { 0 };
> +
> +	ev.data.u64 = ref.u64;
> +	ev.events = EPOLLIN;
> +	epoll_ctl(vdev->context->epollfd, EPOLL_CTL_ADD, fd, &ev);
>  }
>  
>  /**
> @@ -678,7 +687,6 @@ static int vu_wait_queue(const struct vu_virtq *vq)
>   *
>   * Return: number of bytes sent, -1 if there is an error
>   */
> -/* cppcheck-suppress unusedFunction */
>  int vu_send(struct vu_dev *vdev, const void *buf, size_t size)
>  {
>  	struct vu_virtq *vq = &vdev->vq[VHOST_USER_RX_QUEUE];
> @@ -864,7 +872,6 @@ static void vu_handle_tx(struct vu_dev *vdev, int index,
>   * @vdev:	vhost-user device
>   * @ref:	epoll reference information
>   */
> -/* cppcheck-suppress unusedFunction */
>  void vu_kick_cb(struct vu_dev *vdev, union epoll_ref ref,
>  		const struct timespec *now)
>  {
> @@ -1102,11 +1109,11 @@ static bool vu_set_vring_enable_exec(struct vu_dev *vdev,
>   * @c:		execution context
>   * @vdev:	vhost-user device
>   */
> -/* cppcheck-suppress unusedFunction */
>  void vu_init(struct ctx *c, struct vu_dev *vdev)
>  {
>  	int i;
>  
> +	c->vdev = vdev;
>  	vdev->context = c;
>  	vdev->hdrlen = 0;
>  	for (i = 0; i < VHOST_USER_MAX_QUEUES; i++) {
> @@ -1170,7 +1177,7 @@ void vu_cleanup(struct vu_dev *vdev)
>   */
>  static void vu_sock_reset(struct vu_dev *vdev)
>  {
> -	(void)vdev;
> +	tap_sock_reset(vdev->context);
>  }
>  
>  /**
> @@ -1179,7 +1186,6 @@ static void vu_sock_reset(struct vu_dev *vdev)
>   * @fd:		vhost-user message socket
>   * @events:	epoll events
>   */
> -/* cppcheck-suppress unusedFunction */
>  void tap_handler_vu(struct vu_dev *vdev, int fd, uint32_t events)
>  {
>  	struct vhost_user_msg msg = { 0 };
> diff --git a/virtio.c b/virtio.c
> index d02e6e04701d..55fc647842bb 100644
> --- a/virtio.c
> +++ b/virtio.c
> @@ -559,7 +559,6 @@ void vu_queue_unpop(struct vu_virtq *vq)
>   * @vq:		Virtqueue
>   * @num:	Number of element to unpop
>   */
> -/* cppcheck-suppress unusedFunction */
>  bool vu_queue_rewind(struct vu_virtq *vq, unsigned int num)
>  {
>  	if (num > vq->inuse)
> diff --git a/vu_common.c b/vu_common.c
> new file mode 100644
> index 000000000000..611c44a39142
> --- /dev/null
> +++ b/vu_common.c
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later

// SPDX...

> + * Copyright Red Hat
> + * Author: Laurent Vivier <lvivier@redhat.com>
> + *
> + * common_vu.c - vhost-user common UDP and TCP functions
> + */
> +
> +#include <unistd.h>
> +#include <sys/uio.h>
> +
> +#include "util.h"
> +#include "passt.h"
> +#include "vhost_user.h"
> +#include "vu_common.h"
> +
> +void vu_send_frame(const struct vu_dev *vdev, struct vu_virtq *vq,
> +		   struct vu_virtq_element *elem, const struct iovec *iov_vu,
> +		   int iov_used)

Function comment missing.

> +{
> +	int i;
> +
> +	for (i = 0; i < iov_used; i++)
> +		vu_queue_fill(vq, &elem[i], iov_vu[i].iov_len, i);
> +
> +	vu_queue_flush(vq, iov_used);
> +	vu_queue_notify(vdev, vq);
> +}
> diff --git a/vu_common.h b/vu_common.h
> new file mode 100644
> index 000000000000..d2ea46bd379b
> --- /dev/null
> +++ b/vu_common.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + * Copyright Red Hat
> + * Author: Laurent Vivier <lvivier@redhat.com>
> + *
> + * vhost-user common UDP and TCP functions
> + */
> +
> +#ifndef VU_COMMON_H
> +#define VU_COMMON_H
> +
> +static inline void *vu_eth(const struct vu_dev *vdev, void *base)

Function comments missing (but these are really obvious ones, so I
don't actually care that much).

> +{
> +	return ((char *)base + vdev->hdrlen);
> +}
> +
> +static inline void *vu_ip(const struct vu_dev *vdev, void *base)
> +{
> +	return (struct ethhdr *)vu_eth(vdev, base) + 1;
> +}
> +
> +static inline void *vu_payloadv4(const struct vu_dev *vdev, void *base)
> +{
> +	return (struct iphdr *)vu_ip(vdev, base) + 1;
> +}
> +
> +static inline void *vu_payloadv6(const struct vu_dev *vdev, void *base)
> +{
> +	return (struct ipv6hdr *)vu_ip(vdev, base) + 1;
> +}
> +
> +void vu_send_frame(const struct vu_dev *vdev, struct vu_virtq *vq,
> +		   struct vu_virtq_element *elem, const struct iovec *iov_vu,
> +		   int iov_used);
> +#endif /* VU_COMMON_H */

-- 
Stefano


  parent reply	other threads:[~2024-08-23 12:32 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-15 15:50 [PATCH v3 0/4] Add vhost-user support to passt. (part 3) Laurent Vivier
2024-08-15 15:50 ` [PATCH v3 1/4] packet: replace struct desc by struct iovec Laurent Vivier
2024-08-20  0:27   ` David Gibson
2024-08-15 15:50 ` [PATCH v3 2/4] vhost-user: introduce virtio API Laurent Vivier
2024-08-20  1:00   ` David Gibson
2024-08-22 22:14   ` Stefano Brivio
2024-08-15 15:50 ` [PATCH v3 3/4] vhost-user: introduce vhost-user API Laurent Vivier
2024-08-22 22:14   ` Stefano Brivio
2024-08-26  5:27     ` David Gibson
2024-08-26  7:55       ` Stefano Brivio
2024-08-26  9:53         ` David Gibson
2024-08-26  5:26   ` David Gibson
2024-08-26 22:14     ` Stefano Brivio
2024-08-27  4:42       ` David Gibson
2024-09-05  9:58     ` Laurent Vivier
2024-08-15 15:50 ` [PATCH v3 4/4] vhost-user: add vhost-user Laurent Vivier
2024-08-22  9:59   ` Stefano Brivio
2024-08-22 22:14   ` Stefano Brivio
2024-08-23 12:32   ` Stefano Brivio [this message]
2024-08-20 22:41 ` [PATCH v3 0/4] Add vhost-user support to passt. (part 3) Stefano Brivio
2024-08-22 16:53   ` Stefano Brivio
2024-08-23 12:32     ` Stefano Brivio

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240823143236.25ca0ac7@elisabeth \
    --to=sbrivio@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=passt-dev@passt.top \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).