浏览代码

Kernel: Add a writev() syscall for writing multiple buffers in one go.

We then use this immediately in the WindowServer/LibGUI communication in
order to send both message + optional "extra data" with a single syscall.
Andreas Kling 6 年之前
父节点
当前提交
99aead4857
共有 8 个文件被更改,包括 110 次插入53 次删除
  1. 71 32
      Kernel/Process.cpp
  2. 2 0
      Kernel/Process.h
  3. 2 0
      Kernel/Syscall.cpp
  4. 1 0
      Kernel/Syscall.h
  5. 5 0
      Kernel/UnixTypes.h
  6. 1 0
      LibC/Makefile
  7. 12 4
      LibGUI/GEventLoop.cpp
  8. 16 17
      Servers/WindowServer/WSClientConnection.cpp

+ 71 - 32
Kernel/Process.cpp

@@ -818,53 +818,92 @@ int Process::sys$ptsname_r(int fd, char* buffer, ssize_t size)
     return 0;
 }
 
-ssize_t Process::sys$write(int fd, const byte* data, ssize_t size)
+ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count)
 {
-    if (size < 0)
+    if (iov_count < 0)
         return -EINVAL;
-    if (size == 0)
-        return 0;
-    if (!validate_read(data, size))
+
+    if (!validate_read_typed(iov, iov_count))
         return -EFAULT;
-#ifdef DEBUG_IO
-    dbgprintf("%s(%u): sys$write(%d, %p, %u)\n", name().characters(), pid(), fd, data, size);
-#endif
+
+    // FIXME: Return EINVAL if sum of iovecs is greater than INT_MAX
+
     auto* descriptor = file_descriptor(fd);
     if (!descriptor)
         return -EBADF;
+
+    int nwritten = 0;
+    for (int i = 0; i < iov_count; ++i) {
+        int rc = do_write(*descriptor, (const byte*)iov[i].iov_base, iov[i].iov_len);
+        if (rc < 0) {
+            if (nwritten == 0)
+                return rc;
+            return nwritten;
+        }
+        nwritten += rc;
+    }
+
+    if (current->has_unmasked_pending_signals()) {
+        current->block(Thread::State::BlockedSignal);
+        if (nwritten == 0)
+            return -EINTR;
+    }
+
+    return nwritten;
+}
+
+ssize_t Process::do_write(FileDescriptor& descriptor, const byte* data, int data_size)
+{
     ssize_t nwritten = 0;
-    if (descriptor->is_blocking()) {
-        while (nwritten < (ssize_t)size) {
+    if (!descriptor.is_blocking())
+        return descriptor.write(data, data_size);
+
+    while (nwritten < data_size) {
 #ifdef IO_DEBUG
-            dbgprintf("while %u < %u\n", nwritten, size);
+        dbgprintf("while %u < %u\n", nwritten, size);
 #endif
-            if (!descriptor->can_write()) {
+        if (!descriptor.can_write()) {
 #ifdef IO_DEBUG
-                dbgprintf("block write on %d\n", fd);
+            dbgprintf("block write on %d\n", fd);
 #endif
-                current->block(Thread::State::BlockedWrite, *descriptor);
-            }
-            ssize_t rc = descriptor->write((const byte*)data + nwritten, size - nwritten);
+            current->block(Thread::State::BlockedWrite, descriptor);
+        }
+        ssize_t rc = descriptor.write(data + nwritten, data_size - nwritten);
 #ifdef IO_DEBUG
-            dbgprintf("   -> write returned %d\n", rc);
+        dbgprintf("   -> write returned %d\n", rc);
 #endif
-            if (rc < 0) {
-                // FIXME: Support returning partial nwritten with errno.
-                ASSERT(nwritten == 0);
-                return rc;
-            }
-            if (rc == 0)
-                break;
-            if (current->has_unmasked_pending_signals()) {
-                current->block(Thread::State::BlockedSignal);
-                if (nwritten == 0)
-                    return -EINTR;
-            }
-            nwritten += rc;
+        if (rc < 0) {
+            // FIXME: Support returning partial nwritten with errno.
+            ASSERT(nwritten == 0);
+            return rc;
         }
-    } else {
-        nwritten = descriptor->write((const byte*)data, size);
+        if (rc == 0)
+            break;
+        if (current->has_unmasked_pending_signals()) {
+            current->block(Thread::State::BlockedSignal);
+            if (nwritten == 0)
+                return -EINTR;
+        }
+        nwritten += rc;
     }
+    return nwritten;
+}
+
+ssize_t Process::sys$write(int fd, const byte* data, ssize_t size)
+{
+    if (size < 0)
+        return -EINVAL;
+    if (size == 0)
+        return 0;
+    if (!validate_read(data, size))
+        return -EFAULT;
+#ifdef DEBUG_IO
+    dbgprintf("%s(%u): sys$write(%d, %p, %u)\n", name().characters(), pid(), fd, data, size);
+#endif
+    auto* descriptor = file_descriptor(fd);
+    if (!descriptor)
+        return -EBADF;
+    auto nwritten = do_write(*descriptor, data, size);
     if (current->has_unmasked_pending_signals()) {
         current->block(Thread::State::BlockedSignal);
         if (nwritten == 0)

+ 2 - 0
Kernel/Process.h

@@ -110,6 +110,7 @@ public:
     int sys$close(int fd);
     ssize_t sys$read(int fd, byte*, ssize_t);
     ssize_t sys$write(int fd, const byte*, ssize_t);
+    ssize_t sys$writev(int fd, const struct iovec* iov, int iov_count);
     int sys$fstat(int fd, stat*);
     int sys$lstat(const char*, stat*);
     int sys$stat(const char*, stat*);
@@ -255,6 +256,7 @@ private:
     Process(String&& name, uid_t, gid_t, pid_t ppid, RingLevel, RetainPtr<Inode>&& cwd = nullptr, RetainPtr<Inode>&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr);
 
     int do_exec(String path, Vector<String> arguments, Vector<String> environment);
+    ssize_t do_write(FileDescriptor&, const byte*, int data_size);
 
     int alloc_fd(int first_candidate_fd = 0);
     void disown_all_shared_buffers();

+ 2 - 0
Kernel/Syscall.cpp

@@ -267,6 +267,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
         return current->process().sys$systrace((pid_t)arg1);
     case Syscall::SC_mknod:
         return current->process().sys$mknod((const char*)arg1, (mode_t)arg2, (dev_t)arg3);
+    case Syscall::SC_writev:
+        return current->process().sys$writev((int)arg1, (const struct iovec*)arg2, (int)arg3);
     default:
         kprintf("<%u> int0x82: Unknown function %u requested {%x, %x, %x}\n", current->process().pid(), function, arg1, arg2, arg3);
         break;

+ 1 - 0
Kernel/Syscall.h

@@ -102,6 +102,7 @@
     __ENUMERATE_SYSCALL(systrace) \
     __ENUMERATE_SYSCALL(exit_thread) \
     __ENUMERATE_SYSCALL(mknod) \
+    __ENUMERATE_SYSCALL(writev) \
 
 
 namespace Syscall {

+ 5 - 0
Kernel/UnixTypes.h

@@ -390,3 +390,8 @@ struct [[gnu::packed]] FarPtr {
     dword offset { 0 };
     word selector { 0 };
 };
+
+struct iovec {
+    void* iov_base;
+    size_t iov_len;
+};

+ 1 - 0
LibC/Makefile

@@ -41,6 +41,7 @@ LIBC_OBJS = \
        sys/select.o \
        sys/socket.o \
        sys/wait.o \
+       sys/uio.o \
        poll.o \
        locale.o \
        arpa/inet.o \

+ 12 - 4
LibGUI/GEventLoop.cpp

@@ -19,6 +19,7 @@
 #include <LibC/errno.h>
 #include <LibC/string.h>
 #include <LibC/stdlib.h>
+#include <sys/uio.h>
 
 //#define GEVENTLOOP_DEBUG
 //#define COALESCING_DEBUG
@@ -359,13 +360,20 @@ bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message, cons
     if (!extra_data.is_empty())
         const_cast<WSAPI_ClientMessage&>(message).extra_size = extra_data.size();
 
-    int nwritten = write(s_event_fd, &message, sizeof(WSAPI_ClientMessage));
-    ASSERT(nwritten == sizeof(WSAPI_ClientMessage));
+    struct iovec iov[2];
+    int iov_count = 1;
+    iov[0].iov_base = (void*)&message;
+    iov[0].iov_len = sizeof(message);
+
     if (!extra_data.is_empty()) {
-        nwritten = write(s_event_fd, extra_data.data(), extra_data.size());
-        ASSERT(nwritten == extra_data.size());
+        iov[1].iov_base = (void*)extra_data.data();
+        iov[1].iov_len = extra_data.size();
+        ++iov_count;
     }
 
+    int nwritten = writev(s_event_fd, iov, iov_count);
+    ASSERT(nwritten == sizeof(message) + extra_data.size());
+
     return true;
 }
 

+ 16 - 17
Servers/WindowServer/WSClientConnection.cpp

@@ -10,6 +10,7 @@
 #include <WindowServer/WSScreen.h>
 #include <SharedBuffer.h>
 #include <sys/ioctl.h>
+#include <sys/uio.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdio.h>
@@ -76,31 +77,29 @@ void WSClientConnection::post_message(const WSAPI_ServerMessage& message, const
     if (!extra_data.is_empty())
         const_cast<WSAPI_ServerMessage&>(message).extra_size = extra_data.size();
 
-    int nwritten = write(m_fd, &message, sizeof(message));
+    struct iovec iov[2];
+    int iov_count = 1;
+
+    iov[0].iov_base = (void*)&message;
+    iov[0].iov_len = sizeof(message);
+
+    if (!extra_data.is_empty()) {
+        iov[1].iov_base = (void*)extra_data.data();
+        iov[1].iov_len = extra_data.size();
+        ++iov_count;
+    }
+
+    int nwritten = writev(m_fd, iov, iov_count);
     if (nwritten < 0) {
         if (errno == EPIPE) {
             dbgprintf("WSClientConnection::post_message: Disconnected from peer.\n");
             return;
         }
-        perror("WSClientConnection::post_message write");
+        perror("WSClientConnection::post_message writev");
         ASSERT_NOT_REACHED();
     }
 
-    ASSERT(nwritten == sizeof(message));
-
-    if (!extra_data.is_empty()) {
-        nwritten = write(m_fd, extra_data.data(), extra_data.size());
-        if (nwritten < 0) {
-            if (errno == EPIPE) {
-                dbgprintf("WSClientConnection::post_message: Disconnected from peer during extra_data write.\n");
-                return;
-            }
-            perror("WSClientConnection::post_message write (extra_data)");
-            ASSERT_NOT_REACHED();
-        }
-
-        ASSERT(nwritten == extra_data.size());
-    }
+    ASSERT(nwritten == sizeof(message) + extra_data.size());
 }
 
 void WSClientConnection::notify_about_new_screen_rect(const Rect& rect)