commit e7410e29eb092d28a616a7e1bf805d39c154d987 from: lex0de date: Mon Aug 31 19:24:12 2026 UTC disk fixes commit - 881c6377d4b550f742935f30340d6a2fc92426f0 commit + e7410e29eb092d28a616a7e1bf805d39c154d987 blob - d2f8c1fdb899a833e10af3bccfc4d275b6f63e82 blob + bc69f8c2d60c1508f5e3361e85b1d6eb3b6baad9 --- changelog.md +++ changelog.md @@ -2,6 +2,12 @@ # 2026-08-31 +## Ext2 indirect write performance + +- Restored 4 KiB ext2 write batching for aligned and partial-block operations, replacing eight 512-byte service requests with one batched request. +- Removed redundant initialization and rereading when adding the first singly indirect block pointer. +- Added filesystem benchmark validation across the direct-to-indirect boundary and increased the syscall benchmark sample to one million calls. + ## Ubuntu 26.04 toolchain setup and x86_64 boot reliability - Added Ubuntu 26.04 build and QEMU environment setup, plus a root-level helper for building and installing the Mercuron LLVM toolchain from `third-party/ports/llvm`. blob - 924031f1060d6c236bf3817eb6f7ba70fce9f0dd blob + 42cc40383a9fb3a8cefc75783f0b6323379f73ea --- servers/fs/ext2/main.c +++ servers/fs/ext2/main.c @@ -521,10 +521,9 @@ ext2_write_bytes(uint64_t offset, const void *buf, siz if (block_off == 0 && chunk == ext2_ctx.block_size) { /* Aligned full block write - no RMW needed */ uint64_t sector = block_start / EXT2_SECTOR_SIZE; - for (uint32_t s = 0; s < ext2_ctx.sectors_per_block; s++) { - if (block_write_sectors(sector + s, 1, in + (s * EXT2_SECTOR_SIZE)) != 0) + if (block_write_sectors(sector, + ext2_ctx.sectors_per_block, in) != 0) return -1; - } in += ext2_ctx.block_size; offset += ext2_ctx.block_size; len -= ext2_ctx.block_size; @@ -533,15 +532,13 @@ ext2_write_bytes(uint64_t offset, const void *buf, siz /* Partial block write - RMW at edge */ uint64_t sector = block_start / EXT2_SECTOR_SIZE; - for (uint32_t s = 0; s < ext2_ctx.sectors_per_block; s++) { - if (block_read_sectors(sector + s, 1, temp + (s * EXT2_SECTOR_SIZE)) != 0) + if (block_read_sectors(sector, + ext2_ctx.sectors_per_block, temp) != 0) return -1; - } memcpy(temp + block_off, in, chunk); - for (uint32_t s = 0; s < ext2_ctx.sectors_per_block; s++) { - if (block_write_sectors(sector + s, 1, temp + (s * EXT2_SECTOR_SIZE)) != 0) + if (block_write_sectors(sector, + ext2_ctx.sectors_per_block, temp) != 0) return -1; - } in += chunk; offset += chunk; len -= chunk; @@ -1907,66 +1904,68 @@ ext2_inode_block(const struct ext2_inode *inode, uint3 static int ext2_inode_set_block(struct ext2_inode *inode, uint32_t idx, uint32_t block) { - static uint8_t indir_buf[EXT2_MAX_BLOCK_SIZE]; + static uint8_t indir_buf[EXT2_MAX_BLOCK_SIZE]; + uint32_t *entries; + uint32_t indirect_idx; + uint32_t indir_block_no; + uint32_t per_block; - /* Direct blocks (0-11) */ - if (idx < 12) { - inode->i_block[idx] = block; - return 0; - } - - /* Single indirect blocks (12 to 12 + per_block - 1) */ - uint32_t per_block = ext2_ctx.block_size / sizeof(uint32_t); - if (idx < 12 + per_block) { - uint32_t indirect_idx = idx - 12; - - /* Check if we need to allocate the indirect block itself */ - if (inode->i_block[12] == 0) { - /* Allocate the indirect block */ - uint32_t indir_block_no; - if (ext2_allocate_block(&indir_block_no) != 0) { - printf("[ext2] ext2_inode_set_block: failed to allocate indirect block\n"); - return -1; - } - #ifdef LENIX_DEBUG - printf("[ext2] ext2_inode_set_block: allocated indirect block %u\n", indir_block_no); - #endif - inode->i_block[12] = indir_block_no; - - /* Initialize the indirect block to all zeros */ - memset(indir_buf, 0, ext2_ctx.block_size); - if (ext2_write_block(indir_block_no, indir_buf) != 0) { - printf("[ext2] ext2_inode_set_block: failed to initialize indirect block\n"); - return -1; - } - } - - /* Read the indirect block */ - if (ext2_read_block(inode->i_block[12], indir_buf) != 0) { - printf("[ext2] ext2_inode_set_block: failed to read indirect block %u\n", inode->i_block[12]); - return -1; - } - - /* Update the block pointer in the indirect block */ - uint32_t *entries = (uint32_t *)indir_buf; - entries[indirect_idx] = block; - - /* Write the indirect block back */ - if (ext2_write_block(inode->i_block[12], indir_buf) != 0) { - printf("[ext2] ext2_inode_set_block: failed to write indirect block %u\n", inode->i_block[12]); - return -1; - } - - #ifdef LENIX_DEBUG - printf("[ext2] ext2_inode_set_block: set block[%u] = %u in indirect block\n", idx, block); - #endif + /* Direct blocks (0-11). */ + if (idx < 12) { + inode->i_block[idx] = block; return 0; - } + } - /* Double and triple indirect blocks not yet supported */ - printf("[ext2] ext2_inode_set_block: ERROR - block index %u not supported (max %u for single indirect)\n", - idx, 12 + per_block - 1); - return -1; + /* Single indirect blocks (12 to 12 + per_block - 1). */ + per_block = ext2_ctx.block_size / sizeof(uint32_t); + if (idx >= 12 + per_block) { + printf("[ext2] ext2_inode_set_block: ERROR - block index %u " + "not supported (max %u for single indirect)\n", + idx, 12 + per_block - 1); + return -1; + } + + indirect_idx = idx - 12; + indir_block_no = inode->i_block[12]; + if (indir_block_no == 0) { + if (ext2_allocate_block(&indir_block_no) != 0) { + puts("[ext2] ext2_inode_set_block: failed to allocate " + "indirect block"); + return -1; + } +#ifdef LENIX_DEBUG + printf("[ext2] ext2_inode_set_block: allocated indirect " + "block %u\n", indir_block_no); +#endif + memset(indir_buf, 0, ext2_ctx.block_size); + entries = (uint32_t *)indir_buf; + entries[indirect_idx] = block; + if (ext2_write_block(indir_block_no, indir_buf) != 0) { + puts("[ext2] ext2_inode_set_block: failed to write new " + "indirect block"); + return -1; + } + inode->i_block[12] = indir_block_no; + } else { + if (ext2_read_block(indir_block_no, indir_buf) != 0) { + printf("[ext2] ext2_inode_set_block: failed to read " + "indirect block %u\n", indir_block_no); + return -1; + } + entries = (uint32_t *)indir_buf; + entries[indirect_idx] = block; + if (ext2_write_block(indir_block_no, indir_buf) != 0) { + printf("[ext2] ext2_inode_set_block: failed to write " + "indirect block %u\n", indir_block_no); + return -1; + } + } + +#ifdef LENIX_DEBUG + printf("[ext2] ext2_inode_set_block: set block[%u] = %u in " + "indirect block\n", idx, block); +#endif + return 0; } static int ext2_dir_lookup(const struct ext2_inode *dir_inode, blob - 744fd4f61aab484bb5ff14dc0eb38561b5264922 blob + 6a0a754703653f6c7041f7affb80153972eb6cc4 --- user/bench/bench_fs_read.c +++ user/bench/bench_fs_read.c @@ -17,6 +17,7 @@ #define FS_ITERATIONS 10 #define FS_BUFSIZE 4096 #define FS_WRITE_BLOCKS 16 +#define FS_FILE_SIZE (FS_BUFSIZE * FS_WRITE_BLOCKS) #define FS_TEST_PATH "/fstest" #define FS_BENCH_VERBOSE 0 #define FS_BENCH_DIAG 1 /* Diagnostic timing breakdowns */ @@ -29,6 +30,77 @@ #define FS_SUMMARY(...) printf(__VA_ARGS__) +static void +fill_test_block(char *buf, int block_index) +{ + memset(buf, block_index + 1, FS_BUFSIZE); +} + +static int +verify_test_file(void) +{ + char buf[FS_BUFSIZE]; + ssize_t n; + int fd; + + fd = open(FS_TEST_PATH, O_RDONLY, 0); + if (fd < 0) { + FS_SUMMARY("[bench_fs_read] ERROR: validation open failed " + "(errno=%d)\n", errno); + return -1; + } + + for (int block = 0; block < FS_WRITE_BLOCKS; block++) { + size_t block_bytes = 0; + + while (block_bytes < sizeof(buf)) { + n = read(fd, buf + block_bytes, + sizeof(buf) - block_bytes); + if (n < 0) { + FS_SUMMARY("[bench_fs_read] ERROR: validation read " + "failed at block %d (errno=%d)\n", block, + errno); + close(fd); + return -1; + } + if (n == 0) { + FS_SUMMARY("[bench_fs_read] ERROR: validation " + "reached EOF at block %d\n", block); + close(fd); + return -1; + } + block_bytes += (size_t)n; + } + + for (size_t offset = 0; offset < sizeof(buf); offset++) { + if ((uint8_t)buf[offset] != (uint8_t)(block + 1)) { + FS_SUMMARY("[bench_fs_read] ERROR: validation " + "mismatch at block %d offset %zu\n", block, + offset); + close(fd); + return -1; + } + } + } + + n = read(fd, buf, 1); + if (n != 0) { + FS_SUMMARY("[bench_fs_read] ERROR: validation file size " + "mismatch\n"); + close(fd); + return -1; + } + if (close(fd) < 0) { + FS_SUMMARY("[bench_fs_read] ERROR: validation close failed " + "(errno=%d)\n", errno); + return -1; + } + + FS_SUMMARY("[bench_fs_read] Validation passed: %d bytes\n", + FS_FILE_SIZE); + return 0; +} + int main(void) { @@ -51,8 +123,8 @@ main(void) } char buf[FS_BUFSIZE]; - memset(buf, 0x42, sizeof(buf)); for (int i = 0; i < FS_WRITE_BLOCKS; i++) { + fill_test_block(buf, i); ssize_t wrote = write(fd, buf, sizeof(buf)); if (wrote != (ssize_t)sizeof(buf)) { FS_SUMMARY("[bench_fs_read] ERROR: write failed at block %d (errno=%d)\n", @@ -65,6 +137,8 @@ main(void) FS_SUMMARY("[bench_fs_read] ERROR: close failed after write (errno=%d)\n", errno); return 1; } + if (verify_test_file() != 0) + return 1; /* Phase 2: timed read benchmark */ #if FS_BENCH_DIAG @@ -157,8 +231,13 @@ main(void) bench_ns = (uint64_t)(bench_end.tv_sec - bench_start.tv_sec) * 1000000000ULL + (uint64_t)(bench_end.tv_nsec - bench_start.tv_nsec); - if (iterations_done == 0) - iterations_done = 1; /* avoid div-by-zero if everything failed */ + if (iterations_done != FS_ITERATIONS || + total_bytes != (uint64_t)FS_FILE_SIZE * FS_ITERATIONS) { + FS_SUMMARY("[bench_fs_read] ERROR: incomplete benchmark " + "(%d iterations, %llu bytes)\n", iterations_done, + (unsigned long long)total_bytes); + return 1; + } avg_iter_ns = bench_ns / (uint64_t)iterations_done; throughput = (bench_ns > 0) ? (total_bytes * 1000000000ULL) / bench_ns : 0; blob - ef3da32a0f2ff18210e491d8f217e921fa98ecac blob + 109184356b181a1709846caa73074c995200add3 --- user/bench/bench_syscall.c +++ user/bench/bench_syscall.c @@ -9,7 +9,7 @@ #include #include -#define ITERATIONS 100000 +#define ITERATIONS 1000000 #define WARMUP_ITERATIONS 1000 /* Minimal syscall wrapper - prevents compiler optimization */ @@ -55,6 +55,10 @@ main(void) /* Calculate elapsed time in nanoseconds */ elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL + (end.tv_nsec - start.tv_nsec); + if (elapsed_ns == 0) { + puts("[bench_syscall] ERROR: elapsed time is zero"); + return 1; + } ns_per_call = (long)(elapsed_ns / ITERATIONS);