summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-02-16 12:13:43 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-02-16 12:13:43 +0100
commit4ea28bf0a57ab88d3c5e3aec81c28de53c6ebc5b (patch)
treecbd8885f633b384a07050b0948f7197d87d46ba5 /Kernel
parent4db78dabd37cdf16bcd9df7733936f91f581935a (diff)
downloadserenity-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.h2
-rw-r--r--Kernel/Process.cpp150
-rw-r--r--Kernel/Process.h10
-rw-r--r--Kernel/Syscall.cpp6
-rw-r--r--Kernel/Syscall.h3
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