1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 13:37:45 +00:00

Kernel: SharedBuffer sharing cleanup

Rather than limiting it to two shared processes, store a Vector of
references, so we can add more if we want. Makes the code a little
more generic.

No actual change to the syscall interface yet, so nothing takes
advantage of this yet.
This commit is contained in:
Robin Burchell 2019-07-15 20:38:41 +02:00 committed by Andreas Kling
parent 6e90f00925
commit df3e295ba6

View file

@ -31,7 +31,7 @@
//#define DEBUG_IO //#define DEBUG_IO
//#define TASK_DEBUG //#define TASK_DEBUG
//#define FORK_DEBUG //#define FORK_DEBUG
#define SIGNAL_DEBUG //#define SIGNAL_DEBUG
//#define SHARED_BUFFER_DEBUG //#define SHARED_BUFFER_DEBUG
static pid_t next_pid; static pid_t next_pid;
@ -2367,101 +2367,127 @@ int Process::sys$setsockopt(const Syscall::SC_setsockopt_params* params)
} }
struct SharedBuffer { struct SharedBuffer {
SharedBuffer(pid_t pid1, pid_t pid2, int size) private:
: m_pid1(pid1) struct Reference {
, m_pid2(pid2) Reference(pid_t pid)
, m_vmo(VMObject::create_anonymous(size)) : pid(pid)
{ {
ASSERT(pid1 != pid2);
} }
void* retain(Process& process) pid_t pid;
unsigned count { 1 };
Region* region { nullptr };
};
public:
SharedBuffer(int id, int size)
: m_shared_buffer_id(id)
, m_vmo(VMObject::create_anonymous(size))
{ {
if (m_pid1 == process.pid()) { #ifdef SHARED_BUFFER_DEBUG
++m_pid1_retain_count; dbgprintf("Created shared buffer %d of size %d\n", m_shared_buffer_id, size);
if (!m_pid1_region) { #endif
m_pid1_region = process.allocate_region_with_vmo(VirtualAddress(), size(), m_vmo, 0, "SharedBuffer", PROT_READ | (m_pid1_writable ? PROT_WRITE : 0));
m_pid1_region->set_shared(true);
} }
return m_pid1_region->vaddr().as_ptr();
} else if (m_pid2 == process.pid()) { ~SharedBuffer()
++m_pid2_retain_count; {
if (!m_pid2_region) { #ifdef SHARED_BUFFER_DEBUG
m_pid2_region = process.allocate_region_with_vmo(VirtualAddress(), size(), m_vmo, 0, "SharedBuffer", PROT_READ | (m_pid2_writable ? PROT_WRITE : 0)); dbgprintf("Destroyed shared buffer %d of size %d\n", m_shared_buffer_id, size());
m_pid2_region->set_shared(true); #endif
} }
return m_pid2_region->vaddr().as_ptr();
bool is_shared_with(pid_t peer_pid)
{
LOCKER(m_refs.lock());
for (auto& ref : m_refs.resource()) {
if (ref.pid == peer_pid) {
return true;
} }
return nullptr; }
return false;
}
void* get_address(Process& process)
{
LOCKER(m_refs.lock());
ASSERT(is_shared_with(process.pid()));
for (auto& ref : m_refs.resource()) {
if (ref.pid == process.pid()) {
if (ref.region == nullptr) {
ref.region = process.allocate_region_with_vmo(VirtualAddress(), size(), m_vmo, 0, "SharedBuffer", PROT_READ | (m_writable ? PROT_WRITE : 0));
ref.region->set_shared(true);
}
return ref.region->vaddr().as_ptr();
}
}
ASSERT_NOT_REACHED();
}
void share_with(pid_t peer_pid)
{
LOCKER(m_refs.lock());
for (auto& ref : m_refs.resource()) {
if (ref.pid == peer_pid) {
ref.count++;
return;
}
}
m_refs.resource().append(Reference(peer_pid));
} }
void release(Process& process) void release(Process& process)
{ {
if (m_pid1 == process.pid()) { LOCKER(m_refs.lock());
ASSERT(m_pid1_retain_count); #ifdef SHARED_BUFFER_DEBUG
--m_pid1_retain_count; dbgprintf("Releasing shared buffer %d of size %d by PID %d\n", m_shared_buffer_id, size(), process.pid());
if (!m_pid1_retain_count) { #endif
if (m_pid1_region) for (int i = 0; i < m_refs.resource().size(); ++i) {
process.deallocate_region(*m_pid1_region); auto& ref = m_refs.resource()[i];
m_pid1_region = nullptr; if (ref.pid == process.pid()) {
} if (--ref.count == 0) {
process.deallocate_region(*ref.region);
m_refs.resource().remove(i);
destroy_if_unused(); destroy_if_unused();
} else if (m_pid2 == process.pid()) { return;
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) void disown(pid_t pid)
{ {
if (m_pid1 == pid) { LOCKER(m_refs.lock());
m_pid1 = 0; #ifdef SHARED_BUFFER_DEBUG
m_pid1_retain_count = 0; dbgprintf("Disowning shared buffer %d of size %d by PID %d\n", m_shared_buffer_id, size(), pid);
destroy_if_unused(); #endif
} else if (m_pid2 == pid) { for (int i = 0; i < m_refs.resource().size(); ++i) {
m_pid2 = 0; auto& ref = m_refs.resource()[i];
m_pid2_retain_count = 0; if (ref.pid == pid) {
m_refs.resource().remove(i);
destroy_if_unused(); destroy_if_unused();
return;
}
} }
} }
pid_t pid1() const { return m_pid1; }
pid_t pid2() const { return m_pid2; }
unsigned pid1_ref_count() const { return m_pid1_retain_count; }
unsigned pid2_ref_count() const { return m_pid2_retain_count; }
size_t size() const { return m_vmo->size(); } size_t size() const { return m_vmo->size(); }
void destroy_if_unused(); void destroy_if_unused();
void seal() void seal()
{ {
m_pid1_writable = false; LOCKER(m_refs.lock());
m_pid2_writable = false; m_writable = false;
if (m_pid1_region) { for (auto& ref : m_refs.resource()) {
m_pid1_region->set_writable(false); ref.region->set_writable(false);
MM.remap_region(*m_pid1_region->page_directory(), *m_pid1_region); MM.remap_region(*ref.region->page_directory(), *ref.region);
}
if (m_pid2_region) {
m_pid2_region->set_writable(false);
MM.remap_region(*m_pid2_region->page_directory(), *m_pid2_region);
} }
} }
int m_shared_buffer_id { -1 }; int m_shared_buffer_id { -1 };
pid_t m_pid1; bool m_writable { true };
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 };
bool m_pid1_writable { false };
bool m_pid2_writable { false };
NonnullRefPtr<VMObject> m_vmo; NonnullRefPtr<VMObject> m_vmo;
Lockable<Vector<Reference, 2>> m_refs;
}; };
static int s_next_shared_buffer_id; static int s_next_shared_buffer_id;
@ -2475,10 +2501,11 @@ Lockable<HashMap<int, OwnPtr<SharedBuffer>>>& shared_buffers()
void SharedBuffer::destroy_if_unused() void SharedBuffer::destroy_if_unused()
{ {
if (!m_pid1_retain_count && !m_pid2_retain_count) { LOCKER(m_refs.lock());
if (m_refs.resource().size() == 0) {
LOCKER(shared_buffers().lock()); LOCKER(shared_buffers().lock());
#ifdef SHARED_BUFFER_DEBUG #ifdef SHARED_BUFFER_DEBUG
kprintf("Destroying unused SharedBuffer{%p} id: %d (pid1: %d, pid2: %d)\n", this, m_shared_buffer_id, m_pid1, m_pid2); kprintf("Destroying unused SharedBuffer{%p} id: %d\n", this, m_shared_buffer_id);
#endif #endif
auto count_before = shared_buffers().resource().size(); auto count_before = shared_buffers().resource().size();
shared_buffers().resource().remove(m_shared_buffer_id); shared_buffers().resource().remove(m_shared_buffer_id);
@ -2505,6 +2532,7 @@ int Process::sys$create_shared_buffer(pid_t peer_pid, int size, void** buffer)
return -EINVAL; return -EINVAL;
if (!validate_write_typed(buffer)) if (!validate_write_typed(buffer))
return -EFAULT; return -EFAULT;
{ {
InterruptDisabler disabler; InterruptDisabler disabler;
auto* peer = Process::from_pid(peer_pid); auto* peer = Process::from_pid(peer_pid);
@ -2513,16 +2541,16 @@ int Process::sys$create_shared_buffer(pid_t peer_pid, int size, void** buffer)
} }
LOCKER(shared_buffers().lock()); LOCKER(shared_buffers().lock());
int shared_buffer_id = ++s_next_shared_buffer_id; int shared_buffer_id = ++s_next_shared_buffer_id;
auto shared_buffer = make<SharedBuffer>(m_pid, peer_pid, size); auto shared_buffer = make<SharedBuffer>(shared_buffer_id, size);
shared_buffer->m_shared_buffer_id = shared_buffer_id; shared_buffer->share_with(m_pid);
shared_buffer->share_with(peer_pid);
*buffer = shared_buffer->get_address(*this);
ASSERT((int)shared_buffer->size() >= size); ASSERT((int)shared_buffer->size() >= size);
shared_buffer->m_pid1_region = allocate_region_with_vmo(VirtualAddress(), shared_buffer->size(), shared_buffer->m_vmo, 0, "SharedBuffer", PROT_READ | PROT_WRITE);
shared_buffer->m_pid1_region->set_shared(true);
*buffer = shared_buffer->m_pid1_region->vaddr().as_ptr();
#ifdef SHARED_BUFFER_DEBUG #ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Created shared buffer %d (%u bytes, vmo is %u) for sharing with %d\n", name().characters(), pid(), shared_buffer_id, size, shared_buffer->size(), peer_pid); kprintf("%s(%u): Created shared buffer %d @ %p (%u bytes, vmo is %u)\n", name().characters(), pid(), shared_buffer_id, *buffer, size, shared_buffer->size());
#endif #endif
shared_buffers().resource().set(shared_buffer_id, move(shared_buffer)); shared_buffers().resource().set(shared_buffer_id, move(shared_buffer));
return shared_buffer_id; return shared_buffer_id;
} }
@ -2547,12 +2575,12 @@ void* Process::sys$get_shared_buffer(int shared_buffer_id)
if (it == shared_buffers().resource().end()) if (it == shared_buffers().resource().end())
return (void*)-EINVAL; return (void*)-EINVAL;
auto& shared_buffer = *(*it).value; auto& shared_buffer = *(*it).value;
if (shared_buffer.pid1() != m_pid && shared_buffer.pid2() != m_pid) if (!shared_buffer.is_shared_with(m_pid))
return (void*)-EINVAL; return (void*)-EINVAL;
#ifdef SHARED_BUFFER_DEBUG #ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Retaining shared buffer %d, buffer count: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size()); kprintf("%s(%u): Retaining shared buffer %d, buffer count: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size());
#endif #endif
return shared_buffer.retain(*this); return shared_buffer.get_address(*this);
} }
int Process::sys$seal_shared_buffer(int shared_buffer_id) int Process::sys$seal_shared_buffer(int shared_buffer_id)
@ -2562,7 +2590,7 @@ int Process::sys$seal_shared_buffer(int shared_buffer_id)
if (it == shared_buffers().resource().end()) if (it == shared_buffers().resource().end())
return -EINVAL; return -EINVAL;
auto& shared_buffer = *(*it).value; auto& shared_buffer = *(*it).value;
if (shared_buffer.pid1() != m_pid && shared_buffer.pid2() != m_pid) if (!shared_buffer.is_shared_with(m_pid))
return -EINVAL; return -EINVAL;
#ifdef SHARED_BUFFER_DEBUG #ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Sealing shared buffer %d\n", name().characters(), pid(), shared_buffer_id); kprintf("%s(%u): Sealing shared buffer %d\n", name().characters(), pid(), shared_buffer_id);
@ -2578,7 +2606,7 @@ int Process::sys$get_shared_buffer_size(int shared_buffer_id)
if (it == shared_buffers().resource().end()) if (it == shared_buffers().resource().end())
return -EINVAL; return -EINVAL;
auto& shared_buffer = *(*it).value; auto& shared_buffer = *(*it).value;
if (shared_buffer.pid1() != m_pid && shared_buffer.pid2() != m_pid) if (!shared_buffer.is_shared_with(m_pid))
return -EINVAL; return -EINVAL;
#ifdef SHARED_BUFFER_DEBUG #ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Get shared buffer %d size: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size()); kprintf("%s(%u): Get shared buffer %d size: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size());