From 4ea28bf0a57ab88d3c5e3aec81c28de53c6ebc5b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 16 Feb 2019 12:13:43 +0100 Subject: [PATCH] Kernel: Add a simple shared memory API for two processes only. And use this to implement shared bitmaps between WindowServer and clients. --- Kernel/MemoryManager.h | 2 + Kernel/Process.cpp | 150 +++++++++++++++++++++++++++- Kernel/Process.h | 10 +- Kernel/Syscall.cpp | 6 ++ Kernel/Syscall.h | 3 + LibC/unistd.cpp | 18 ++++ LibC/unistd.h | 3 + SharedGraphics/GraphicsBitmap.cpp | 59 ++++++----- SharedGraphics/GraphicsBitmap.h | 15 ++- SharedGraphics/Painter.cpp | 2 +- WindowServer/WSAPITypes.h | 2 +- WindowServer/WSClientConnection.cpp | 16 +-- WindowServer/WSClientConnection.h | 2 + 13 files changed, 240 insertions(+), 48 deletions(-) diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index d79382b4f28..0cdd526a0fc 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -103,6 +103,8 @@ public: void inode_contents_changed(Badge, off_t, size_t, const byte*); void inode_size_changed(Badge, size_t old_size, size_t new_size); + size_t size() const { return m_size; } + private: VMObject(RetainPtr&&); explicit VMObject(VMObject&); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 4e75e11f433..1060cf70216 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2141,7 +2141,7 @@ void Process::finalize() m_fds.clear(); m_tty = nullptr; - + disown_all_shared_buffers(); { InterruptDisabler disabler; if (auto* parent_process = Process::from_pid(m_ppid)) { @@ -2353,3 +2353,151 @@ bool Process::wait_for_connect(Socket& socket, int& error) } return true; } + +struct SharedBuffer { + SharedBuffer(pid_t pid1, pid_t pid2, size_t size) + : m_pid1(pid1) + , m_pid2(pid2) + , m_vmo(VMObject::create_anonymous(size)) + { + ASSERT(pid1 != pid2); + } + + void* retain(Process& process) + { + if (m_pid1 == process.pid()) { + ++m_pid1_retain_count; + if (!m_pid1_region) + m_pid1_region = process.allocate_region_with_vmo(LinearAddress(), size(), m_vmo.copy_ref(), 0, "SharedBuffer", true, true); + return m_pid1_region->laddr().as_ptr(); + } else if (m_pid2 == process.pid()) { + ++m_pid2_retain_count; + if (!m_pid2_region) + m_pid2_region = process.allocate_region_with_vmo(LinearAddress(), size(), m_vmo.copy_ref(), 0, "SharedBuffer", true, true); + return m_pid2_region->laddr().as_ptr(); + } + return nullptr; + } + + void release(Process& process) + { + if (m_pid1 == process.pid()) { + ASSERT(m_pid1_retain_count); + --m_pid1_retain_count; + if (!m_pid1_retain_count) { + if (m_pid1_region) + process.deallocate_region(*m_pid1_region); + m_pid1_region = nullptr; + } + destroy_if_unused(); + } else if (m_pid2 == process.pid()) { + ASSERT(m_pid2_retain_count); + --m_pid2_retain_count; + if (!m_pid2_retain_count) { + if (m_pid2_region) + process.deallocate_region(*m_pid2_region); + m_pid2_region = nullptr; + } + destroy_if_unused(); + } + } + + void disown(pid_t pid) + { + if (m_pid1 == pid) { + m_pid1 = 0; + m_pid1_retain_count = 0; + destroy_if_unused(); + } else if (m_pid2 == pid) { + m_pid2 = 0; + m_pid2_retain_count = 0; + destroy_if_unused(); + } + } + + pid_t pid1() const { return m_pid1; } + pid_t pid2() const { return m_pid2; } + unsigned pid1_retain_count() const { return m_pid1_retain_count; } + unsigned pid2_retain_count() const { return m_pid2_retain_count; } + size_t size() const { return m_vmo->size(); } + void destroy_if_unused(); + + int m_shared_buffer_id { -1 }; + pid_t m_pid1; + pid_t m_pid2; + unsigned m_pid1_retain_count { 1 }; + unsigned m_pid2_retain_count { 0 }; + Region* m_pid1_region { nullptr }; + Region* m_pid2_region { nullptr }; + RetainPtr m_vmo; +}; + +static int s_next_shared_buffer_id; +Lockable>>& shared_buffers() +{ + static Lockable>>* map; + if (!map) + map = new Lockable>>; + return *map; +} + +void SharedBuffer::destroy_if_unused() +{ + if (!m_pid1_retain_count && !m_pid2_retain_count) { + LOCKER(shared_buffers().lock()); + kprintf("Destroying unused SharedBuffer{%p} (pid1: %d, pid2: %d)\n", this, m_pid1, m_pid2); + shared_buffers().resource().remove(m_shared_buffer_id); + } +} + +void Process::disown_all_shared_buffers() +{ + LOCKER(shared_buffers().lock()); + for (auto& it : shared_buffers().resource()) { + (*it.value).disown(m_pid); + } +} + +int Process::sys$create_shared_buffer(pid_t peer_pid, size_t size, void** buffer) +{ + if (!peer_pid || peer_pid < 0 || peer_pid == m_pid) + return -EINVAL; + if (!validate_write_typed(buffer)) + return -EFAULT; + { + InterruptDisabler disabler; + auto* peer = Process::from_pid(peer_pid); + if (!peer) + return -ESRCH; + } + LOCKER(shared_buffers().lock()); + int shared_buffer_id = ++s_next_shared_buffer_id; + auto shared_buffer = make(m_pid, peer_pid, size); + shared_buffer->m_pid1_region = allocate_region_with_vmo(LinearAddress(), shared_buffer->size(), shared_buffer->m_vmo.copy_ref(), 0, "SharedBuffer", true, true); + *buffer = shared_buffer->m_pid1_region->laddr().as_ptr(); + shared_buffers().resource().set(shared_buffer_id, move(shared_buffer)); + return shared_buffer_id; +} + +int Process::sys$release_shared_buffer(int shared_buffer_id) +{ + LOCKER(shared_buffers().lock()); + auto it = shared_buffers().resource().find(shared_buffer_id); + if (it == shared_buffers().resource().end()) + return -EINVAL; + auto& shared_buffer = *(*it).value; + shared_buffer.release(*this); + return 0; +} + +void* Process::sys$get_shared_buffer(int shared_buffer_id) +{ + LOCKER(shared_buffers().lock()); + auto it = shared_buffers().resource().find(shared_buffer_id); + if (it == shared_buffers().resource().end()) + return (void*)-EINVAL; + auto& shared_buffer = *(*it).value; + if (shared_buffer.pid1() != m_pid && shared_buffer.pid2() != m_pid) + return (void*)-EINVAL; + return shared_buffer.retain(*this); +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 3c6396875bc..e6c18a431d3 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -223,6 +223,10 @@ public: int sys$accept(int sockfd, sockaddr*, socklen_t*); int sys$connect(int sockfd, const sockaddr*, socklen_t); + int sys$create_shared_buffer(pid_t peer_pid, size_t, void** buffer); + void* sys$get_shared_buffer(int shared_buffer_id); + int sys$release_shared_buffer(int shared_buffer_id); + bool wait_for_connect(Socket&, int& error); static void initialize(); @@ -294,6 +298,8 @@ public: Region* allocate_region_with_vmo(LinearAddress, size_t, RetainPtr&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable); Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr&&, String&& name, bool is_readable, bool is_writable); + Region* allocate_region(LinearAddress, size_t, String&& name, bool is_readable = true, bool is_writable = true, bool commit = true); + bool deallocate_region(Region& region); private: friend class MemoryManager; @@ -307,6 +313,7 @@ private: int alloc_fd(); void set_default_signal_dispositions(); + void disown_all_shared_buffers(); RetainPtr m_page_directory; @@ -365,9 +372,6 @@ private: TTY* m_tty { nullptr }; - Region* allocate_region(LinearAddress, size_t, String&& name, bool is_readable = true, bool is_writable = true, bool commit = true); - bool deallocate_region(Region& region); - Region* region_from_range(LinearAddress, size_t); Vector> m_regions; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 7c5f0449269..65f6c12a39c 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -211,6 +211,12 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->sys$accept((int)arg1, (sockaddr*)arg2, (socklen_t*)arg3); case Syscall::SC_connect: return current->sys$connect((int)arg1, (const sockaddr*)arg2, (socklen_t)arg3); + case Syscall::SC_create_shared_buffer: + return current->sys$create_shared_buffer((pid_t)arg1, (size_t)arg2, (void**)arg3); + case Syscall::SC_get_shared_buffer: + return (dword)current->sys$get_shared_buffer((int)arg1); + case Syscall::SC_release_shared_buffer: + return current->sys$release_shared_buffer((int)arg1); default: kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 11c0964a14b..d250e1c0957 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -79,6 +79,9 @@ __ENUMERATE_SYSCALL(accept) \ __ENUMERATE_SYSCALL(listen) \ __ENUMERATE_SYSCALL(connect) \ + __ENUMERATE_SYSCALL(create_shared_buffer) \ + __ENUMERATE_SYSCALL(get_shared_buffer) \ + __ENUMERATE_SYSCALL(release_shared_buffer) \ #ifdef SERENITY diff --git a/LibC/unistd.cpp b/LibC/unistd.cpp index e0123fe63ad..39d18b32e54 100644 --- a/LibC/unistd.cpp +++ b/LibC/unistd.cpp @@ -348,4 +348,22 @@ int read_tsc(unsigned* lsw, unsigned* msw) __RETURN_WITH_ERRNO(rc, rc, -1); } +int create_shared_buffer(pid_t peer_pid, size_t size, void** buffer) +{ + int rc = syscall(SC_create_shared_buffer, peer_pid, size, buffer); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +void* get_shared_buffer(int shared_buffer_id) +{ + int rc = syscall(SC_get_shared_buffer, shared_buffer_id); + __RETURN_WITH_ERRNO(rc, (void*)rc, (void*)-1); +} + +int release_shared_buffer(int shared_buffer_id) +{ + int rc = syscall(SC_release_shared_buffer, shared_buffer_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + } diff --git a/LibC/unistd.h b/LibC/unistd.h index 99f18bad675..5ec0d04ca7c 100644 --- a/LibC/unistd.h +++ b/LibC/unistd.h @@ -8,6 +8,9 @@ __BEGIN_DECLS extern char** environ; +int create_shared_buffer(pid_t peer_pid, size_t, void** buffer); +void* get_shared_buffer(int shared_buffer_id); +int release_shared_buffer(int shared_buffer_id); int read_tsc(unsigned* lsw, unsigned* msw); inline int getpagesize() { return 4096; } pid_t fork(); diff --git a/SharedGraphics/GraphicsBitmap.cpp b/SharedGraphics/GraphicsBitmap.cpp index 5d0183b806b..4803ecfcb1c 100644 --- a/SharedGraphics/GraphicsBitmap.cpp +++ b/SharedGraphics/GraphicsBitmap.cpp @@ -15,31 +15,6 @@ #endif #ifdef KERNEL -RetainPtr GraphicsBitmap::create(Process& process, const Size& size) -{ - return adopt(*new GraphicsBitmap(process, size)); -} - -GraphicsBitmap::GraphicsBitmap(Process& process, const Size& size) - : m_size(size) - , m_pitch(size.width() * sizeof(RGBA32)) - , m_client_process(process.make_weak_ptr()) -{ - InterruptDisabler disabler; - size_t size_in_bytes = size.width() * size.height() * sizeof(RGBA32); - auto vmo = VMObject::create_anonymous(size_in_bytes); - m_client_region = process.allocate_region_with_vmo(LinearAddress(), size_in_bytes, vmo.copy_ref(), 0, "GraphicsBitmap (client)", true, true); - m_client_region->set_shared(true); - m_client_region->set_is_bitmap(true); - m_client_region->commit(); - auto& server = WSMessageLoop::the().server_process(); - m_server_region = server.allocate_region_with_vmo(LinearAddress(), size_in_bytes, move(vmo), 0, "GraphicsBitmap (server)", true, false); - m_server_region->set_shared(true); - m_server_region->set_is_bitmap(true); - - m_data = (RGBA32*)m_server_region->laddr().as_ptr(); -} - RetainPtr GraphicsBitmap::create_kernel_only(const Size& size) { return adopt(*new GraphicsBitmap(size)); @@ -115,11 +90,32 @@ GraphicsBitmap::GraphicsBitmap(const Size& size, RGBA32* data) { } +RetainPtr GraphicsBitmap::create_with_shared_buffer(int shared_buffer_id, const Size& size, RGBA32* data) +{ + if (!data) { +#ifdef KERNEL + void* shared_buffer = current->sys$get_shared_buffer(shared_buffer_id); +#else + void* shared_buffer = get_shared_buffer(shared_buffer_id); +#endif + if (!shared_buffer || shared_buffer == (void*)-1) + return nullptr; + data = (RGBA32*)shared_buffer; + } + return adopt(*new GraphicsBitmap(shared_buffer_id, size, data)); +} + +GraphicsBitmap::GraphicsBitmap(int shared_buffer_id, const Size& size, RGBA32* data) + : m_size(size) + , m_data(data) + , m_pitch(size.width() * sizeof(RGBA32)) + , m_shared_buffer_id(shared_buffer_id) +{ +} + GraphicsBitmap::~GraphicsBitmap() { #ifdef KERNEL - if (m_client_region && m_client_process) - m_client_process->deallocate_region(*m_client_region); if (m_server_region) WSMessageLoop::the().server_process().deallocate_region(*m_server_region); #else @@ -128,6 +124,15 @@ GraphicsBitmap::~GraphicsBitmap() ASSERT(rc == 0); } #endif + if (m_shared_buffer_id != -1) { + int rc; +#ifdef KERNEL + rc = current->sys$release_shared_buffer(m_shared_buffer_id); +#else + rc = release_shared_buffer(m_shared_buffer_id); +#endif + ASSERT(rc == 0); + } m_data = nullptr; } diff --git a/SharedGraphics/GraphicsBitmap.h b/SharedGraphics/GraphicsBitmap.h index 608575796d5..28d21111867 100644 --- a/SharedGraphics/GraphicsBitmap.h +++ b/SharedGraphics/GraphicsBitmap.h @@ -7,18 +7,16 @@ #include #include -#ifdef KERNEL -#include "Process.h" -#endif +class Region; class GraphicsBitmap : public Retainable { public: #ifdef KERNEL - static RetainPtr create(Process&, const Size&); static RetainPtr create_kernel_only(const Size&); #endif static RetainPtr create_wrapper(const Size&, RGBA32*); static RetainPtr load_from_file(const String& path, const Size&); + static RetainPtr create_with_shared_buffer(int shared_buffer_id, const Size&, RGBA32* buffer = nullptr); ~GraphicsBitmap(); RGBA32* scanline(int y); @@ -31,16 +29,17 @@ public: size_t pitch() const { return m_pitch; } #ifdef KERNEL - Region* client_region() { return m_client_region; } Region* server_region() { return m_server_region; } #endif + int shared_buffer_id() const { return m_shared_buffer_id; } + private: #ifdef KERNEL - GraphicsBitmap(Process&, const Size&); GraphicsBitmap(const Size&); #endif GraphicsBitmap(const Size&, RGBA32*); + GraphicsBitmap(int shared_buffer_id, const Size&, RGBA32*); Size m_size; RGBA32* m_data { nullptr }; @@ -50,9 +49,9 @@ private: bool m_mmaped { false }; #endif + int m_shared_buffer_id { -1 }; + #ifdef KERNEL - WeakPtr m_client_process; - Region* m_client_region { nullptr }; Region* m_server_region { nullptr }; #endif }; diff --git a/SharedGraphics/Painter.cpp b/SharedGraphics/Painter.cpp index 1150fb50c31..96676e0746b 100644 --- a/SharedGraphics/Painter.cpp +++ b/SharedGraphics/Painter.cpp @@ -32,7 +32,7 @@ Painter::Painter(GWidget& widget) auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::DidGetWindowBackingStore); m_backing_store_id = response.backing.backing_store_id; - m_target = GraphicsBitmap::create_wrapper(response.backing.size, response.backing.pixels); + m_target = GraphicsBitmap::create_with_shared_buffer(response.backing.shared_buffer_id, response.backing.size); ASSERT(m_target); m_window = widget.window(); m_translation.move_by(widget.window_relative_rect().location()); diff --git a/WindowServer/WSAPITypes.h b/WindowServer/WSAPITypes.h index ac55785b533..7c5b4e05412 100644 --- a/WindowServer/WSAPITypes.h +++ b/WindowServer/WSAPITypes.h @@ -118,7 +118,7 @@ struct WSAPI_ServerMessage { WSAPI_Size size; size_t bpp; size_t pitch; - RGBA32* pixels; + int shared_buffer_id; } backing; }; }; diff --git a/WindowServer/WSClientConnection.cpp b/WindowServer/WSClientConnection.cpp index ea134818a14..aadb48b7ebb 100644 --- a/WindowServer/WSClientConnection.cpp +++ b/WindowServer/WSClientConnection.cpp @@ -42,13 +42,12 @@ WSClientConnection* WSClientConnection::ensure_for_client_id(int client_id) WSClientConnection::WSClientConnection(int fd) : m_fd(fd) { - pid_t pid; - int rc = WSMessageLoop::the().server_process().sys$ioctl(m_fd, 413, (int)&pid); + int rc = WSMessageLoop::the().server_process().sys$ioctl(m_fd, 413, (int)&m_pid); ASSERT(rc == 0); { InterruptDisabler disabler; - auto* process = Process::from_pid(pid); + auto* process = Process::from_pid(m_pid); ASSERT(process); m_process = process->make_weak_ptr(); m_client_id = (int)process; @@ -85,9 +84,12 @@ void WSClientConnection::post_message(const WSAPI_ServerMessage& message) RetainPtr WSClientConnection::create_bitmap(const Size& size) { - if (!m_process) - return nullptr; - return GraphicsBitmap::create(*m_process, size); + RGBA32* buffer; + int shared_buffer_id = current->sys$create_shared_buffer(m_pid, size.area() * sizeof(RGBA32), (void**)&buffer); + ASSERT(shared_buffer_id >= 0); + ASSERT(buffer); + ASSERT(buffer != (void*)-1); + return GraphicsBitmap::create_with_shared_buffer(shared_buffer_id, size, buffer); } void WSClientConnection::on_message(WSMessage& message) @@ -369,7 +371,7 @@ void WSClientConnection::handle_request(WSAPIGetWindowBackingStoreRequest& reque response.backing.bpp = sizeof(RGBA32); response.backing.pitch = backing_store->pitch(); response.backing.size = backing_store->size(); - response.backing.pixels = reinterpret_cast(backing_store->client_region()->laddr().as_ptr()); + response.backing.shared_buffer_id = backing_store->shared_buffer_id(); WSMessageLoop::the().post_message_to_client(request.client_id(), response); } diff --git a/WindowServer/WSClientConnection.h b/WindowServer/WSClientConnection.h index b8b09018e29..c835014d305 100644 --- a/WindowServer/WSClientConnection.h +++ b/WindowServer/WSClientConnection.h @@ -32,6 +32,7 @@ public: WSMenuBar* app_menubar() { return m_app_menubar.ptr(); } int fd() const { return m_fd; } + pid_t pid() const { return m_pid; } private: virtual void on_message(WSMessage&) override; @@ -61,6 +62,7 @@ private: int m_client_id { 0 }; int m_fd { -1 }; + pid_t m_pid { 0 }; HashMap> m_windows; HashMap> m_menubars;