Commit 656ab020 authored by Ramon Nou's avatar Ramon Nou
Browse files

fix mmap accesses with lock

parent 4d8fd022
Loading
Loading
Loading
Loading
Loading
+40 −31
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#include <client/cache.hpp>
#include <string>
#include <string_view>
#include <mutex>
#include <set>
#include <tuple>
#include <thread>
@@ -76,7 +77,10 @@ namespace {

// set to store void * addr, fd, length and offset
// set to store void * addr, fd, length, offset, prot
// Protected by mmap_set_mutex for thread-safe access from parallel Python
// threads
std::set<std::tuple<void*, int, size_t, off_t, int>> mmap_set;
std::mutex mmap_set_mutex;

} // namespace

@@ -85,39 +89,45 @@ namespace gkfs::syscall {
void*
gkfs_mmap(void* addr, size_t length, int prot, int flags, int fd,
          off_t offset) {
    if(length == 0) {
        errno = EINVAL;
        return MAP_FAILED;
    }
    void* ptr = calloc(1, length);
    if(ptr == nullptr) {
        return MAP_FAILED;
    }
    // store info on mmap_set
    mmap_set.insert(std::make_tuple(ptr, fd, length, offset, prot));
    // Pre-populate the buffer from the GekkoFS file (read-write mapping)
    auto ret = gkfs::syscall::gkfs_pread(fd, ptr, length, offset);
    if(ret == -1) {
        mmap_set.erase(std::make_tuple(ptr, fd, length, offset, prot));
        free(ptr);
        return MAP_FAILED;
    }
    // Register mapping under lock so concurrent threads don't race on mmap_set
    {
        std::lock_guard<std::mutex> lock(mmap_set_mutex);
        mmap_set.insert(std::make_tuple(ptr, fd, length, offset, prot));
    }
    return ptr;
}

int
// cppcheck-suppress constParameterPointer
gkfs_msync(void* addr, size_t length, int flags) {
    // check if addr is from gekkofs (mmap_set)
    // if so, get the fd and offset
    // pwrite length to the original offset

    std::lock_guard<std::mutex> lock(mmap_set_mutex);
    // Find by start address; msync may pass a sub-range length so we use the
    // full mapping length stored in mmap_set.
    auto it = std::find_if(
            mmap_set.begin(), mmap_set.end(),
            [addr](const auto& t) { return std::get<0>(t) == addr; });

    if(it != mmap_set.end()) {
        const auto& tuple = *it;
        int fd = std::get<1>(tuple);
        off_t offset = std::get<3>(tuple);
        int prot = std::get<4>(tuple);
        int fd = std::get<1>(*it);
        size_t map_length = std::get<2>(*it); // use stored length, not caller's
        off_t offset = std::get<3>(*it);
        int prot = std::get<4>(*it);
        if(prot & PROT_WRITE) {
            gkfs::syscall::gkfs_pwrite(fd, addr, length, offset);
            gkfs::syscall::gkfs_pwrite(fd, addr, map_length, offset);
        }
        return 0;
    }
@@ -127,27 +137,26 @@ gkfs_msync(void* addr, size_t length, int flags) {

int
gkfs_munmap(void* addr, size_t length) {
    // check if addr is from gekkofs (mmap_set)
    // if so, get the fd and offset
    // pwrite length to the original offset
    // return
    // if not just go to the normal msync
    if(mmap_set.size() != 0) {
        // use find_if std::algorithm
        // if found, call msync

    std::unique_lock<std::mutex> lock(mmap_set_mutex);
    auto it = std::find_if(
            mmap_set.begin(), mmap_set.end(),
            [&addr](const std::tuple<void*, int, size_t, off_t, int>& t) {
                return std::get<0>(t) == addr;
            });
    if(it != mmap_set.end()) {
            gkfs_msync(addr, length, 0);
            free(addr);
        int fd = std::get<1>(*it);
        size_t map_length = std::get<2>(*it);
        off_t offset = std::get<3>(*it);
        int prot = std::get<4>(*it);
        // Flush dirty pages back before freeing
        if(prot & PROT_WRITE) {
            gkfs::syscall::gkfs_pwrite(fd, addr, map_length, offset);
        }
        mmap_set.erase(it);
        lock.unlock(); // release lock before free to avoid holding it longer
        free(addr);
        return 0;
    }
    }
    return -1;
}