--- v5
+++ v3
@@ -1,94 +1,141 @@
-This moves wait loop for data to dedicated function, because later
-it will be used by SEQPACKET data receive loop.
+This prepares 'vsock_connectible_recvmg()' to call SEQPACKET receive
+loop:
+1) Some shared check left in this function, then socket type
+ specific receive loop is called.
+2) Stream receive loop is moved to separate function.
Signed-off-by: Arseny Krasnov <arseny.krasnov@kaspersky.com>
---
- net/vmw_vsock/af_vsock.c | 155 +++++++++++++++++++++------------------
- 1 file changed, 83 insertions(+), 72 deletions(-)
+ net/vmw_vsock/af_vsock.c | 242 ++++++++++++++++++++++-----------------
+ 1 file changed, 138 insertions(+), 104 deletions(-)
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
-index 656370e11707..6cf7bb977aa1 100644
+index c9ce57db9554..524df8fc84cd 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
-@@ -1832,6 +1832,68 @@ static int vsock_connectible_sendmsg(struct socket *sock, struct msghdr *msg,
- return err;
+@@ -1858,65 +1858,69 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg,
+ return vsock_connectible_sendmsg(sock, msg, len);
}
+-
+-static int
+-vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+- int flags)
+static int vsock_wait_data(struct sock *sk, struct wait_queue_entry *wait,
+ long timeout,
+ struct vsock_transport_recv_notify_data *recv_data,
+ size_t target)
+ {
+- struct sock *sk;
++ int err = 0;
+ struct vsock_sock *vsk;
+ const struct vsock_transport *transport;
+- int err;
+- size_t target;
+- ssize_t copied;
+- long timeout;
+- struct vsock_transport_recv_notify_data recv_data;
+-
+- DEFINE_WAIT(wait);
+
+- sk = sock->sk;
+ vsk = vsock_sk(sk);
+ transport = vsk->transport;
+- err = 0;
+-
+- lock_sock(sk);
+-
+- if (!transport || sk->sk_state != TCP_ESTABLISHED) {
+- /* Recvmsg is supposed to return 0 if a peer performs an
+- * orderly shutdown. Differentiate between that case and when a
+- * peer has not connected or a local shutdown occured with the
+- * SOCK_DONE flag.
+- */
+- if (sock_flag(sk, SOCK_DONE))
+- err = 0;
+- else
+- err = -ENOTCONN;
+
++ if (sk->sk_err != 0 ||
++ (sk->sk_shutdown & RCV_SHUTDOWN) ||
++ (vsk->peer_shutdown & SEND_SHUTDOWN)) {
++ err = -1;
+ goto out;
+ }
+-
+- if (flags & MSG_OOB) {
+- err = -EOPNOTSUPP;
++ /* Don't wait for non-blocking sockets. */
++ if (timeout == 0) {
++ err = -EAGAIN;
+ goto out;
+ }
+
+- /* We don't check peer_shutdown flag here since peer may actually shut
+- * down, but there can be data in the queue that a local socket can
+- * receive.
+- */
+- if (sk->sk_shutdown & RCV_SHUTDOWN) {
+- err = 0;
+- goto out;
++ if (recv_data) {
++ err = transport->notify_recv_pre_block(vsk, target, recv_data);
++ if (err < 0)
++ goto out;
+ }
+
+- /* It is valid on Linux to pass in a zero-length receive buffer. This
+- * is not an error. We may as well bail out now.
+- */
+- if (!len) {
+- err = 0;
++ release_sock(sk);
++ timeout = schedule_timeout(timeout);
++ lock_sock(sk);
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeout);
++ goto out;
++ } else if (timeout == 0) {
++ err = -EAGAIN;
+ goto out;
+ }
+
++out:
++ finish_wait(sk_sleep(sk), wait);
++ return err;
++}
++
++static int __vsock_stream_recvmsg(struct sock *sk, struct msghdr *msg,
++ size_t len, int flags)
+{
++ struct vsock_transport_recv_notify_data recv_data;
+ const struct vsock_transport *transport;
+ struct vsock_sock *vsk;
-+ s64 data;
++ ssize_t copied;
++ size_t target;
++ long timeout;
+ int err;
+
++ DEFINE_WAIT(wait);
++
+ vsk = vsock_sk(sk);
-+ err = 0;
+ transport = vsk->transport;
-+ prepare_to_wait(sk_sleep(sk), wait, TASK_INTERRUPTIBLE);
-+
-+ while ((data = vsock_stream_has_data(vsk)) == 0) {
-+ if (sk->sk_err != 0 ||
-+ (sk->sk_shutdown & RCV_SHUTDOWN) ||
-+ (vsk->peer_shutdown & SEND_SHUTDOWN)) {
-+ break;
-+ }
-+
-+ /* Don't wait for non-blocking sockets. */
-+ if (timeout == 0) {
-+ err = -EAGAIN;
-+ break;
-+ }
-+
-+ if (recv_data) {
-+ err = transport->notify_recv_pre_block(vsk, target, recv_data);
-+ if (err < 0)
-+ break;
-+ }
-+
-+ release_sock(sk);
-+ timeout = schedule_timeout(timeout);
-+ lock_sock(sk);
-+
-+ if (signal_pending(current)) {
-+ err = sock_intr_errno(timeout);
-+ break;
-+ } else if (timeout == 0) {
-+ err = -EAGAIN;
-+ break;
-+ }
-+ }
-+
-+ finish_wait(sk_sleep(sk), wait);
-+
-+ if (err)
-+ return err;
-+
-+ /* Internal transport error when checking for available
-+ * data. XXX This should be changed to a connection
-+ * reset in a later change.
-+ */
-+ if (data < 0)
-+ return -ENOMEM;
-+
-+ return data;
-+}
-+
- static int
- vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
- int flags)
-@@ -1911,85 +1973,34 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
++
+ /* We must not copy less than target bytes into the user's buffer
+ * before returning successfully, so we wait for the consume queue to
+ * have that much data to consume before dequeueing. Note that this
+@@ -1937,85 +1941,53 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
while (1) {
-- s64 ready;
--
-- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
-- ready = vsock_stream_has_data(vsk);
--
-- if (ready == 0) {
++ ssize_t read;
+ s64 ready;
+
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ ready = vsock_stream_has_data(vsk);
+
+ if (ready == 0) {
- if (sk->sk_err != 0 ||
- (sk->sk_shutdown & RCV_SHUTDOWN) ||
- (vsk->peer_shutdown & SEND_SHUTDOWN)) {
@@ -101,8 +148,7 @@
- finish_wait(sk_sleep(sk), &wait);
- break;
- }
-+ ssize_t read;
-
+-
- err = transport->notify_recv_pre_block(
- vsk, target, &recv_data);
- if (err < 0) {
@@ -112,10 +158,7 @@
- release_sock(sk);
- timeout = schedule_timeout(timeout);
- lock_sock(sk);
-+ err = vsock_wait_data(sk, &wait, timeout, &recv_data, target);
-+ if (err <= 0)
-+ break;
-
+-
- if (signal_pending(current)) {
- err = sock_intr_errno(timeout);
- finish_wait(sk_sleep(sk), &wait);
@@ -123,31 +166,44 @@
- } else if (timeout == 0) {
- err = -EAGAIN;
- finish_wait(sk_sleep(sk), &wait);
-- break;
++ if (vsock_wait_data(sk, &wait, timeout, &recv_data, target))
+ break;
- }
- } else {
- ssize_t read;
--
++ continue;
++ }
+
- finish_wait(sk_sleep(sk), &wait);
--
++ finish_wait(sk_sleep(sk), &wait);
+
- if (ready < 0) {
- /* Invalid queue pair content. XXX This should
- * be changed to a connection reset in a later
- * change.
- */
--
++ if (ready < 0) {
++ /* Invalid queue pair content. XXX This should
++ * be changed to a connection reset in a later
++ * change.
++ */
+
- err = -ENOMEM;
- goto out;
- }
--
++ err = -ENOMEM;
++ goto out;
++ }
+
- err = transport->notify_recv_pre_dequeue(
- vsk, target, &recv_data);
- if (err < 0)
- break;
-+ err = transport->notify_recv_pre_dequeue(vsk, target,
-+ &recv_data);
++ err = transport->notify_recv_pre_dequeue(vsk,
++ target, &recv_data);
+ if (err < 0)
+ break;
++ read = transport->stream_dequeue(vsk, msg, len - copied, flags);
- read = transport->stream_dequeue(
- vsk, msg,
@@ -156,7 +212,6 @@
- err = -ENOMEM;
- break;
- }
-+ read = transport->stream_dequeue(vsk, msg, len - copied, flags);
+ if (read < 0) {
+ err = -ENOMEM;
+ break;
@@ -167,11 +222,11 @@
- err = transport->notify_recv_post_dequeue(
- vsk, target, read,
-- !(flags & MSG_PEEK), &recv_data);
++ err = transport->notify_recv_post_dequeue(vsk,
++ target, read,
+ !(flags & MSG_PEEK), &recv_data);
- if (err < 0)
- goto out;
-+ err = transport->notify_recv_post_dequeue(vsk, target, read,
-+ !(flags & MSG_PEEK), &recv_data);
+ if (err < 0)
+ goto out;
@@ -186,6 +241,75 @@
}
if (sk->sk_err)
+@@ -2031,6 +2003,68 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+ return err;
+ }
+
++static int __vsock_seqpacket_recvmsg(struct sock *sk, struct msghdr *msg,
++ size_t len, int flags)
++{
++ return -1;
++}
++
++static int
++vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
++ int flags)
++{
++ const struct vsock_transport *transport;
++ struct vsock_sock *vsk;
++ struct sock *sk;
++ int err = 0;
++
++ sk = sock->sk;
++
++ lock_sock(sk);
++
++ vsk = vsock_sk(sk);
++ transport = vsk->transport;
++
++ if (!transport || sk->sk_state != TCP_ESTABLISHED) {
++ /* Recvmsg is supposed to return 0 if a peer performs an
++ * orderly shutdown. Differentiate between that case and when a
++ * peer has not connected or a local shutdown occurred with the
++ * SOCK_DONE flag.
++ */
++ if (!sock_flag(sk, SOCK_DONE))
++ err = -ENOTCONN;
++
++ goto out;
++ }
++
++ if (flags & MSG_OOB) {
++ err = -EOPNOTSUPP;
++ goto out;
++ }
++
++ /* We don't check peer_shutdown flag here since peer may actually shut
++ * down, but there can be data in the queue that a local socket can
++ * receive.
++ */
++ if (sk->sk_shutdown & RCV_SHUTDOWN)
++ goto out;
++
++ /* It is valid on Linux to pass in a zero-length receive buffer. This
++ * is not an error. We may as well bail out now.
++ */
++ if (!len)
++ goto out;
++
++ 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);
++ return err;
++}
++
+ static int
+ vsock_stream_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+ int flags)
--
2.25.1