On Wed, Oct 28, 2026 at 05:04:32PM +0100, Sergio Lopez wrote: > Add the datagram feature bit and packet type, and implement the common > virtio transport operations needed to bind, send, and receive > datagrams. > > Send each message as an OP_RW packet with a maximum 64 KiB payload, > including support for empty messages. Bound pending sends by the > socket send buffer instead of connection-oriented peer credits. Charge > pending payload bytes before handing the skb to the backend and > release that charge exactly once on completion, rejection, or queued > drops. > > Deliver incoming datagrams through the bound table without entering > the connection state machine. Validate the transport and connected > peer under the socket lock, including after shutdown, and silently > discard undeliverable messages. Use the socket datagram queue for > recvmsg(), including source addresses, peeking, and truncation > reporting based on the payload length after socket filtering. Reject > MSG_OOB and MSG_ERRQUEUE before accessing the receive queue. Reject > datagram sockmap admission until its receive callbacks support it. > > Signed-off-by: Sergio Lopez <[email protected]> > --- > include/linux/virtio_vsock.h | 2 + > include/uapi/linux/virtio_vsock.h | 2 + > net/vmw_vsock/virtio_transport_common.c | 150 > +++++++++++++++++++++++++++++--- > net/vmw_vsock/vsock_bpf.c | 3 +- > 4 files changed, 142 insertions(+), 15 deletions(-) > > diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h > index f91704731057..0bccae661221 100644 > --- a/include/linux/virtio_vsock.h > +++ b/include/linux/virtio_vsock.h > @@ -12,6 +12,8 @@ > struct virtio_vsock_skb_cb { > bool reply; > bool tap_delivered; > + /* Datagram payload is still charged to bytes_unsent. */ > + bool tx_accounted; > u32 offset; > }; > > diff --git a/include/uapi/linux/virtio_vsock.h > b/include/uapi/linux/virtio_vsock.h > index 64738838bee5..27b4b2b8bf13 100644 > --- a/include/uapi/linux/virtio_vsock.h > +++ b/include/uapi/linux/virtio_vsock.h > @@ -40,6 +40,7 @@ > > /* The feature bitmap for virtio vsock */ > #define VIRTIO_VSOCK_F_SEQPACKET 1 /* SOCK_SEQPACKET supported */ > +#define VIRTIO_VSOCK_F_DGRAM 3 /* SOCK_DGRAM supported */ > > struct virtio_vsock_config { > __le64 guest_cid; > @@ -69,6 +70,7 @@ struct virtio_vsock_hdr { > enum virtio_vsock_type { > VIRTIO_VSOCK_TYPE_STREAM = 1, > VIRTIO_VSOCK_TYPE_SEQPACKET = 2, > + VIRTIO_VSOCK_TYPE_DGRAM = 3, > }; > > enum virtio_vsock_op { > diff --git a/net/vmw_vsock/virtio_transport_common.c > b/net/vmw_vsock/virtio_transport_common.c > index 1e762a480df4..ae33630d99e7 100644 > --- a/net/vmw_vsock/virtio_transport_common.c > +++ b/net/vmw_vsock/virtio_transport_common.c > @@ -204,10 +204,18 @@ static u16 virtio_transport_get_type(struct sock *sk) > { > if (sk->sk_type == SOCK_STREAM) > return VIRTIO_VSOCK_TYPE_STREAM; > + else if (sk->sk_type == SOCK_DGRAM) > + return VIRTIO_VSOCK_TYPE_DGRAM; > else > return VIRTIO_VSOCK_TYPE_SEQPACKET; > } > > +static void virtio_transport_dgram_skb_destructor(struct sk_buff *skb) > +{ > + virtio_transport_consume_skb_sent(skb, false); > + sock_wfree(skb); > +} > + > /* Returns new sk_buff on success, otherwise returns NULL. */ > static struct sk_buff *virtio_transport_alloc_skb(struct > virtio_vsock_pkt_info *info, > size_t payload_len, > @@ -303,6 +311,7 @@ static int virtio_transport_send_pkt_info(struct > vsock_sock *vsk, > u32 src_cid, src_port, dst_cid, dst_port; > const struct virtio_transport *t_ops; > struct iov_iter_state msg_iter_state; > + struct sock *sk = sk_vsock(vsk); > struct virtio_vsock_sock *vvs; > struct ubuf_info *uarg = NULL; > u32 pkt_len = info->pkt_len; > @@ -329,12 +338,24 @@ static int virtio_transport_send_pkt_info(struct > vsock_sock *vsk, > > vvs = vsk->trans; > > - /* virtio_transport_get_credit might return less than pkt_len credit */ > - pkt_len = virtio_transport_get_credit(vvs, pkt_len); > + if (info->type == VIRTIO_VSOCK_TYPE_DGRAM) { > + if (pkt_len > VIRTIO_VSOCK_MAX_PKT_BUF_SIZE) > + return -EMSGSIZE; > > - /* Do not send zero length OP_RW pkt */ > - if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) > - return pkt_len; > + /* Datagram sends are serialized by the socket lock. */ > + if (sk_wmem_alloc_get(sk) >= READ_ONCE(sk->sk_sndbuf)) { > + sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); > + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); > + return -EAGAIN; > + } > + } else { > + /* virtio_transport_get_credit might return less than pkt_len > credit */ > + pkt_len = virtio_transport_get_credit(vvs, pkt_len); > + > + /* Do not send zero length OP_RW pkt */ > + if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) > + return pkt_len; > + } > > if (info->msg && (info->msg->msg_flags & MSG_ZEROCOPY)) { > /* If 'info->msg' is not NULL, this is only VIRTIO_VSOCK_OP_RW. > @@ -348,7 +369,8 @@ static int virtio_transport_send_pkt_info(struct > vsock_sock *vsk, > uarg = msg_zerocopy_realloc(sk_vsock(vsk), pkt_len, > NULL, false); > if (!uarg) { > - virtio_transport_put_credit(vvs, pkt_len); > + if (info->type != VIRTIO_VSOCK_TYPE_DGRAM) > + virtio_transport_put_credit(vvs, > pkt_len); > return -ENOMEM; > } > > @@ -393,7 +415,15 @@ static int virtio_transport_send_pkt_info(struct > vsock_sock *vsk, > break; > } > > - virtio_transport_inc_tx_pkt(vvs, skb); > + if (info->type == VIRTIO_VSOCK_TYPE_DGRAM) { > + spin_lock_bh(&vvs->tx_lock); > + vvs->bytes_unsent += skb_len; > + spin_unlock_bh(&vvs->tx_lock); > + VIRTIO_VSOCK_SKB_CB(skb)->tx_accounted = true; > + skb->destructor = virtio_transport_dgram_skb_destructor; > + } else { > + virtio_transport_inc_tx_pkt(vvs, skb); > + } > > ret = t_ops->send_pkt(skb, info->net); > if (ret < 0) > @@ -416,7 +446,8 @@ static int virtio_transport_send_pkt_info(struct > vsock_sock *vsk, > if (info->msg && ret < 0) > iov_iter_restore(&info->msg->msg_iter, &msg_iter_state); > > - virtio_transport_put_credit(vvs, rest_len); > + if (info->type != VIRTIO_VSOCK_TYPE_DGRAM) > + virtio_transport_put_credit(vvs, rest_len); > > /* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1. > * skb_zcopy_set() increases it for each skb, so we can drop that > @@ -620,7 +651,9 @@ void virtio_transport_consume_skb_sent(struct sk_buff > *skb, bool consume) > { > struct sock *s = skb->sk; > > - if (s && skb->len) { > + if (s && skb->len && > + (s->sk_type != SOCK_DGRAM || > + VIRTIO_VSOCK_SKB_CB(skb)->tx_accounted)) { > struct vsock_sock *vs = vsock_sk(s); > struct virtio_vsock_sock *vvs; > > @@ -631,6 +664,9 @@ void virtio_transport_consume_skb_sent(struct sk_buff > *skb, bool consume) > spin_unlock_bh(&vvs->tx_lock); > } > > + if (s && s->sk_type == SOCK_DGRAM) > + VIRTIO_VSOCK_SKB_CB(skb)->tx_accounted = false; > + > if (consume) > consume_skb(skb); > } > @@ -999,7 +1035,47 @@ virtio_transport_dgram_dequeue(struct vsock_sock *vsk, > struct msghdr *msg, > size_t len, int flags) > { > - return -EOPNOTSUPP; > + struct sock *sk = sk_vsock(vsk); > + struct virtio_vsock_hdr *hdr; > + struct sk_buff *skb; > + size_t payload_len; > + int err; > + > + if (flags & (MSG_OOB | MSG_ERRQUEUE)) > + return -EOPNOTSUPP; > + > + skb = skb_recv_datagram(sk, flags, &err); > + if (!skb) > + return err; > + > + hdr = virtio_vsock_hdr(skb); > + payload_len = skb->len; > + > + if (payload_len > len) { > + payload_len = len; > + msg->msg_flags |= MSG_TRUNC; > + } > + > + err = skb_copy_datagram_iter(skb, 0, &msg->msg_iter, payload_len); > + if (err) > + goto out; > + > + if (msg->msg_name) { > + DECLARE_SOCKADDR(struct sockaddr_vm *, vm_addr, msg->msg_name); > + > + vsock_addr_init(vm_addr, le64_to_cpu(hdr->src_cid), > + le32_to_cpu(hdr->src_port)); > + msg->msg_namelen = sizeof(*vm_addr); > + } > + > + if (flags & MSG_TRUNC) > + err = skb->len; > + else > + err = payload_len; > + > +out: > + skb_free_datagram(sk, skb); > + return err; > } > EXPORT_SYMBOL_GPL(virtio_transport_dgram_dequeue); > > @@ -1202,13 +1278,13 @@ EXPORT_SYMBOL_GPL(virtio_transport_stream_is_active); > int virtio_transport_dgram_bind(struct vsock_sock *vsk, > struct sockaddr_vm *addr) > { > - return -EOPNOTSUPP; > + return vsock_bind_common(vsk, addr); > } > EXPORT_SYMBOL_GPL(virtio_transport_dgram_bind); > > bool virtio_transport_dgram_allow(struct vsock_sock *vsk, u32 cid, u32 port) > { > - return false; > + return true; > } > EXPORT_SYMBOL_GPL(virtio_transport_dgram_allow); > > @@ -1246,7 +1322,17 @@ virtio_transport_dgram_enqueue(struct vsock_sock *vsk, > struct msghdr *msg, > size_t dgram_len) > { > - return -EOPNOTSUPP; > + struct virtio_vsock_pkt_info info = { > + .op = VIRTIO_VSOCK_OP_RW, > + .msg = msg, > + .pkt_len = dgram_len, > + .vsk = vsk, > + .remote_cid = remote_addr->svm_cid, > + .remote_port = remote_addr->svm_port, > + .net = sock_net(sk_vsock(vsk)), > + }; > + > + return virtio_transport_send_pkt_info(vsk, &info); > } > EXPORT_SYMBOL_GPL(virtio_transport_dgram_enqueue); > > @@ -1771,7 +1857,8 @@ virtio_transport_recv_listen(struct sock *sk, struct > sk_buff *skb, > static bool virtio_transport_valid_type(u16 type) > { > return (type == VIRTIO_VSOCK_TYPE_STREAM) || > - (type == VIRTIO_VSOCK_TYPE_SEQPACKET); > + (type == VIRTIO_VSOCK_TYPE_SEQPACKET) || > + (type == VIRTIO_VSOCK_TYPE_DGRAM); > } > > /* We are under the virtio-vsock's vsock->rx_lock or vhost-vsock's vq->mutex > @@ -1805,6 +1892,41 @@ void virtio_transport_recv_pkt(struct virtio_transport > *t, > goto free_pkt; > } > > + /* Dgram packets are delivered directly to bound sockets without > + * going through the connection state machine. Undeliverable > + * datagrams are silently dropped (no RST). > + */ > + if (le16_to_cpu(hdr->type) == VIRTIO_VSOCK_TYPE_DGRAM) { > + if (le16_to_cpu(hdr->op) != VIRTIO_VSOCK_OP_RW) > + goto free_pkt; > + > + sk = vsock_find_bound_socket_net(&dst, net); > + > + if (!sk || sk->sk_type != SOCK_DGRAM) { > + if (sk) > + sock_put(sk); > + goto free_pkt; > + } > + > + vsk = vsock_sk(sk); > + lock_sock(sk); > + if (sock_flag(sk, SOCK_DEAD) || > + vsk->transport != &t->transport || > + (sk->sk_socket->state != SS_UNCONNECTED && > + !vsock_check_source(vsk, &t->transport, &src))) { > + release_sock(sk); > + sock_put(sk); > + goto free_pkt; > + } > + > + if (sock_queue_rcv_skb(sk, skb)) > + kfree_skb(skb);
I'd love to see the receive path be free of lock_sock the way that UDP does. My understanding from udp is that we can avoid the race with close/SOCK_DEAD by using SOCK_RCU_FREE and putting this receive in an rcu read-side section. I think this makes sk->sk_socket->state safe inside the RCU read side section too. I think vsock_check_source() in a later patch goes away with virtio_transport_dgram_source_allow(), so I think maybe that patch should come before this? Best, Bobby > + > + release_sock(sk); > + sock_put(sk); > + return; > + } > + > /* The socket must be in connected or bound table > * otherwise send reset back > */ > diff --git a/net/vmw_vsock/vsock_bpf.c b/net/vmw_vsock/vsock_bpf.c > index 9049d2648646..296e1fa7970b 100644 > --- a/net/vmw_vsock/vsock_bpf.c > +++ b/net/vmw_vsock/vsock_bpf.c > @@ -161,7 +161,8 @@ int vsock_bpf_update_proto(struct sock *sk, struct > sk_psock *psock, bool restore > if (!vsk->transport) > return -ENODEV; > > - if (!vsk->transport->read_skb) > + /* Datagram receive is not implemented by the sockmap callbacks. */ > + if (sk->sk_type == SOCK_DGRAM || !vsk->transport->read_skb) > return -EOPNOTSUPP; > > vsock_bpf_check_needs_rebuild(psock->sk_proto); > > -- > 2.56.0 >
