From 65e019aab2df2e65fe8e7ba1923445d91d508572 Mon Sep 17 00:00:00 2001 From: Karim Date: Thu, 11 Dec 2025 21:18:35 +0300 Subject: [PATCH 1/2] Implement lab-2 --- lab/vtpc/.gitignore | 2 + lab/vtpc/.idea/.gitignore | 8 + lab/vtpc/.idea/codeStyles/Project.xml | 105 +++++ lab/vtpc/.idea/codeStyles/codeStyleConfig.xml | 5 + lab/vtpc/.idea/editor.xml | 101 ++++ lab/vtpc/.idea/misc.xml | 7 + lab/vtpc/.idea/modules.xml | 8 + lab/vtpc/.idea/vcs.xml | 6 + lab/vtpc/.idea/vtpc.iml | 2 + lab/vtpc/lib/CMakeLists.txt | 10 + lab/vtpc/lib/io_load.c | 16 + lab/vtpc/lib/io_load.h | 32 ++ lab/vtpc/lib/io_load_args.c | 125 +++++ lab/vtpc/lib/io_load_runner.c | 176 +++++++ lab/vtpc/lib/io_load_vtpc.c | 194 ++++++++ lab/vtpc/lib/vtpc.c | 433 +++++++++++++++++- 16 files changed, 1222 insertions(+), 8 deletions(-) create mode 100644 lab/vtpc/.idea/.gitignore create mode 100644 lab/vtpc/.idea/codeStyles/Project.xml create mode 100644 lab/vtpc/.idea/codeStyles/codeStyleConfig.xml create mode 100644 lab/vtpc/.idea/editor.xml create mode 100644 lab/vtpc/.idea/misc.xml create mode 100644 lab/vtpc/.idea/modules.xml create mode 100644 lab/vtpc/.idea/vcs.xml create mode 100644 lab/vtpc/.idea/vtpc.iml create mode 100644 lab/vtpc/lib/io_load.c create mode 100644 lab/vtpc/lib/io_load.h create mode 100644 lab/vtpc/lib/io_load_args.c create mode 100644 lab/vtpc/lib/io_load_runner.c create mode 100644 lab/vtpc/lib/io_load_vtpc.c diff --git a/lab/vtpc/.gitignore b/lab/vtpc/.gitignore index fb90d94..6e2b275 100644 --- a/lab/vtpc/.gitignore +++ b/lab/vtpc/.gitignore @@ -6,3 +6,5 @@ build/ # Python __pycache__/ + +.vscode \ No newline at end of file diff --git a/lab/vtpc/.idea/.gitignore b/lab/vtpc/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/lab/vtpc/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/lab/vtpc/.idea/codeStyles/Project.xml b/lab/vtpc/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..81469bd --- /dev/null +++ b/lab/vtpc/.idea/codeStyles/Project.xml @@ -0,0 +1,105 @@ + + + + + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/codeStyles/codeStyleConfig.xml b/lab/vtpc/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/lab/vtpc/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/editor.xml b/lab/vtpc/.idea/editor.xml new file mode 100644 index 0000000..008fac2 --- /dev/null +++ b/lab/vtpc/.idea/editor.xml @@ -0,0 +1,101 @@ + + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/misc.xml b/lab/vtpc/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/lab/vtpc/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/modules.xml b/lab/vtpc/.idea/modules.xml new file mode 100644 index 0000000..7b7448b --- /dev/null +++ b/lab/vtpc/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/vcs.xml b/lab/vtpc/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/lab/vtpc/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/lab/vtpc/.idea/vtpc.iml b/lab/vtpc/.idea/vtpc.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/lab/vtpc/.idea/vtpc.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/lab/vtpc/lib/CMakeLists.txt b/lab/vtpc/lib/CMakeLists.txt index e904b37..33730ae 100644 --- a/lab/vtpc/lib/CMakeLists.txt +++ b/lab/vtpc/lib/CMakeLists.txt @@ -9,3 +9,13 @@ target_include_directories( PUBLIC . ) + +add_executable( + io_load_vtpc + io_load.c + io_load_args.c + io_load_vtpc.c +) + +target_include_directories(io_load_vtpc PRIVATE .) +target_link_libraries(io_load_vtpc PRIVATE vtpc) diff --git a/lab/vtpc/lib/io_load.c b/lab/vtpc/lib/io_load.c new file mode 100644 index 0000000..637807e --- /dev/null +++ b/lab/vtpc/lib/io_load.c @@ -0,0 +1,16 @@ +#include "io_load.h" + +#include + +int main(int argc, char* argv[]) { + options_t opts; + if (parse_args(argc, argv, &opts) != 0) { + print_usage(argv[0]); + return 1; + } + + if (run_io_workload(&opts) != 0) + return 1; + + return 0; +} diff --git a/lab/vtpc/lib/io_load.h b/lab/vtpc/lib/io_load.h new file mode 100644 index 0000000..537690a --- /dev/null +++ b/lab/vtpc/lib/io_load.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +typedef enum { + MODE_READ, + MODE_WRITE +} io_mode_t; + +typedef enum { + ORDER_SEQUENCE, + ORDER_RANDOM +} io_order_t; + +typedef struct { + io_mode_t mode; + size_t block_size; + size_t block_count; + const char* path; + int repeat; + bool use_direct; + io_order_t order; + bool range_set; + off_t range_start; + off_t range_end; +} options_t; + +void print_usage(const char* prog); +int parse_args(int argc, char* argv[], options_t* opts); +int run_io_workload(const options_t* opts); diff --git a/lab/vtpc/lib/io_load_args.c b/lab/vtpc/lib/io_load_args.c new file mode 100644 index 0000000..17ce494 --- /dev/null +++ b/lab/vtpc/lib/io_load_args.c @@ -0,0 +1,125 @@ +#include "io_load.h" + +#include +#include +#include +#include + +static bool parse_range(const char* text, off_t* start, off_t* end, bool* range_set) { + const char* dash = strchr(text, '-'); + if (dash == NULL) + return false; + + char left[32]; + char right[32]; + size_t left_len = (size_t)(dash - text); + size_t right_len = strlen(dash + 1); + if (left_len == 0 || right_len == 0 || + left_len >= sizeof(left) || right_len >= sizeof(right)) + return false; + + memcpy(left, text, left_len); + left[left_len] = '\0'; + memcpy(right, dash + 1, right_len + 1); + + errno = 0; + long long start_val = strtoll(left, NULL, 10); + long long end_val = strtoll(right, NULL, 10); + if (errno != 0 || start_val < 0 || end_val < 0) + return false; + + *start = (off_t)start_val; + *end = (off_t)end_val; + *range_set = !(start_val == 0 && end_val == 0); + return true; +} + +void print_usage(const char* prog) { + fprintf(stderr, + "Usage: %s --rw --block_size --block_count \n" + " --file [--range start-end] [--direct on|off]\n" + " [--type sequence|random] [--repeat N]\n", + prog); +} + +int parse_args(int argc, char* argv[], options_t* opts) { + opts->mode = MODE_READ; + opts->block_size = 4096; + opts->block_count = 1024; + opts->path = NULL; + opts->repeat = 1; + opts->use_direct = false; + opts->order = ORDER_SEQUENCE; + opts->range_set = false; + opts->range_start = 0; + opts->range_end = 0; + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--rw") == 0 && i + 1 < argc) { + const char* mode = argv[++i]; + if (strcmp(mode, "read") == 0) { + opts->mode = MODE_READ; + } else if (strcmp(mode, "write") == 0) { + opts->mode = MODE_WRITE; + } else { + fprintf(stderr, "Unknown --rw value: %s\n", mode); + return -1; + } + } else if (strcmp(argv[i], "--block_size") == 0 && i + 1 < argc) { + opts->block_size = (size_t)strtoull(argv[++i], NULL, 10); + if (opts->block_size == 0) { + fprintf(stderr, "block_size must be > 0\n"); + return -1; + } + } else if (strcmp(argv[i], "--block_count") == 0 && i + 1 < argc) { + opts->block_count = (size_t)strtoull(argv[++i], NULL, 10); + if (opts->block_count == 0) { + fprintf(stderr, "block_count must be > 0\n"); + return -1; + } + } else if (strcmp(argv[i], "--file") == 0 && i + 1 < argc) { + opts->path = argv[++i]; + } else if (strcmp(argv[i], "--repeat") == 0 && i + 1 < argc) { + opts->repeat = atoi(argv[++i]); + if (opts->repeat <= 0) { + fprintf(stderr, "repeat must be > 0\n"); + return -1; + } + } else if (strcmp(argv[i], "--direct") == 0 && i + 1 < argc) { + const char* val = argv[++i]; + if (strcmp(val, "on") == 0) { + opts->use_direct = true; + } else if (strcmp(val, "off") == 0) { + opts->use_direct = false; + } else { + fprintf(stderr, "--direct accepts on/off\n"); + return -1; + } + } else if (strcmp(argv[i], "--type") == 0 && i + 1 < argc) { + const char* val = argv[++i]; + if (strcmp(val, "sequence") == 0) { + opts->order = ORDER_SEQUENCE; + } else if (strcmp(val, "random") == 0) { + opts->order = ORDER_RANDOM; + } else { + fprintf(stderr, "--type accepts sequence/random\n"); + return -1; + } + } else if (strcmp(argv[i], "--range") == 0 && i + 1 < argc) { + if (!parse_range(argv[++i], &opts->range_start, &opts->range_end, &opts->range_set)) { + fprintf(stderr, "Invalid range format\n"); + return -1; + } + } else { + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + return -1; + } + } + + if (opts->path == NULL) { + fprintf(stderr, "--file is required\n"); + return -1; + } + + return 0; +} diff --git a/lab/vtpc/lib/io_load_runner.c b/lab/vtpc/lib/io_load_runner.c new file mode 100644 index 0000000..9d2a07a --- /dev/null +++ b/lab/vtpc/lib/io_load_runner.c @@ -0,0 +1,176 @@ +#include "io_load.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void* allocate_buffer(size_t size) { + void* buffer = malloc(size); + if (buffer == NULL) + fprintf(stderr, "malloc failed\n"); + return buffer; +} + +static double seconds_between(const struct timespec* start, + const struct timespec* end) { + time_t sec = end->tv_sec - start->tv_sec; + long nsec = end->tv_nsec - start->tv_nsec; + if (nsec < 0) { + sec -= 1; + nsec += 1000000000L; + } + return (double)sec + (double)nsec / 1e9; +} + +static int check_range(const options_t* opts, const struct stat* st) { + off_t range_start = opts->range_start; + off_t range_end = opts->range_end; + + if (!opts->range_set) { + range_start = 0; + if (opts->mode == MODE_READ) { + range_end = st->st_size; + } else { + range_end = range_start + (off_t)(opts->block_size * opts->block_count); + } + } + + if (opts->mode == MODE_READ && range_end > st->st_size) { + fprintf(stderr, "Range exceeds file size for read mode\n"); + return -1; + } + + if (range_end <= range_start) { + fprintf(stderr, "Invalid range\n"); + return -1; + } + + off_t span = range_end - range_start; + if (opts->order == ORDER_SEQUENCE && + span < (off_t)(opts->block_size * opts->block_count)) { + fprintf(stderr, "Range is too small for sequential access\n"); + return -1; + } + + return 0; +} + +static off_t pick_offset(const options_t* opts, off_t range_start, size_t index) { + if (opts->order == ORDER_RANDOM) { + off_t range_bytes = opts->range_end - opts->range_start; + if (range_bytes < (off_t)opts->block_size) + return opts->range_start; + + off_t slots = range_bytes / (off_t)opts->block_size; + if (slots <= 0) + slots = 1; + off_t slot = (off_t)(rand() % (int)slots); + return opts->range_start + slot * (off_t)opts->block_size; + } + + return range_start + (off_t)(index * opts->block_size); +} + +int run_io_workload(const options_t* opts) { + int flags = (opts->mode == MODE_READ) ? O_RDONLY : (O_WRONLY | O_CREAT); + int fd = open(opts->path, flags, 0666); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", opts->path, strerror(errno)); + return 1; + } + +#if defined(F_NOCACHE) + if (opts->use_direct && fcntl(fd, F_NOCACHE, 1) != 0) + fprintf(stderr, "fcntl failed: %s\n", strerror(errno)); +#else + if (opts->use_direct) + fprintf(stderr, "Warning: F_NOCACHE not supported on this platform\n"); +#endif + + struct stat st; + if (fstat(fd, &st) != 0) { + fprintf(stderr, "fstat failed: %s\n", strerror(errno)); + close(fd); + return 1; + } + + options_t local_opts = *opts; + if (!local_opts.range_set) { + local_opts.range_start = 0; + if (local_opts.mode == MODE_READ) + local_opts.range_end = st.st_size; + else + local_opts.range_end = local_opts.range_start + (off_t)(local_opts.block_size * local_opts.block_count); + } + + if (check_range(&local_opts, &st) != 0) { + close(fd); + return 1; + } + + void* buffer = allocate_buffer(local_opts.block_size); + if (buffer == NULL) { + close(fd); + return 1; + } + + if (local_opts.mode == MODE_WRITE) { + for (size_t i = 0; i < local_opts.block_size; ++i) + ((unsigned char*)buffer)[i] = (unsigned char)('A' + (i % 26)); + } + + srand(0); + + struct timespec total_start = {0}, total_end = {0}; + clock_gettime(CLOCK_MONOTONIC, &total_start); + + for (int r = 0; r < local_opts.repeat; ++r) { + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); + + for (size_t i = 0; i < local_opts.block_count; ++i) { + off_t offset = pick_offset(&local_opts, local_opts.range_start, i); + ssize_t done; + if (local_opts.mode == MODE_READ) { + done = pread(fd, buffer, local_opts.block_size, offset); + } else { + done = pwrite(fd, buffer, local_opts.block_size, offset); + } + if (done < 0) { + fprintf(stderr, "I/O error at block %zu: %s\n", i, strerror(errno)); + free(buffer); + close(fd); + return 1; + } + if ((size_t)done != local_opts.block_size) { + fprintf(stderr, "Short transfer at block %zu\n", i); + free(buffer); + close(fd); + return 1; + } + } + + clock_gettime(CLOCK_MONOTONIC, &end); + time_t sec = end.tv_sec - start.tv_sec; + long nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec -= 1; + nsec += 1000000000L; + } + double elapsed = (double)sec + (double)nsec / 1e9; + printf("Iteration %d: blocks=%zu size=%zu bytes time=%.6f s\n", r + 1, local_opts.block_count, local_opts.block_size, elapsed); + } + + clock_gettime(CLOCK_MONOTONIC, &total_end); + printf("Total time: %.6f s\n", seconds_between(&total_start, &total_end)); + + free(buffer); + close(fd); + return 0; +} diff --git a/lab/vtpc/lib/io_load_vtpc.c b/lab/vtpc/lib/io_load_vtpc.c new file mode 100644 index 0000000..a24a8b5 --- /dev/null +++ b/lab/vtpc/lib/io_load_vtpc.c @@ -0,0 +1,194 @@ +#include "io_load.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vtpc.h" + +static void* allocate_buffer(size_t size) { + void* buffer = malloc(size); + if (buffer == NULL) + fprintf(stderr, "malloc failed\n"); + return buffer; +} + +static double seconds_between(const struct timespec* start, + const struct timespec* end) { + time_t sec = end->tv_sec - start->tv_sec; + long nsec = end->tv_nsec - start->tv_nsec; + if (nsec < 0) { + sec -= 1; + nsec += 1000000000L; + } + return (double)sec + (double)nsec / 1e9; +} + +static int check_range(const options_t* opts, const struct stat* st) { + off_t range_start = opts->range_start; + off_t range_end = opts->range_end; + + if (!opts->range_set) { + range_start = 0; + if (opts->mode == MODE_READ) { + range_end = st->st_size; + } else { + range_end = range_start + (off_t)(opts->block_size * opts->block_count); + } + } + + if (opts->mode == MODE_READ && range_end > st->st_size) { + fprintf(stderr, "Range exceeds file size for read mode\n"); + return -1; + } + + if (range_end <= range_start) { + fprintf(stderr, "Invalid range\n"); + return -1; + } + + off_t span = range_end - range_start; + if (opts->order == ORDER_SEQUENCE && + span < (off_t)(opts->block_size * opts->block_count)) { + fprintf(stderr, "Range is too small for sequential access\n"); + return -1; + } + + return 0; +} + +static off_t pick_offset(const options_t* opts, off_t range_start, size_t index) { + if (opts->order == ORDER_RANDOM) { + off_t range_bytes = opts->range_end - opts->range_start; + if (range_bytes < (off_t)opts->block_size) + return opts->range_start; + + off_t slots = range_bytes / (off_t)opts->block_size; + if (slots <= 0) + slots = 1; + off_t slot = (off_t)(rand() % (int)slots); + return opts->range_start + slot * (off_t)opts->block_size; + } + + return range_start + (off_t)(index * opts->block_size); +} + +static int vtpc_seek(int fd, off_t offset) { + if (vtpc_lseek(fd, offset, SEEK_SET) == (off_t)-1) { + fprintf(stderr, "vtpc_lseek failed: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +int run_io_workload(const options_t* opts) { + struct stat st; + memset(&st, 0, sizeof(st)); + if (opts->mode == MODE_READ) { + if (stat(opts->path, &st) != 0) { + fprintf(stderr, "stat failed: %s\n", strerror(errno)); + return 1; + } + } + + int flags = (opts->mode == MODE_READ) ? O_RDONLY : (O_WRONLY | O_CREAT); + int fd = vtpc_open(opts->path, flags, 0666); + if (fd < 0) { + fprintf(stderr, "Failed to open %s via vtpc: %s\n", opts->path, strerror(errno)); + return 1; + } + + if (opts->use_direct) { + fprintf(stderr, "Note: --direct is handled inside vtpc_open (O_DIRECT/F_NOCACHE best-effort)\n"); + } + + options_t local_opts = *opts; + if (!local_opts.range_set) { + local_opts.range_start = 0; + if (local_opts.mode == MODE_READ) + local_opts.range_end = st.st_size; + else + local_opts.range_end = local_opts.range_start + (off_t)(local_opts.block_size * local_opts.block_count); + } + + if (check_range(&local_opts, &st) != 0) { + vtpc_close(fd); + return 1; + } + + void* buffer = allocate_buffer(local_opts.block_size); + if (buffer == NULL) { + vtpc_close(fd); + return 1; + } + + if (local_opts.mode == MODE_WRITE) { + for (size_t i = 0; i < local_opts.block_size; ++i) + ((unsigned char*)buffer)[i] = (unsigned char)('A' + (i % 26)); + } + + srand(0); + + struct timespec total_start = {0}, total_end = {0}; + clock_gettime(CLOCK_MONOTONIC, &total_start); + + for (int r = 0; r < local_opts.repeat; ++r) { + struct timespec start, end; + clock_gettime(CLOCK_MONOTONIC, &start); + + for (size_t i = 0; i < local_opts.block_count; ++i) { + off_t offset = pick_offset(&local_opts, local_opts.range_start, i); + if (vtpc_seek(fd, offset) != 0) { + free(buffer); + vtpc_close(fd); + return 1; + } + + ssize_t done; + if (local_opts.mode == MODE_READ) { + done = vtpc_read(fd, buffer, local_opts.block_size); + } else { + done = vtpc_write(fd, buffer, local_opts.block_size); + } + + if (done < 0) { + fprintf(stderr, "I/O error at block %zu: %s\n", i, strerror(errno)); + free(buffer); + vtpc_close(fd); + return 1; + } + if ((size_t)done != local_opts.block_size) { + fprintf(stderr, "Short transfer at block %zu\n", i); + free(buffer); + vtpc_close(fd); + return 1; + } + } + + clock_gettime(CLOCK_MONOTONIC, &end); + time_t sec = end.tv_sec - start.tv_sec; + long nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec -= 1; + nsec += 1000000000L; + } + double elapsed = (double)sec + (double)nsec / 1e9; + printf("Iteration %d: blocks=%zu size=%zu bytes time=%.6f s\n", r + 1, local_opts.block_count, local_opts.block_size, elapsed); + } + + clock_gettime(CLOCK_MONOTONIC, &total_end); + printf("Total time (vtpc): %.6f s\n", seconds_between(&total_start, &total_end)); + + if (vtpc_fsync(fd) != 0) + fprintf(stderr, "vtpc_fsync failed: %s\n", strerror(errno)); + + free(buffer); + vtpc_close(fd); + return 0; +} diff --git a/lab/vtpc/lib/vtpc.c b/lab/vtpc/lib/vtpc.c index 173ab1b..d96af98 100644 --- a/lab/vtpc/lib/vtpc.c +++ b/lab/vtpc/lib/vtpc.c @@ -1,30 +1,447 @@ #include "vtpc.h" +#include #include -#include -#include +#include +#include +#include +#include #include +#define VTPC_MAX_FILES 128 +#define VTPC_PAGE_CAPACITY 64 + +struct vtpc_page { + off_t base; + size_t valid; + int dirty; + int in_use; + uint64_t last_access; + char* data; +}; + +struct vtpc_file { + int fd; + int can_read; + int can_write; + int direct_io; + off_t position; + off_t file_size; + size_t page_size; + size_t capacity; + uint64_t access_clock; + struct vtpc_page* pages; +}; + +static struct vtpc_file* g_files[VTPC_MAX_FILES]; + +static size_t vtpc_get_page_size(void) { + long page = sysconf(_SC_PAGESIZE); + if (page <= 0) + page = 4096; + return (size_t)page; +} + +static off_t vtpc_align_down(off_t value, size_t align) { + off_t mod = value % (off_t)align; + if (mod < 0) + mod += (off_t)align; + return value - mod; +} + +static size_t vtpc_min_size(size_t a, size_t b) { + return (a < b) ? a : b; +} + +static size_t vtpc_max_size(size_t a, size_t b) { + return (a > b) ? a : b; +} + +static struct vtpc_file* vtpc_lookup(int handle) { + if (handle < 0 || handle >= VTPC_MAX_FILES) + return NULL; + return g_files[handle]; +} + +static int vtpc_store(struct vtpc_file* file) { + for (int i = 0; i < VTPC_MAX_FILES; ++i) { + if (g_files[i] == NULL) { + g_files[i] = file; + return i; + } + } + errno = EMFILE; + return -1; +} + +static void vtpc_drop(int handle) { + if (handle < 0 || handle >= VTPC_MAX_FILES) + return; + g_files[handle] = NULL; +} + +static int vtpc_open_raw(const char* path, int mode, int access, int* direct_io) { + int fd = -1; + *direct_io = 0; + +#ifdef O_DIRECT + fd = open(path, mode | O_DIRECT, access); + if (fd >= 0) { + *direct_io = 1; + return fd; + } + if (errno != EINVAL && errno != EOPNOTSUPP) + return fd; +#endif + + fd = open(path, mode, access); + if (fd < 0) + return fd; + +#ifdef F_NOCACHE + if (fcntl(fd, F_NOCACHE, 1) == 0) + *direct_io = 1; +#endif + + return fd; +} + +static int vtpc_alloc_pages(struct vtpc_file* file) { + file->pages = calloc(file->capacity, sizeof(*file->pages)); + if (file->pages == NULL) + return -1; + + for (size_t i = 0; i < file->capacity; ++i) { + if (posix_memalign((void**)&file->pages[i].data, file->page_size, file->page_size) != 0) { + for (size_t j = 0; j < i; ++j) + free(file->pages[j].data); + free(file->pages); + file->pages = NULL; + return -1; + } + memset(file->pages[i].data, 0, file->page_size); + } + return 0; +} + +static struct vtpc_page* vtpc_find_page(struct vtpc_file* file, off_t base) { + for (size_t i = 0; i < file->capacity; ++i) { + if (file->pages[i].in_use && file->pages[i].base == base) + return &file->pages[i]; + } + return NULL; +} + +static int vtpc_flush_page(struct vtpc_file* file, struct vtpc_page* page) { + if (!page->in_use || !page->dirty) + return 0; + + if (file->file_size <= page->base) { + page->dirty = 0; + return 0; + } + + size_t len = (size_t)vtpc_min_size((size_t)(file->file_size - page->base), file->page_size); + if (len == 0) { + page->dirty = 0; + return 0; + } + + size_t write_len = len; + if (file->direct_io) + write_len = file->page_size; + + ssize_t written = pwrite(file->fd, page->data, write_len, page->base); + if (written < 0 || (size_t)written != write_len) + return -1; + +#if defined(POSIX_FADV_DONTNEED) + (void)posix_fadvise(file->fd, page->base, (off_t)write_len, POSIX_FADV_DONTNEED); +#endif + + if (write_len > len && ftruncate(file->fd, file->file_size) != 0) + return -1; + + page->dirty = 0; + return 0; +} + +static struct vtpc_page* vtpc_pick_victim(struct vtpc_file* file) { + struct vtpc_page* victim = NULL; + for (size_t i = 0; i < file->capacity; ++i) { + if (!file->pages[i].in_use) + continue; + if (victim == NULL || file->pages[i].last_access > victim->last_access) + victim = &file->pages[i]; + } + return victim; +} + +static struct vtpc_page* vtpc_prepare_page(struct vtpc_file* file, off_t base) { + struct vtpc_page* page = vtpc_find_page(file, base); + if (page != NULL) + return page; + + for (size_t i = 0; i < file->capacity; ++i) { + if (!file->pages[i].in_use) { + page = &file->pages[i]; + break; + } + } + + if (page == NULL) { + page = vtpc_pick_victim(file); + if (page == NULL) { + errno = ENOMEM; + return NULL; + } + if (vtpc_flush_page(file, page) != 0) + return NULL; + } + + page->in_use = 1; + page->base = base; + page->valid = 0; + page->dirty = 0; + page->last_access = 0; + + ssize_t done = pread(file->fd, page->data, file->page_size, page->base); + if (done < 0) { + page->in_use = 0; + return NULL; + } + +#if defined(POSIX_FADV_DONTNEED) + (void)posix_fadvise(file->fd, page->base, (off_t)file->page_size, POSIX_FADV_DONTNEED); +#endif + + page->valid = (size_t)done; + if (page->valid < file->page_size) + memset(page->data + page->valid, 0, file->page_size - page->valid); + + return page; +} + +static int vtpc_flush_all(struct vtpc_file* file) { + int has_dirty = 0; + for (size_t i = 0; i < file->capacity; ++i) { + if (file->pages[i].in_use && file->pages[i].dirty) { + has_dirty = 1; + if (vtpc_flush_page(file, &file->pages[i]) != 0) + return -1; + } + } + + if (has_dirty && file->can_write) { + if (ftruncate(file->fd, file->file_size) != 0) + return -1; + } + + if (fsync(file->fd) != 0) + return -1; + + return 0; +} + int vtpc_open(const char* path, int mode, int access) { - return open(path, mode, access); + size_t page_size = vtpc_get_page_size(); + + struct vtpc_file* file = calloc(1, sizeof(*file)); + if (file == NULL) + return -1; + + file->page_size = page_size; + file->capacity = VTPC_PAGE_CAPACITY; + + int direct_io = 0; + int fd = vtpc_open_raw(path, mode, access, &direct_io); + if (fd < 0) { + free(file); + return -1; + } + + struct stat st; + if (fstat(fd, &st) != 0) { + close(fd); + free(file); + return -1; + } + + file->fd = fd; + file->file_size = st.st_size; + file->position = 0; + file->direct_io = direct_io; + file->access_clock = 0; + + int accmode = mode & O_ACCMODE; + file->can_read = (accmode == O_RDONLY || accmode == O_RDWR); + file->can_write = (accmode == O_WRONLY || accmode == O_RDWR); + + if (vtpc_alloc_pages(file) != 0) { + close(fd); + free(file); + errno = ENOMEM; + return -1; + } + + int handle = vtpc_store(file); + if (handle < 0) { + for (size_t i = 0; i < file->capacity; ++i) + free(file->pages[i].data); + free(file->pages); + close(fd); + free(file); + return -1; + } + + return handle; } int vtpc_close(int fd) { - return close(fd); + struct vtpc_file* file = vtpc_lookup(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + + int result = 0; + if (vtpc_flush_all(file) != 0) + result = -1; + + if (close(file->fd) != 0) + result = -1; + + for (size_t i = 0; i < file->capacity; ++i) + free(file->pages[i].data); + free(file->pages); + free(file); + vtpc_drop(fd); + return result; } ssize_t vtpc_read(int fd, void* buf, size_t count) { - return read(fd, buf, count); + struct vtpc_file* file = vtpc_lookup(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + if (!file->can_read) { + errno = EBADF; + return -1; + } + if (count == 0) + return 0; + + size_t total = 0; + while (total < count) { + if (file->position >= file->file_size) + break; + + off_t base = vtpc_align_down(file->position, file->page_size); + size_t page_off = (size_t)(file->position - base); + size_t max_in_page = file->page_size - page_off; + + size_t remaining = count - total; + size_t available = (size_t)vtpc_min_size((size_t)(file->file_size - file->position), max_in_page); + size_t chunk = vtpc_min_size(remaining, available); + + if (chunk == 0) + break; + + struct vtpc_page* page = vtpc_prepare_page(file, base); + if (page == NULL) + return -1; + + page->last_access = ++file->access_clock; + + if (page->valid < page_off + chunk) + chunk = (page->valid > page_off) ? (page->valid - page_off) : 0; + + if (chunk == 0) + break; + + memcpy((char*)buf + total, page->data + page_off, chunk); + total += chunk; + file->position += (off_t)chunk; + } + + return (ssize_t)total; } ssize_t vtpc_write(int fd, const void* buf, size_t count) { - return write(fd, buf, count); + struct vtpc_file* file = vtpc_lookup(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + if (!file->can_write) { + errno = EBADF; + return -1; + } + if (count == 0) + return 0; + + size_t total = 0; + while (total < count) { + off_t base = vtpc_align_down(file->position, file->page_size); + size_t page_off = (size_t)(file->position - base); + size_t remaining = count - total; + size_t chunk = vtpc_min_size(remaining, file->page_size - page_off); + + struct vtpc_page* page = vtpc_prepare_page(file, base); + if (page == NULL) + return -1; + + memcpy(page->data + page_off, (const char*)buf + total, chunk); + page->valid = vtpc_min_size(file->page_size, vtpc_max_size(page->valid, page_off + chunk)); + page->dirty = 1; + page->last_access = ++file->access_clock; + + total += chunk; + file->position += (off_t)chunk; + + off_t new_end = base + (off_t)vtpc_max_size(page->valid, page_off + chunk); + if (new_end > file->file_size) + file->file_size = new_end; + } + + return (ssize_t)total; } off_t vtpc_lseek(int fd, off_t offset, int whence) { - return lseek(fd, offset, whence); + struct vtpc_file* file = vtpc_lookup(fd); + if (file == NULL) { + errno = EBADF; + return (off_t)-1; + } + + off_t base = 0; + if (whence == SEEK_SET) { + base = offset; + } else if (whence == SEEK_CUR) { + base = file->position + offset; + } else if (whence == SEEK_END) { + base = file->file_size + offset; + } else { + errno = EINVAL; + return (off_t)-1; + } + + if (base < 0) { + errno = EINVAL; + return (off_t)-1; + } + + file->position = base; + return base; } int vtpc_fsync(int fd) { - return fsync(fd); + struct vtpc_file* file = vtpc_lookup(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + + return vtpc_flush_all(file); } From 9ec4080eafaddf431ddb72037e30ee2219a2d2c4 Mon Sep 17 00:00:00 2001 From: Karim Date: Fri, 12 Dec 2025 10:18:55 +0300 Subject: [PATCH 2/2] Fix something --- lab/vtpc/.gitignore | 6 +++++- lab/vtpc/lib/vtpc.c | 26 ++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lab/vtpc/.gitignore b/lab/vtpc/.gitignore index 6e2b275..b91b9c0 100644 --- a/lab/vtpc/.gitignore +++ b/lab/vtpc/.gitignore @@ -7,4 +7,8 @@ build/ # Python __pycache__/ -.vscode \ No newline at end of file +.vscode + +.idea + +*.bin diff --git a/lab/vtpc/lib/vtpc.c b/lab/vtpc/lib/vtpc.c index d96af98..cf1838a 100644 --- a/lab/vtpc/lib/vtpc.c +++ b/lab/vtpc/lib/vtpc.c @@ -74,10 +74,10 @@ static int vtpc_store(struct vtpc_file* file) { return -1; } -static void vtpc_drop(int handle) { - if (handle < 0 || handle >= VTPC_MAX_FILES) +static void vtpc_drop(int fd) { + if (fd < 0 || fd >= VTPC_MAX_FILES) return; - g_files[handle] = NULL; + g_files[fd] = NULL; } static int vtpc_open_raw(const char* path, int mode, int access, int* direct_io) { @@ -155,9 +155,6 @@ static int vtpc_flush_page(struct vtpc_file* file, struct vtpc_page* page) { if (written < 0 || (size_t)written != write_len) return -1; -#if defined(POSIX_FADV_DONTNEED) - (void)posix_fadvise(file->fd, page->base, (off_t)write_len, POSIX_FADV_DONTNEED); -#endif if (write_len > len && ftruncate(file->fd, file->file_size) != 0) return -1; @@ -210,7 +207,6 @@ static struct vtpc_page* vtpc_prepare_page(struct vtpc_file* file, off_t base) { page->in_use = 0; return NULL; } - #if defined(POSIX_FADV_DONTNEED) (void)posix_fadvise(file->fd, page->base, (off_t)file->page_size, POSIX_FADV_DONTNEED); #endif @@ -253,8 +249,19 @@ int vtpc_open(const char* path, int mode, int access) { file->page_size = page_size; file->capacity = VTPC_PAGE_CAPACITY; + int accmode = mode & O_ACCMODE; + int direct_io = 0; - int fd = vtpc_open_raw(path, mode, access, &direct_io); + int fd = -1; + + if (accmode == O_WRONLY) { + int rw_mode = (mode & ~O_ACCMODE) | O_RDWR; + fd = vtpc_open_raw(path, rw_mode, access, &direct_io); + } + + if (fd < 0) + fd = vtpc_open_raw(path, mode, access, &direct_io); + if (fd < 0) { free(file); return -1; @@ -273,7 +280,6 @@ int vtpc_open(const char* path, int mode, int access) { file->direct_io = direct_io; file->access_clock = 0; - int accmode = mode & O_ACCMODE; file->can_read = (accmode == O_RDONLY || accmode == O_RDWR); file->can_write = (accmode == O_WRONLY || accmode == O_RDWR); @@ -342,7 +348,7 @@ ssize_t vtpc_read(int fd, void* buf, size_t count) { size_t max_in_page = file->page_size - page_off; size_t remaining = count - total; - size_t available = (size_t)vtpc_min_size((size_t)(file->file_size - file->position), max_in_page); + size_t available = vtpc_min_size((size_t)(file->file_size - file->position), max_in_page); size_t chunk = vtpc_min_size(remaining, available); if (chunk == 0)