diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 6dac60f5..fa7f4f62 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCES src/memory/KernelVM.cpp src/memory/AddressSpace.cpp src/memory/MemoryMap.cpp + src/memory/SharedMemory.cpp src/boot/Init.cpp src/arch/Serial.cpp src/arch/Timer.cpp diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index a3732a66..2a63f68e 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -113,6 +113,11 @@ namespace VFS { } + virtual Result query_shared_memory(off_t, usize) + { + return err(EACCES); + } + // Directory-specific methods virtual Result> find(const char* name) const = 0; @@ -186,6 +191,7 @@ namespace VFS protected: mutable InodeMetadata m_metadata; + Option m_shmid {}; }; class FileInode : public Inode diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h index ee88e481..fa7211f3 100644 --- a/kernel/src/fs/ext2/Inode.h +++ b/kernel/src/fs/ext2/Inode.h @@ -34,6 +34,11 @@ namespace Ext2 { } + Result query_shared_memory(off_t, usize) override + { + return err(ENOTSUP); + } + Result set_metadata(const VFS::InodeMetadata&) override { return err(EROFS); diff --git a/kernel/src/fs/tmpfs/Inode.cpp b/kernel/src/fs/tmpfs/Inode.cpp index a92ec3cb..0299ea7b 100644 --- a/kernel/src/fs/tmpfs/Inode.cpp +++ b/kernel/src/fs/tmpfs/Inode.cpp @@ -1,4 +1,6 @@ #include "fs/tmpfs/Inode.h" +#include "arch/MMU.h" +#include "memory/SharedMemory.h" namespace TmpFS { @@ -127,4 +129,28 @@ namespace TmpFS return {}; } + + Result FileInode::query_shared_memory(off_t offset, usize count) + { + if (offset + (count * ARCH_PAGE_SIZE) > m_data_buffer.size()) return err(EINVAL); + + if (!m_shmid.has_value()) + { + u64 shmid = TRY(SharedMemory::create(m_data_buffer.data() + offset, offset, count)); + m_shmid = shmid; + return shmid; + } + + auto* shm = g_shared_memory_map.try_get_ref(*m_shmid); + if (shm->offset > offset) + { + TRY(shm->grow_backward(m_data_buffer.data() + offset, (shm->offset - offset) / ARCH_PAGE_SIZE)); + } + if (shm->frames.size() < count) + { + TRY(shm->grow_forward(m_data_buffer.data() + offset + (shm->frames.size() * ARCH_PAGE_SIZE), + count - shm->frames.size())); + } + return *m_shmid; + } } diff --git a/kernel/src/fs/tmpfs/Inode.h b/kernel/src/fs/tmpfs/Inode.h index c89b2b78..286c96ef 100644 --- a/kernel/src/fs/tmpfs/Inode.h +++ b/kernel/src/fs/tmpfs/Inode.h @@ -33,6 +33,8 @@ namespace TmpFS Result truncate(usize size) override; + Result query_shared_memory(off_t offset, usize count) override; + void did_link() override { m_metadata.nlinks++; diff --git a/kernel/src/memory/SharedMemory.cpp b/kernel/src/memory/SharedMemory.cpp new file mode 100644 index 00000000..92f3f78f --- /dev/null +++ b/kernel/src/memory/SharedMemory.cpp @@ -0,0 +1,120 @@ +#include "memory/SharedMemory.h" +#include "memory/MemoryManager.h" +#include +#include + +HashMap g_shared_memory_map; +Atomic g_next_shmem_id; + +Result SharedMemory::create(u8* mem, off_t offset, usize count) +{ + SharedMemory shmem; + shmem.offset = offset; + + auto guard = make_scope_guard([&shmem] { + for (u64 frame : shmem.frames) { MemoryManager::free_frame(frame); } + }); + + while (count--) + { + u64 frame = TRY(MemoryManager::alloc_frame()); + TRY(shmem.frames.try_append(frame)); + if (mem) + { + memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE); + mem += ARCH_PAGE_SIZE; + } + else { memset((void*)MMU::translate_physical_address(frame), 0, ARCH_PAGE_SIZE); } + } + + const u64 id = g_next_shmem_id++; + + check(TRY(g_shared_memory_map.try_set(id, move(shmem)))); + + guard.deactivate(); + + return id; +} + +Result SharedMemory::grow_forward(u8* mem, usize count) +{ + u64 old_count = frames.size(); + + auto guard = make_scope_guard([old_count, this] { + while (old_count < this->frames.size()) { MemoryManager::free_frame(*this->frames.try_pop()); } + }); + + while (count--) + { + u64 frame = TRY(MemoryManager::alloc_frame()); + TRY(frames.try_append(frame)); + if (mem) + { + memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE); + mem += ARCH_PAGE_SIZE; + } + } + + guard.deactivate(); + + return {}; +} + +Result SharedMemory::grow_backward(u8* mem, usize count) +{ + Vector new_frames; + + const usize bytes = count * ARCH_PAGE_SIZE; + + auto guard = make_scope_guard([&new_frames, count] { + for (u64 i = 0; i < count && i < new_frames.size(); i++) { MemoryManager::free_frame(new_frames[i]); } + }); + + while (count--) + { + u64 frame = TRY(MemoryManager::alloc_frame()); + TRY(new_frames.try_append(frame)); + if (mem) + { + memcpy((void*)MMU::translate_physical_address(frame), mem, ARCH_PAGE_SIZE); + mem += ARCH_PAGE_SIZE; + } + } + + for (u64 frame : frames) { TRY(new_frames.try_append(frame)); } + + frames = move(new_frames); + offset -= bytes; + + guard.deactivate(); + + return {}; +} + +Result SharedMemory::map(u64 virt, int flags, off_t _offset, usize count) +{ + usize off = _offset / ARCH_PAGE_SIZE; + if (off + count > frames.size()) return err(EINVAL); + + for (usize i = off; i < count; i++) + { + TRY(MMU::map(virt, frames[i], flags, MMU::UseHugePages::No)); + virt += ARCH_PAGE_SIZE; + } + + return {}; +} + +void SharedMemory::free() +{ + if (inode && (prot & PROT_WRITE)) + { + for (u64 i = 0; i < frames.size(); i++) + { + inode->write((u8*)MMU::translate_physical_address(frames[i]), offset + (i * ARCH_PAGE_SIZE), + ARCH_PAGE_SIZE); + } + } + + for (u64 frame : frames) { MemoryManager::free_frame(frame); } +} diff --git a/kernel/src/memory/SharedMemory.h b/kernel/src/memory/SharedMemory.h new file mode 100644 index 00000000..7541d918 --- /dev/null +++ b/kernel/src/memory/SharedMemory.h @@ -0,0 +1,25 @@ +#pragma once +#include "fs/VFS.h" +#include +#include +#include + +struct SharedMemory +{ + Vector frames; + off_t offset; + int prot; + SharedPtr inode {}; + int refs { 0 }; + + static Result create(u8* mem, off_t offset, usize count); + + Result grow_forward(u8* mem, usize count); + Result grow_backward(u8* mem, usize count); + + Result map(u64 virt, int flags, off_t offset, usize count); + + void free(); +}; + +extern HashMap g_shared_memory_map; diff --git a/libluna/include/luna/HashMap.h b/libluna/include/luna/HashMap.h index 639151d5..3a276ae2 100644 --- a/libluna/include/luna/HashMap.h +++ b/libluna/include/luna/HashMap.h @@ -20,6 +20,11 @@ template u64 hash(const HashPair& value, u64 salt template struct HashMap { public: + Result try_set(const K& key, V&& value) + { + return m_table.try_set(HashPair { key, move(value) }); + } + Result try_set(const K& key, const V& value) { return m_table.try_set(HashPair { key, value });