diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-02-16 12:13:43 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-02-16 12:13:43 +0100 |
commit | 4ea28bf0a57ab88d3c5e3aec81c28de53c6ebc5b (patch) | |
tree | cbd8885f633b384a07050b0948f7197d87d46ba5 /Kernel | |
parent | 4db78dabd37cdf16bcd9df7733936f91f581935a (diff) | |
download | serenity-4ea28bf0a57ab88d3c5e3aec81c28de53c6ebc5b.zip |
Kernel: Add a simple shared memory API for two processes only.
And use this to implement shared bitmaps between WindowServer and clients.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/MemoryManager.h | 2 | ||||
-rw-r--r-- | Kernel/Process.cpp | 150 | ||||
-rw-r--r-- | Kernel/Process.h | 10 | ||||
-rw-r--r-- | Kernel/Syscall.cpp | 6 | ||||
-rw-r--r-- | Kernel/Syscall.h | 3 |
5 files changed, 167 insertions, 4 deletions
diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index d79382b4f2..0cdd526a0f 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -103,6 +103,8 @@ public: void inode_contents_changed(Badge<Inode>, off_t, size_t, const byte*); void inode_size_changed(Badge<Inode>, size_t old_size, size_t new_size); + size_t size() const { return m_size; } + private: VMObject(RetainPtr<Inode>&&); explicit VMObject(VMObject&); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 4e75e11f43..1060cf7021 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<VMObject> m_vmo; +}; + +static int s_next_shared_buffer_id; +Lockable<HashMap<int, OwnPtr<SharedBuffer>>>& shared_buffers() +{ + static Lockable<HashMap<int, OwnPtr<SharedBuffer>>>* map; + if (!map) + map = new Lockable<HashMap<int, OwnPtr<SharedBuffer>>>; + 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<SharedBuffer>(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 3c6396875b..e6c18a431d 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<VMObject>&&, size_t offset_in_vmo, String&& name, bool is_readable, bool is_writable); Region* allocate_file_backed_region(LinearAddress, size_t, RetainPtr<Inode>&&, 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<PageDirectory> 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<RetainPtr<Region>> m_regions; diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 7c5f044926..65f6c12a39 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 11c0964a14..d250e1c095 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 |