From 99aead4857c873c0abfde625424dbad6afe4e49c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 10 May 2019 03:19:25 +0200 Subject: [PATCH] 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. --- Kernel/Process.cpp | 105 ++++++++++++++------ Kernel/Process.h | 2 + Kernel/Syscall.cpp | 2 + Kernel/Syscall.h | 1 + Kernel/UnixTypes.h | 5 + LibC/Makefile | 1 + LibGUI/GEventLoop.cpp | 16 ++- Servers/WindowServer/WSClientConnection.cpp | 33 +++--- 8 files changed, 111 insertions(+), 54 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index c71dc14890..f379c53bff 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -818,6 +818,77 @@ int Process::sys$ptsname_r(int fd, char* buffer, ssize_t size) return 0; } +ssize_t Process::sys$writev(int fd, const struct iovec* iov, int iov_count) +{ + if (iov_count < 0) + return -EINVAL; + + if (!validate_read_typed(iov, iov_count)) + return -EFAULT; + + // 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()) + return descriptor.write(data, data_size); + + while (nwritten < data_size) { +#ifdef IO_DEBUG + dbgprintf("while %u < %u\n", nwritten, size); +#endif + if (!descriptor.can_write()) { +#ifdef IO_DEBUG + dbgprintf("block write on %d\n", fd); +#endif + 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); +#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; + } + return nwritten; +} + ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) { if (size < 0) @@ -832,39 +903,7 @@ ssize_t Process::sys$write(int fd, const byte* data, ssize_t size) auto* descriptor = file_descriptor(fd); if (!descriptor) return -EBADF; - ssize_t nwritten = 0; - if (descriptor->is_blocking()) { - while (nwritten < (ssize_t)size) { -#ifdef IO_DEBUG - dbgprintf("while %u < %u\n", nwritten, size); -#endif - if (!descriptor->can_write()) { -#ifdef IO_DEBUG - 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); -#ifdef IO_DEBUG - 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; - } - } else { - nwritten = descriptor->write((const byte*)data, size); - } + auto nwritten = do_write(*descriptor, data, size); if (current->has_unmasked_pending_signals()) { current->block(Thread::State::BlockedSignal); if (nwritten == 0) diff --git a/Kernel/Process.h b/Kernel/Process.h index 75d398a040..613c003ff7 100644 --- a/Kernel/Process.h +++ b/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&& cwd = nullptr, RetainPtr&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr); int do_exec(String path, Vector arguments, Vector environment); + ssize_t do_write(FileDescriptor&, const byte*, int data_size); int alloc_fd(int first_candidate_fd = 0); void disown_all_shared_buffers(); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 8594072026..7487f1bb04 100644 --- a/Kernel/Syscall.cpp +++ b/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; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index b1168c777a..1844826888 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -102,6 +102,7 @@ __ENUMERATE_SYSCALL(systrace) \ __ENUMERATE_SYSCALL(exit_thread) \ __ENUMERATE_SYSCALL(mknod) \ + __ENUMERATE_SYSCALL(writev) \ namespace Syscall { diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 4261f8ad61..ab3669e70b 100644 --- a/Kernel/UnixTypes.h +++ b/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; +}; diff --git a/LibC/Makefile b/LibC/Makefile index 0352ed5622..ab307c5cbd 100644 --- a/LibC/Makefile +++ b/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 \ diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index 34e06ea0c7..19ce764658 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -19,6 +19,7 @@ #include #include #include +#include //#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(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; } diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 5b8a8f9686..769c0dbba3 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -76,31 +77,29 @@ void WSClientConnection::post_message(const WSAPI_ServerMessage& message, const if (!extra_data.is_empty()) const_cast(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)