diff mbox

[5/8] linux-user: Implement sendmmsg syscall

Message ID f19e00d776b781bfb0067b9b20a592440fd2990e.1394453724.git.riku.voipio@linaro.org
State Accepted
Commit f19e00d776b781bfb0067b9b20a592440fd2990e
Headers show

Commit Message

Riku Voipio March 10, 2014, 12:22 p.m. UTC
From: Alexander Graf <agraf@suse.de>

Glibc when built for newer kernels assumes that the sendmmsg syscall is
available. Without it, dns resolution simply fails to work.

Wrap the syscall with existing infrastructure so that we don't have a host
dependency on sendmmsg.

To avoid locking the same area of guest memory twice (which will break if
DEBUG_REMAP is defined) we pull the lock/unlock part of do_sendrecvmsg()
out into its own function so the actual implementation can be shared.

Signed-off-by: Alexander Graf <agraf@suse.de>
[PMM: add recvmmsg support;
 handle errors (which also implies support for non-blocking operations);
 cap the vector length as the kernel implementation does;
 don't lock guest memory twice;
 support MSG_WAITFORONE flag]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
---
 linux-user/syscall.c      | 86 +++++++++++++++++++++++++++++++++++++++++------
 linux-user/syscall_defs.h |  4 +++
 2 files changed, 80 insertions(+), 10 deletions(-)
diff mbox

Patch

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ccdbc4e..1f64867 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1904,23 +1904,16 @@  static abi_long do_connect(int sockfd, abi_ulong target_addr,
     return get_errno(connect(sockfd, addr, addrlen));
 }
 
-/* do_sendrecvmsg() Must return target values and target errnos. */
-static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
-                               int flags, int send)
+/* do_sendrecvmsg_locked() Must return target values and target errnos. */
+static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
+                                      int flags, int send)
 {
     abi_long ret, len;
-    struct target_msghdr *msgp;
     struct msghdr msg;
     int count;
     struct iovec *vec;
     abi_ulong target_vec;
 
-    /* FIXME */
-    if (!lock_user_struct(send ? VERIFY_READ : VERIFY_WRITE,
-                          msgp,
-                          target_msg,
-                          send ? 1 : 0))
-        return -TARGET_EFAULT;
     if (msgp->msg_name) {
         msg.msg_namelen = tswap32(msgp->msg_namelen);
         msg.msg_name = alloca(msg.msg_namelen);
@@ -1975,10 +1968,75 @@  static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
 out:
     unlock_iovec(vec, target_vec, count, !send);
 out2:
+    return ret;
+}
+
+static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
+                               int flags, int send)
+{
+    abi_long ret;
+    struct target_msghdr *msgp;
+
+    if (!lock_user_struct(send ? VERIFY_READ : VERIFY_WRITE,
+                          msgp,
+                          target_msg,
+                          send ? 1 : 0)) {
+        return -TARGET_EFAULT;
+    }
+    ret = do_sendrecvmsg_locked(fd, msgp, flags, send);
     unlock_user_struct(msgp, target_msg, send ? 0 : 1);
     return ret;
 }
 
+#ifdef TARGET_NR_sendmmsg
+/* We don't rely on the C library to have sendmmsg/recvmmsg support,
+ * so it might not have this *mmsg-specific flag either.
+ */
+#ifndef MSG_WAITFORONE
+#define MSG_WAITFORONE 0x10000
+#endif
+
+static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec,
+                                unsigned int vlen, unsigned int flags,
+                                int send)
+{
+    struct target_mmsghdr *mmsgp;
+    abi_long ret = 0;
+    int i;
+
+    if (vlen > UIO_MAXIOV) {
+        vlen = UIO_MAXIOV;
+    }
+
+    mmsgp = lock_user(VERIFY_WRITE, target_msgvec, sizeof(*mmsgp) * vlen, 1);
+    if (!mmsgp) {
+        return -TARGET_EFAULT;
+    }
+
+    for (i = 0; i < vlen; i++) {
+        ret = do_sendrecvmsg_locked(fd, &mmsgp[i].msg_hdr, flags, send);
+        if (is_error(ret)) {
+            break;
+        }
+        mmsgp[i].msg_len = tswap32(ret);
+        /* MSG_WAITFORONE turns on MSG_DONTWAIT after one packet */
+        if (flags & MSG_WAITFORONE) {
+            flags |= MSG_DONTWAIT;
+        }
+    }
+
+    unlock_user(mmsgp, target_msgvec, sizeof(*mmsgp) * i);
+
+    /* Return number of datagrams sent if we sent any at all;
+     * otherwise return the error.
+     */
+    if (i) {
+        return i;
+    }
+    return ret;
+}
+#endif
+
 /* If we don't have a system accept4() then just call accept.
  * The callsites to do_accept4() will ensure that they don't
  * pass a non-zero flags argument in this config.
@@ -6716,6 +6774,14 @@  abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
         ret = do_sendrecvmsg(arg1, arg2, arg3, 1);
         break;
 #endif
+#ifdef TARGET_NR_sendmmsg
+    case TARGET_NR_sendmmsg:
+        ret = do_sendrecvmmsg(arg1, arg2, arg3, arg4, 1);
+        break;
+    case TARGET_NR_recvmmsg:
+        ret = do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0);
+        break;
+#endif
 #ifdef TARGET_NR_sendto
     case TARGET_NR_sendto:
         ret = do_sendto(arg1, arg2, arg3, arg4, arg5, arg6);
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index d55f396..732c9e3 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -240,6 +240,10 @@  __target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cms
   return __cmsg;
 }
 
+struct target_mmsghdr {
+    struct target_msghdr msg_hdr;              /* Message header */
+    unsigned int         msg_len;              /* Number of bytes transmitted */
+};
 
 struct  target_rusage {
         struct target_timeval ru_utime;        /* user time used */