From 55fd46a18629a6a645e394453dc5ddbcabd9dadb Mon Sep 17 00:00:00 2001 From: "Tse-Chia.Chang" Date: Mon, 24 Jun 2024 23:08:47 +0800 Subject: [PATCH] Reimplement the read and the write operations This commit implements the 'simplefs_write' and 'simplefs_read' functions for the simplefs filesystem. These functions handle data writing and reading operations directly from the disk, bypassing the page cache. To achieve this, we used the 'sb_bread' and 'brelse' functions provided by the Linux kernel to read data directly from the disk. For writing data, we used the 'mark_buffer_dirty' and 'sync_dirty_buffer' functions to ensure that data is correctly written to the disk. Removing 'generic_file_read_iter' and 'generic_file_write_iter'. Through the following experiment, we can observe that once we implement 'simplefs_read', 'generic_file_read_iter' is no longer called, and the same applies to simplefs_write. Experiment: First, wrap 'generic_file_read_iter' with 'test_generic_file_read_iter' and then set up the following: $ echo function > current_tracer $ echo test_generic_file_read_iter > set_ftrace_filter $ echo simplefs_read > set_ftrace_filter Run the fio command: $ fio --name=readtest --size=1M --filename=./testfile --bs=4k \ --rw=read --iodepth=4 From the results, we can see that 'test_generic_file_read_iter' is no longer used. root@vm:/sys/kernel/debug/tracing# cat trace tracer: function entries-in-buffer/entries-written: 256/256 #P:4 _-----=> irqs-off/BH-disabled / _----=> need-resched | / _---=> hardirq/softirq || / _--=> preempt-depth ||| / _-=> migrate-disable |||| / delay TASK-PID CPU# ||||| TIMESTAMP FUNCTION | | | ||||| | | fio-44553 [001] ...1. 83186.031300: simplefs_read <-vfs_read fio-44553 [001] ...1. 83186.031325: simplefs_read <-vfs_read fio-44553 [001] ...1. 83186.031328: simplefs_read <-vfs_read fio-44553 [001] ...1. 83186.031332: simplefs_read <-vfs_read ... fio-44553 [001] ...1. 83186.031880: simplefs_read <-vfs_read Close #57 --- file.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++-- simplefs.h | 2 + 2 files changed, 170 insertions(+), 4 deletions(-) diff --git a/file.c b/file.c index 73131e8..d59de09 100644 --- a/file.c +++ b/file.c @@ -255,7 +255,7 @@ static int simplefs_write_end(struct file *file, * over the data block pointers, releasing the associated data blocks, and * updating the inode metadata (size and block count). */ -static int simplefs_file_open(struct inode *inode, struct file *filp) +static int simplefs_open(struct inode *inode, struct file *filp) { bool wronly = (filp->f_flags & O_WRONLY); bool rdwr = (filp->f_flags & O_RDWR); @@ -293,6 +293,170 @@ static int simplefs_file_open(struct inode *inode, struct file *filp) return 0; } +static ssize_t simplefs_read(struct file *file, + char __user *buf, + size_t len, + loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + ssize_t bytes_read = 0; + loff_t pos = *ppos; + + if (pos > inode->i_size) + return 0; + + /* find extent block */ + struct buffer_head *bh = sb_bread(sb, SIMPLEFS_INODE(inode)->ei_block); + struct simplefs_file_ei_block *ei_block = + (struct simplefs_file_ei_block *) bh->b_data; + + + if (pos + len > inode->i_size) + len = inode->i_size - pos; + + /* count block position */ + sector_t block_index = pos / SIMPLEFS_BLOCK_SIZE; + sector_t ei_index = block_index / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + sector_t block_offset = ei_block->extents[ei_index].ee_start + + block_index % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + while (len > 0) { + struct buffer_head *bh_data = sb_bread(sb, block_offset); + if (!bh_data) { + pr_err("Failed to read data block %llu\n", block_offset); + bytes_read = -EIO; + break; + } + + size_t offset = pos % SIMPLEFS_BLOCK_SIZE; + size_t bytes_to_read = + min_t(size_t, len, SIMPLEFS_BLOCK_SIZE - pos % SIMPLEFS_BLOCK_SIZE); + if (copy_to_user(buf + bytes_read, bh_data->b_data + offset, + bytes_to_read)) { + brelse(bh_data); + bytes_read = -EFAULT; + break; + } + brelse(bh_data); + + /* successfully read data */ + bytes_read += bytes_to_read; + len -= bytes_to_read; + pos += bytes_to_read; + + /* count extent block */ + block_index++; + ei_index = block_index / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + block_offset = ei_block->extents[ei_index].ee_start + + block_index % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + } + + brelse(bh); + *ppos = pos; + + return bytes_read; +} + +static ssize_t simplefs_write(struct file *file, + const char __user *buf, + size_t len, + loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + ssize_t bytes_write = 0; + loff_t pos = *ppos; + + if (pos > inode->i_size) + return 0; + len = min_t(size_t, len, SIMPLEFS_MAX_FILESIZE - pos); + + /* find extent block */ + struct buffer_head *bh = sb_bread(sb, SIMPLEFS_INODE(inode)->ei_block); + if (!bh) + return -EIO; + struct simplefs_file_ei_block *ei_block = + (struct simplefs_file_ei_block *) bh->b_data; + + /* count block position */ + sector_t block_index = pos / SIMPLEFS_BLOCK_SIZE; + sector_t ei_index = block_index / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + /* write data */ + while (len > 0) { + /* check if block is allocated */ + if (ei_block->extents[ei_index].ee_start == 0) { + int bno = get_free_blocks(sb, 8); + if (!bno) { + bytes_write = -ENOSPC; + break; + } + ei_block->extents[ei_index].ee_start = bno; + ei_block->extents[ei_index].ee_len = 8; + ei_block->extents[ei_index].ee_block = + ei_index ? ei_block->extents[ei_index - 1].ee_block + + ei_block->extents[ei_index - 1].ee_len + : 0; + } + + struct buffer_head *bh_data = + sb_bread(sb, ei_block->extents[ei_index].ee_start + + block_index % SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + if (!bh_data) { + pr_err("Failed to read data block %llu\n", + ei_block->extents[ei_index].ee_start + + block_index % SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + bytes_write = -EIO; + break; + } + /* copy data from buffer */ + size_t bytes_to_write = + min_t(size_t, len, SIMPLEFS_BLOCK_SIZE - pos % SIMPLEFS_BLOCK_SIZE); + + if (copy_from_user(bh_data->b_data + pos % SIMPLEFS_BLOCK_SIZE, buf, + bytes_to_write)) { + brelse(bh_data); + bytes_write = -EFAULT; + break; + } + + mark_buffer_dirty(bh_data); + sync_dirty_buffer(bh_data); + brelse(bh_data); + + /* successfully write data */ + len = len - bytes_to_write; + bytes_write += bytes_to_write; + pos += bytes_to_write; + + /* count extent block */ + block_index = pos / SIMPLEFS_BLOCK_SIZE; + ei_index = block_index / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + } + mark_buffer_dirty(bh); + sync_dirty_buffer(bh); + brelse(bh); + + inode->i_size = max(pos, inode->i_size); + inode->i_blocks = DIV_ROUND_UP(inode->i_size, SIMPLEFS_BLOCK_SIZE) + 1; +#if SIMPLEFS_AT_LEAST(6, 7, 0) + struct timespec64 cur_time = current_time(inode); + inode_set_mtime_to_ts(inode, cur_time); + inode_set_ctime_to_ts(inode, cur_time); +#elif SIMPLEFS_AT_LEAST(6, 6, 0) + struct timespec64 cur_time = current_time(inode); + inode->i_mtime = cur_time; + inode_set_ctime_to_ts(inode, cur_time); +#else + inode->i_mtime = inode->i_ctime = current_time(inode); +#endif + mark_inode_dirty(inode); + *ppos = pos; + + return bytes_write; +} + const struct address_space_operations simplefs_aops = { #if SIMPLEFS_AT_LEAST(5, 19, 0) .readahead = simplefs_readahead, @@ -307,8 +471,8 @@ const struct address_space_operations simplefs_aops = { const struct file_operations simplefs_file_ops = { .llseek = generic_file_llseek, .owner = THIS_MODULE, - .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, .fsync = generic_file_fsync, - .open = simplefs_file_open, + .open = simplefs_open, + .read = simplefs_read, + .write = simplefs_write, }; diff --git a/simplefs.h b/simplefs.h index 8e7c543..5c384bd 100644 --- a/simplefs.h +++ b/simplefs.h @@ -10,6 +10,8 @@ #define SIMPLEFS_MAX_EXTENTS \ ((SIMPLEFS_BLOCK_SIZE - sizeof(uint32_t)) / sizeof(struct simplefs_extent)) #define SIMPLEFS_MAX_BLOCKS_PER_EXTENT 8 /* It can be ~(uint32) 0 */ +#define SIMPLEFS_MAX_SIZES_PER_EXTENT \ + (SIMPLEFS_MAX_BLOCKS_PER_EXTENT * SIMPLEFS_BLOCK_SIZE) #define SIMPLEFS_MAX_FILESIZE \ ((uint64_t) SIMPLEFS_MAX_BLOCKS_PER_EXTENT * SIMPLEFS_BLOCK_SIZE * \ SIMPLEFS_MAX_EXTENTS)