Commit a1209bc3 authored by Julius Athenstaedt's avatar Julius Athenstaedt
Browse files

adaptive fuse syscall switching

parent 576911cf
Loading
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ extern "C" {
#include <sys/syscall.h>
}

#include <unordered_map>

#include <libsyscall_intercept_hook_point.h>

#include <client/intercept.hpp>
@@ -73,6 +75,18 @@ syscall_no_intercept_wrapper(long syscall_number, Args... args) {

struct statfs;

struct fd_state {
    off_t last_off = -1;
    size_t last_size = -1;
    int seq_count = 0;
    int random_count = 0;
    bool is_random = false;
};
static std::unordered_map<unsigned int, fd_state> fd_state_map;

/// returns false to stay in syscall intercept, true to go to fuse
static bool update_fd_state(unsigned int fd, off_t offset, size_t size);

namespace gkfs::hook {

int
+126 −8
Original line number Diff line number Diff line
@@ -69,6 +69,56 @@ with_errno(T ret) {

} // namespace

static bool
update_fd_state(unsigned int fd, off_t offset, size_t size) {
    auto it = fd_state_map.find(fd);
    if(it == fd_state_map.end()) {
        // No state for this FD, stay in syscall intercept
        return false;
    }
    auto& st = it->second;
    if(st.last_off == -1) {
        // first observation
        st.last_off = offset;
        st.last_size = size;
        return false;
    }

    const off_t expected = st.last_off + st.last_size;
    const off_t diff = offset - expected;

    // configurable thresholds
    constexpr off_t small_delta = 4096; // ≤4 KB jump = still sequential
    constexpr off_t big_jump_threshold =
            128 * 1024; // >128 KB = definitely random

    if(std::abs(diff) <= small_delta) {
        st.seq_count++;
    } else if(std::abs(diff) > big_jump_threshold) {
        st.random_count++;
    } else {
        // medium jump: weak indication of randomness
        st.random_count++;
    }

    if(st.random_count > st.seq_count * 2) {
        st.is_random = true;
    } else {
        st.is_random = false;
    }

    st.last_off = offset;
    st.last_size = size;
    return !st.is_random;
}

static size_t
total_iovec_size(const struct iovec* iov, int iovcnt) {
    size_t total = 0;
    for(int i = 0; i < iovcnt; ++i) total += iov[i].iov_len;
    return total;
}

namespace gkfs::hook {

int
@@ -107,6 +157,7 @@ hook_openat(int dirfd, const char* cpath, int flags, mode_t mode) {
                CTX->file_map()->add(
                        fd, std::make_shared<gkfs::filemap::OpenFile>(resolved,
                                                                      flags));
                fd_state_map[fd] = fd_state{};
                return with_errno(fd);
            }
        }
@@ -122,6 +173,7 @@ hook_close(int fd) {
    LOG(DEBUG, "{}() called with fd: {}", __func__, fd);

    auto ret = gkfs::syscall::gkfs_close(fd);
    fd_state_map.erase(fd);

    if(ret < 0)
        LOG(DEBUG, "{}() close failed with fd: {}, errno {}", __func__, fd,
@@ -257,7 +309,19 @@ hook_read(unsigned int fd, void* buf, size_t count) {
        fmt::ptr(buf), count);

    if(CTX->file_map()->exist(fd)) {
        auto gkfs_fd = CTX->file_map()->get(fd); // retrieve the current offset
        auto pos = gkfs_fd->pos();
        bool use_syscall = update_fd_state(fd, pos, count);
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_read(fd, buf, count));
        } else {
            int ret = syscall_no_intercept_wrapper(SYS_pread64, fd, buf, count,
                                                   pos);
            if(ret > 0) {
                gkfs_fd->pos(pos + ret);
            }
            return ret;
        }
    }
    return syscall_no_intercept_wrapper(SYS_read, fd, buf, count);
}
@@ -269,8 +333,11 @@ hook_pread(unsigned int fd, char* buf, size_t count, loff_t pos) {
        fd, fmt::ptr(buf), count, pos);

    if(CTX->file_map()->exist(fd)) {
        bool use_syscall = update_fd_state(fd, pos, count);
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_pread(fd, buf, count, pos));
        }
    }
    /* Since kernel 2.6: pread() became pread64(), and pwrite() became
     * pwrite64(). */
    return syscall_no_intercept_wrapper(SYS_pread64, fd, buf, count, pos);
@@ -283,7 +350,20 @@ hook_readv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) {
        fmt::ptr(iov), iovcnt);

    if(CTX->file_map()->exist(fd)) {
        auto gkfs_fd = CTX->file_map()->get(fd); // retrieve the current offset
        auto pos = gkfs_fd->pos();
        bool use_syscall =
                update_fd_state(fd, pos, total_iovec_size(iov, iovcnt));
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_readv(fd, iov, iovcnt));
        } else {
            int ret = syscall_no_intercept_wrapper(SYS_preadv, fd, iov, iovcnt,
                                                   pos);
            if(ret > 0) {
                gkfs_fd->pos(pos + ret);
            }
            return ret;
        }
    }
    return syscall_no_intercept_wrapper(SYS_readv, fd, iov, iovcnt);
}
@@ -299,7 +379,12 @@ hook_preadv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt,
        __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h);

    if(CTX->file_map()->exist(fd)) {
        return with_errno(gkfs::syscall::gkfs_preadv(fd, iov, iovcnt, pos_l));
        bool use_syscall =
                update_fd_state(fd, pos_l, total_iovec_size(iov, iovcnt));
        if(use_syscall) {
            return with_errno(
                    gkfs::syscall::gkfs_preadv(fd, iov, iovcnt, pos_l));
        }
    }
    return syscall_no_intercept_wrapper(SYS_preadv, fd, iov, iovcnt, pos_l);
}
@@ -311,7 +396,19 @@ hook_write(unsigned int fd, const char* buf, size_t count) {
        fmt::ptr(buf), count);

    if(CTX->file_map()->exist(fd)) {
        auto gkfs_fd = CTX->file_map()->get(fd); // retrieve the current offset
        auto pos = gkfs_fd->pos();
        bool use_syscall = update_fd_state(fd, pos, count);
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_write(fd, buf, count));
        } else {
            int ret = syscall_no_intercept_wrapper(SYS_pwrite64, fd, buf, count,
                                                   pos);
            if(ret > 0) {
                gkfs_fd->pos(pos + ret);
            }
            return ret;
        }
    }
    return syscall_no_intercept_wrapper(SYS_write, fd, buf, count);
}
@@ -323,8 +420,11 @@ hook_pwrite(unsigned int fd, const char* buf, size_t count, loff_t pos) {
        fd, fmt::ptr(buf), count, pos);

    if(CTX->file_map()->exist(fd)) {
        bool use_syscall = update_fd_state(fd, pos, count);
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_pwrite(fd, buf, count, pos));
        }
    }
    /* Since kernel 2.6: pread() became pread64(), and pwrite() became
     * pwrite64(). */
    return syscall_no_intercept_wrapper(SYS_pwrite64, fd, buf, count, pos);
@@ -337,7 +437,20 @@ hook_writev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) {
        fmt::ptr(iov), iovcnt);

    if(CTX->file_map()->exist(fd)) {
        auto gkfs_fd = CTX->file_map()->get(fd); // retrieve the current offset
        auto pos = gkfs_fd->pos();
        bool use_syscall =
                update_fd_state(fd, pos, total_iovec_size(iov, iovcnt));
        if(use_syscall) {
            return with_errno(gkfs::syscall::gkfs_writev(fd, iov, iovcnt));
        } else {
            int ret = syscall_no_intercept_wrapper(SYS_pwritev, fd, iov, iovcnt,
                                                   pos);
            if(ret > 0) {
                gkfs_fd->pos(pos + ret);
            }
            return ret;
        }
    }
    return syscall_no_intercept_wrapper(SYS_writev, fd, iov, iovcnt);
}
@@ -353,7 +466,12 @@ hook_pwritev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt,
        __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h);

    if(CTX->file_map()->exist(fd)) {
        return with_errno(gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l));
        bool use_syscall =
                update_fd_state(fd, pos_l, total_iovec_size(iov, iovcnt));
        if(use_syscall) {
            return with_errno(
                    gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l));
        }
    }
    return syscall_no_intercept_wrapper(SYS_pwritev, fd, iov, iovcnt, pos_l);
}