Datagram receive queues hold whole messages, and transports may charge queued datagrams to the socket send buffer. Report the size of the next datagram payload for SIOCINQ under the receive-queue lock, so concurrent recvmsg() cannot free the skb during the query. Let transports override the length calculation to exclude headers such as VMCI's. Report datagram write readiness only while the send buffer has space. Set the asynchronous no-space flag when it is full so freeing pending send buffers can wake poll waiters. Signed-off-by: Sergio Lopez --- include/net/af_vsock.h | 2 ++ net/vmw_vsock/af_vsock.c | 32 ++++++++++++++++++++++++-------- net/vmw_vsock/vmci_transport.c | 9 +++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 19bd9c1c3e5b..0c8b8a815b04 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -124,6 +124,8 @@ struct vsock_transport { int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, struct msghdr *, size_t len); bool (*dgram_allow)(struct vsock_sock *vsk, u32 cid, u32 port); + /* Optional payload length override, called with the receive queue locked. */ + size_t (*dgram_payload_len)(const struct sk_buff *skb); /* STREAM. */ /* TODO: stream_bind() */ diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 5d1d85e5f519..dc1add2d2e89 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1344,16 +1344,20 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, mask |= vsock_poll_shutdown(sk, peer_shutdown); /* For datagram sockets we can read if there is something in - * the queue and write as long as the socket isn't shutdown for - * sending. + * the queue and write if the send buffer has space and the + * socket isn't shutdown for sending. */ if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || (sk->sk_shutdown & RCV_SHUTDOWN)) { mask |= EPOLLIN | EPOLLRDNORM; } - if (!(sk->sk_shutdown & SEND_SHUTDOWN)) - mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { + if (sock_writeable(sk)) + mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + else + sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); + } } else if (sock_type_connectible(sk->sk_type)) { const struct vsock_transport *transport; @@ -1617,10 +1621,22 @@ static int vsock_do_ioctl(struct socket *sock, unsigned int cmd, break; } - n_bytes = vsock_stream_has_data(vsk); - if (n_bytes < 0) { - ret = n_bytes; - break; + if (sk->sk_type == SOCK_DGRAM) { + struct sk_buff *skb; + + spin_lock_bh(&sk->sk_receive_queue.lock); + skb = skb_peek(&sk->sk_receive_queue); + if (skb && vsk->transport->dgram_payload_len) + n_bytes = vsk->transport->dgram_payload_len(skb); + else + n_bytes = skb ? skb->len : 0; + spin_unlock_bh(&sk->sk_receive_queue.lock); + } else { + n_bytes = vsock_stream_has_data(vsk); + if (n_bytes < 0) { + ret = n_bytes; + break; + } } ret = put_user(n_bytes, arg); break; diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 1f186e8f8364..2b9493e987d1 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -1761,6 +1761,14 @@ static int vmci_transport_dgram_enqueue( return err - sizeof(*dg); } +static size_t vmci_transport_dgram_payload_len(const struct sk_buff *skb) +{ + if (skb->len < sizeof(struct vmci_datagram)) + return 0; + + return skb->len - sizeof(struct vmci_datagram); +} + static int vmci_transport_dgram_dequeue(struct vsock_sock *vsk, struct msghdr *msg, size_t len, int flags) @@ -2075,6 +2083,7 @@ static struct vsock_transport vmci_transport = { .connect = vmci_transport_connect, .dgram_bind = vmci_transport_dgram_bind, .dgram_dequeue = vmci_transport_dgram_dequeue, + .dgram_payload_len = vmci_transport_dgram_payload_len, .dgram_enqueue = vmci_transport_dgram_enqueue, .dgram_allow = vmci_transport_dgram_allow, .stream_dequeue = vmci_transport_stream_dequeue, -- 2.56.0