af_vsock: implement SEQPACKET receive loop
authorArseny Krasnov <arseny.krasnov@kaspersky.com>
Fri, 11 Jun 2021 11:10:34 +0000 (14:10 +0300)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Jun 2021 20:32:46 +0000 (13:32 -0700)
Add receive loop for SEQPACKET. It looks like receive loop for
STREAM, but there are differences:
1) It doesn't call notify callbacks.
2) It doesn't care about 'SO_SNDLOWAT' and 'SO_RCVLOWAT' values, because
   there is no sense for these values in SEQPACKET case.
3) It waits until whole record is received.
4) It processes and sets 'MSG_TRUNC' flag.

So to avoid extra conditions for two types of socket inside one loop, two
independent functions were created.

Signed-off-by: Arseny Krasnov <arseny.krasnov@kaspersky.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/af_vsock.h
net/vmw_vsock/af_vsock.c

index b1c7172..4d7cf6b 100644 (file)
@@ -135,6 +135,10 @@ struct vsock_transport {
        bool (*stream_is_active)(struct vsock_sock *);
        bool (*stream_allow)(u32 cid, u32 port);
 
+       /* SEQ_PACKET. */
+       ssize_t (*seqpacket_dequeue)(struct vsock_sock *vsk, struct msghdr *msg,
+                                    int flags);
+
        /* Notification. */
        int (*notify_poll_in)(struct vsock_sock *, size_t, bool *);
        int (*notify_poll_out)(struct vsock_sock *, size_t, bool *);
index c4f6bfa..87ae26b 100644 (file)
@@ -1974,6 +1974,56 @@ out:
        return err;
 }
 
+static int __vsock_seqpacket_recvmsg(struct sock *sk, struct msghdr *msg,
+                                    size_t len, int flags)
+{
+       const struct vsock_transport *transport;
+       struct vsock_sock *vsk;
+       ssize_t record_len;
+       long timeout;
+       int err = 0;
+       DEFINE_WAIT(wait);
+
+       vsk = vsock_sk(sk);
+       transport = vsk->transport;
+
+       timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
+       err = vsock_wait_data(sk, &wait, timeout, NULL, 0);
+       if (err <= 0)
+               goto out;
+
+       record_len = transport->seqpacket_dequeue(vsk, msg, flags);
+
+       if (record_len < 0) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       if (sk->sk_err) {
+               err = -sk->sk_err;
+       } else if (sk->sk_shutdown & RCV_SHUTDOWN) {
+               err = 0;
+       } else {
+               /* User sets MSG_TRUNC, so return real length of
+                * packet.
+                */
+               if (flags & MSG_TRUNC)
+                       err = record_len;
+               else
+                       err = len - msg_data_left(msg);
+
+               /* Always set MSG_TRUNC if real length of packet is
+                * bigger than user's buffer.
+                */
+               if (record_len > len)
+                       msg->msg_flags |= MSG_TRUNC;
+       }
+
+out:
+       return err;
+}
+
 static int
 vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
                          int flags)
@@ -2029,7 +2079,10 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
                goto out;
        }
 
-       err = __vsock_stream_recvmsg(sk, msg, len, flags);
+       if (sk->sk_type == SOCK_STREAM)
+               err = __vsock_stream_recvmsg(sk, msg, len, flags);
+       else
+               err = __vsock_seqpacket_recvmsg(sk, msg, len, flags);
 
 out:
        release_sock(sk);