af_vsock: implement SEQPACKET receive loop

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>
This commit is contained in:
Arseny Krasnov 2021-06-11 14:10:34 +03:00 committed by David S. Miller
parent 19c1b90e19
commit 9942c192b2
2 changed files with 58 additions and 1 deletions

View 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 *);

View 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);