From d969bb7359834e9409ee242691c1630f85dcce08 Mon Sep 17 00:00:00 2001 From: roy Date: Tue, 11 Nov 2025 04:54:58 +0800 Subject: [PATCH 1/3] Optimize the file iteration Each `simplefs_extent` structure contains a counter that records the total number of files within that extent. When the counter matches the expected file number, it indicates there are no more files after this index, allowing the iterator to skip directly to the next extension block. This reduces unnecessary scanning and improves traversal efficiency. --- dir.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dir.c b/dir.c index e96484f..a326d98 100644 --- a/dir.c +++ b/dir.c @@ -62,9 +62,10 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx) for (; remained_nr_files && ei < SIMPLEFS_MAX_EXTENTS; ei++) { if (eblock->extents[ei].ee_start == 0) continue; - + int ei_nr = eblock->extents[ei].nr_files; /* Iterate over blocks in one extent */ - for (bi = 0; bi < eblock->extents[ei].ee_len && remained_nr_files; + for (bi = 0; + bi < eblock->extents[ei].ee_len && remained_nr_files && ei_nr; bi++) { bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); if (!bh2) { @@ -86,6 +87,7 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx) offset--; } else { remained_nr_files--; + ei_nr--; if (!dir_emit(ctx, dblock->files[fi].filename, SIMPLEFS_FILENAME_LEN, dblock->files[fi].inode, DT_UNKNOWN)) { From d190d3102a70568a9ed7c5b74a7d27623d7c6865 Mon Sep 17 00:00:00 2001 From: roy Date: Tue, 11 Nov 2025 13:57:34 +0800 Subject: [PATCH 2/3] Rename variables for better readability --- inode.c | 116 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/inode.c b/inode.c index 4ba8b73..214e33b 100644 --- a/inode.c +++ b/inode.c @@ -741,31 +741,31 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) #if SIMPLEFS_AT_LEAST(6, 3, 0) static int simplefs_rename(struct mnt_idmap *id, - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, + struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #elif SIMPLEFS_AT_LEAST(5, 12, 0) static int simplefs_rename(struct user_namespace *ns, - struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, + struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #else -static int simplefs_rename(struct inode *old_dir, - struct dentry *old_dentry, - struct inode *new_dir, - struct dentry *new_dentry, +static int simplefs_rename(struct inode *src_dir, + struct dentry *src_dentry, + struct inode *dest_dir, + struct dentry *dest_dentry, unsigned int flags) #endif { - struct super_block *sb = old_dir->i_sb; - struct simplefs_inode_info *ci_new = SIMPLEFS_INODE(new_dir); - struct inode *src = d_inode(old_dentry); + struct super_block *sb = src_dir->i_sb; + struct simplefs_inode_info *ci_dest = SIMPLEFS_INODE(dest_dir); + struct inode *src = d_inode(src_dentry); struct buffer_head *bh_new = NULL, *bh2 = NULL; - struct simplefs_file_ei_block *eblock_new = NULL; + struct simplefs_file_ei_block *eblk_dest = NULL; struct simplefs_dir_block *dblock = NULL; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) @@ -780,21 +780,21 @@ static int simplefs_rename(struct inode *old_dir, return -EINVAL; /* Check if filename is not too long */ - if (strlen(new_dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) + if (strlen(dest_dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) return -ENAMETOOLONG; - /* Fail if new_dentry exists or if new_dir is full */ - bh_new = sb_bread(sb, ci_new->ei_block); + /* Fail if dest_dentry exists or if dest_dir is full */ + bh_new = sb_bread(sb, ci_dest->ei_block); if (!bh_new) return -EIO; - eblock_new = (struct simplefs_file_ei_block *) bh_new->b_data; + eblk_dest = (struct simplefs_file_ei_block *) bh_new->b_data; for (ei = 0; new_pos < 0 && ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!eblock_new->extents[ei].ee_start) + if (!eblk_dest->extents[ei].ee_start) break; - for (bi = 0; new_pos < 0 && bi < eblock_new->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, eblock_new->extents[ei].ee_start + bi); + for (bi = 0; new_pos < 0 && bi < eblk_dest->extents[ei].ee_len; bi++) { + bh2 = sb_bread(sb, eblk_dest->extents[ei].ee_start + bi); if (!bh2) { ret = -EIO; goto release_new; @@ -804,13 +804,13 @@ static int simplefs_rename(struct inode *old_dir, int blk_nr_files = dblock->nr_files; for (fi = 0; blk_nr_files;) { /* src and target are the same dir (inode is same) */ - if (new_dir == old_dir) { + if (dest_dir == src_dir) { if (dblock->files[fi].inode && !strncmp(dblock->files[fi].filename, - old_dentry->d_name.name, + src_dentry->d_name.name, SIMPLEFS_FILENAME_LEN)) { strncpy(dblock->files[fi].filename, - new_dentry->d_name.name, SIMPLEFS_FILENAME_LEN); + dest_dentry->d_name.name, SIMPLEFS_FILENAME_LEN); mark_buffer_dirty(bh2); brelse(bh2); goto release_new; @@ -820,7 +820,7 @@ static int simplefs_rename(struct inode *old_dir, same name in the target directory */ if (dblock->files[fi].inode && !strncmp(dblock->files[fi].filename, - new_dentry->d_name.name, + dest_dentry->d_name.name, SIMPLEFS_FILENAME_LEN)) { brelse(bh2); ret = -EEXIST; @@ -840,7 +840,7 @@ static int simplefs_rename(struct inode *old_dir, } /* If new directory is full, fail */ - if (new_pos < 0 && eblock_new->nr_files == SIMPLEFS_FILES_PER_EXT) { + if (new_pos < 0 && eblk_dest->nr_files == SIMPLEFS_FILES_PER_EXT) { ret = -EMLINK; goto release_new; } @@ -853,13 +853,13 @@ static int simplefs_rename(struct inode *old_dir, ret = -ENOSPC; goto release_new; } - eblock_new->extents[ei].ee_start = bno; - eblock_new->extents[ei].ee_len = SIMPLEFS_MAX_BLOCKS_PER_EXTENT; - eblock_new->extents[ei].ee_block = - ei ? eblock_new->extents[ei - 1].ee_block + - eblock_new->extents[ei - 1].ee_len + eblk_dest->extents[ei].ee_start = bno; + eblk_dest->extents[ei].ee_len = SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + eblk_dest->extents[ei].ee_block = + ei ? eblk_dest->extents[ei - 1].ee_block + + eblk_dest->extents[ei - 1].ee_len : 0; - bh2 = sb_bread(sb, eblock_new->extents[ei].ee_start + 0); + bh2 = sb_bread(sb, eblk_dest->extents[ei].ee_start + 0); if (!bh2) { ret = -EIO; goto put_block; @@ -869,55 +869,55 @@ static int simplefs_rename(struct inode *old_dir, new_pos = 0; } dblock->files[new_pos].inode = src->i_ino; - strncpy(dblock->files[new_pos].filename, new_dentry->d_name.name, + strncpy(dblock->files[new_pos].filename, dest_dentry->d_name.name, SIMPLEFS_FILENAME_LEN); mark_buffer_dirty(bh2); brelse(bh2); /* Update new parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) - simple_inode_init_ts(new_dir); + simple_inode_init_ts(dest_dir); #elif SIMPLEFS_AT_LEAST(6, 6, 0) - cur_time = current_time(new_dir); - new_dir->i_atime = new_dir->i_mtime = cur_time; - inode_set_ctime_to_ts(new_dir, cur_time); + cur_time = current_time(dest_dir); + dest_dir->i_atime = dest_dir->i_mtime = cur_time; + inode_set_ctime_to_ts(dest_dir, cur_time); #else - new_dir->i_atime = new_dir->i_ctime = new_dir->i_mtime = - current_time(new_dir); + dest_dir->i_atime = dest_dir->i_ctime = dest_dir->i_mtime = + current_time(dest_dir); #endif if (S_ISDIR(src->i_mode)) - inc_nlink(new_dir); - mark_inode_dirty(new_dir); + inc_nlink(dest_dir); + mark_inode_dirty(dest_dir); /* remove target from old parent directory */ - ret = simplefs_remove_from_dir(old_dir, old_dentry); + ret = simplefs_remove_from_dir(src_dir, src_dentry); if (ret != 0) goto release_new; /* Update old parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) - simple_inode_init_ts(old_dir); + simple_inode_init_ts(src_dir); #elif SIMPLEFS_AT_LEAST(6, 6, 0) - cur_time = current_time(old_dir); - old_dir->i_atime = old_dir->i_mtime = cur_time; - inode_set_ctime_to_ts(old_dir, cur_time); + cur_time = current_time(src_dir); + src_dir->i_atime = src_dir->i_mtime = cur_time; + inode_set_ctime_to_ts(src_dir, cur_time); #else - old_dir->i_atime = old_dir->i_ctime = old_dir->i_mtime = - current_time(old_dir); + src_dir->i_atime = src_dir->i_ctime = src_dir->i_mtime = + current_time(src_dir); #endif if (S_ISDIR(src->i_mode)) - drop_nlink(old_dir); - mark_inode_dirty(old_dir); + drop_nlink(src_dir); + mark_inode_dirty(src_dir); return ret; put_block: - if (eblock_new->extents[ei].ee_start) { - put_blocks(SIMPLEFS_SB(sb), eblock_new->extents[ei].ee_start, - eblock_new->extents[ei].ee_len); - memset(&eblock_new->extents[ei], 0, sizeof(struct simplefs_extent)); + if (eblk_dest->extents[ei].ee_start) { + put_blocks(SIMPLEFS_SB(sb), eblk_dest->extents[ei].ee_start, + eblk_dest->extents[ei].ee_len); + memset(&eblk_dest->extents[ei], 0, sizeof(struct simplefs_extent)); } release_new: brelse(bh_new); @@ -975,11 +975,11 @@ static int simplefs_rmdir(struct inode *dir, struct dentry *dentry) return simplefs_unlink(dir, dentry); } -static int simplefs_link(struct dentry *old_dentry, +static int simplefs_link(struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) { - struct inode *old_inode = d_inode(old_dentry); + struct inode *old_inode = d_inode(src_dentry); struct super_block *sb = old_inode->i_sb; struct simplefs_inode_info *ci_dir = SIMPLEFS_INODE(dir); struct simplefs_file_ei_block *eblock = NULL; From d02e46a09a6c4cdc8ab06f681b774a787f28b58b Mon Sep 17 00:00:00 2001 From: roy Date: Sun, 16 Nov 2025 11:06:15 +0800 Subject: [PATCH 3/3] Use hash func to boost file creation and lookup Introduce a hash-based mechanism to speed up file creation and lookup operations. The hash function enables faster access to extent and logical block extent index, improving overall filesystem performance. hash_code = file_hash(file_name); extent index = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT block index = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; Use perf to measure: 1. File Creation (random) Legacy: 259.842753513 seconds time elapsed 23.000247000 seconds user 150.380145000 seconds sys full_name_hash: 222.274028604 seconds time elapsed 20.794966000 seconds user 151.941876000 seconds sys 2. File Listing (random) Legacy: min time: 0.00171 s max time: 0.03799 s avg time: 0.00423332 s tot time: 129.539510 s full_name_hash: min time: 0.00171 s max time: 0.03588 s avg time: 0.00305601 s tot time: 93.514040 s 3. files Removal (Random) Legacy: 106.921706288 seconds time elapsed 16.987883000 seconds user 91.268661000 seconds sys full_name_hash: 86.132655220 seconds time elapsed 19.180209000 seconds user 68.476075000 seconds sys --- Makefile | 2 +- dir.c | 2 +- hash.c | 9 ++ inode.c | 456 ++++++++++++++++++++++++++++++++--------------------- simplefs.h | 3 + 5 files changed, 291 insertions(+), 181 deletions(-) create mode 100644 hash.c diff --git a/Makefile b/Makefile index 8d10f24..067be34 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ obj-m += simplefs.o -simplefs-objs := fs.o super.o inode.o file.o dir.o extent.o +simplefs-objs := fs.o super.o inode.o file.o dir.o extent.o hash.o KDIR ?= /lib/modules/$(shell uname -r)/build diff --git a/dir.c b/dir.c index a326d98..95fb7ba 100644 --- a/dir.c +++ b/dir.c @@ -81,7 +81,7 @@ static int simplefs_iterate(struct file *dir, struct dir_context *ctx) continue; } - for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK;) { + for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK && dblock->nr_files;) { if (dblock->files[fi].inode != 0) { if (offset) { offset--; diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..e19a15e --- /dev/null +++ b/hash.c @@ -0,0 +1,9 @@ +#include +#include +#include "simplefs.h" + +uint32_t simplefs_hash(struct dentry *dentry) +{ + return full_name_hash(dentry, dentry->d_name.name, + strlen(dentry->d_name.name)); +} diff --git a/inode.c b/inode.c index 214e33b..3bcd57a 100644 --- a/inode.c +++ b/inode.c @@ -110,6 +110,77 @@ struct inode *simplefs_iget(struct super_block *sb, unsigned long ino) return ERR_PTR(ret); } +static int __file_lookup(struct inode *dir, + struct dentry *dentry, + int *ei, + int *bi, + int *fi) +{ + struct super_block *sb = dir->i_sb; + struct simplefs_inode_info *ci_dir = SIMPLEFS_INODE(dir); + struct buffer_head *bh = NULL, *bh2 = NULL; + struct simplefs_file_ei_block *eblock = NULL; + struct simplefs_dir_block *dblock = NULL; + struct simplefs_file *f = NULL; + int _ei, _bi, _fi, ret = 0; + int idx_ei, idx_bi; + uint32_t hash_code; + /* Check filename length */ + + /* Read the directory block on disk */ + bh = sb_bread(sb, ci_dir->ei_block); + if (!bh) + return -EIO; + eblock = (struct simplefs_file_ei_block *) bh->b_data; + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + _ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + _bi = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (idx_ei = 0; idx_ei < SIMPLEFS_MAX_EXTENTS; _ei++, idx_ei++) { + if (unlikely(_ei >= SIMPLEFS_MAX_EXTENTS)) + _ei %= SIMPLEFS_MAX_EXTENTS; + + if (!eblock->extents[_ei].ee_start) + continue; + + /* Iterate blocks in extent */ + for (idx_bi = 0; idx_bi < eblock->extents[_ei].ee_len; + _bi++, idx_bi++) { + if (unlikely(_bi >= eblock->extents[_ei].ee_len)) + _bi %= eblock->extents[_ei].ee_len; + + bh2 = sb_bread(sb, eblock->extents[_ei].ee_start + _bi); + if (!bh2) { + ret = -EIO; + goto file_search_end; + } + + + dblock = (struct simplefs_dir_block *) bh2->b_data; + /* Search file in ei_block */ + for (_fi = 0; _fi < dblock->nr_files;) { + f = &dblock->files[_fi]; + if (f->inode && !strncmp(f->filename, dentry->d_name.name, + SIMPLEFS_FILENAME_LEN)) { + *ei = _ei; + *bi = _bi; + *fi = _fi; + + brelse(bh); + brelse(bh2); + return 1; + } + _fi += dblock->files[_fi].nr_blk; + } + brelse(bh2); + } + } +file_search_end: + brelse(bh); + return ret; +} + /* Search for a dentry in dir. * Fills dentry with NULL if not found in dir, or with the corresponding inode * if found. @@ -126,51 +197,32 @@ static struct dentry *simplefs_lookup(struct inode *dir, struct buffer_head *bh = NULL, *bh2 = NULL; struct simplefs_file_ei_block *eblock = NULL; struct simplefs_dir_block *dblock = NULL; - struct simplefs_file *f = NULL; - int ei, bi, fi; + int ei, bi, fi, chk; /* Check filename length */ if (dentry->d_name.len > SIMPLEFS_FILENAME_LEN) return ERR_PTR(-ENAMETOOLONG); /* Read the directory block on disk */ + + chk = __file_lookup(dir, dentry, &ei, &bi, &fi); + if (chk != 1) { + if (!chk) + return ERR_PTR(-chk); + goto search_end; + } + bh = sb_bread(sb, ci_dir->ei_block); if (!bh) return ERR_PTR(-EIO); - eblock = (struct simplefs_file_ei_block *) bh->b_data; - - /* Search for the file in directory */ - for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!eblock->extents[ei].ee_start) - break; - /* Iterate blocks in extent */ - for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); - if (!bh2) - return ERR_PTR(-EIO); + eblock = (struct simplefs_file_ei_block *) bh->b_data; + bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); + if (!bh2) + return ERR_PTR(-EIO); - dblock = (struct simplefs_dir_block *) bh2->b_data; - - /* Search file in ei_block */ - for (fi = 0; fi < dblock->nr_files;) { - f = &dblock->files[fi]; - if (!f->inode) { - brelse(bh2); - goto search_end; - } - if (!strncmp(f->filename, dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - inode = simplefs_iget(sb, f->inode); - brelse(bh2); - goto search_end; - } - fi += dblock->files[fi].nr_blk; - } - brelse(bh2); - bh2 = NULL; - } - } + dblock = (struct simplefs_dir_block *) bh2->b_data; + inode = simplefs_iget(sb, (&dblock->files[fi])->inode); search_end: brelse(bh); @@ -314,26 +366,26 @@ static struct inode *simplefs_new_inode(struct inode *dir, mode_t mode) } static uint32_t simplefs_get_available_ext_idx( - int *dir_nr_files, - struct simplefs_file_ei_block *eblock) + struct simplefs_file_ei_block *eblock, + uint32_t hash_code) { - int ei = 0; + int ei = 0, idx; uint32_t first_empty_blk = -1; - for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (eblock->extents[ei].ee_start && - eblock->extents[ei].nr_files != SIMPLEFS_FILES_PER_EXT) { + + ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (idx = 0; idx < SIMPLEFS_MAX_EXTENTS; ei++, idx++) { + if (unlikely(ei >= SIMPLEFS_MAX_EXTENTS)) + ei %= SIMPLEFS_MAX_EXTENTS; + + if (!eblock->extents[ei].ee_start) { first_empty_blk = ei; break; - } else if (!eblock->extents[ei].ee_start) { - if (first_empty_blk == -1) - first_empty_blk = ei; - } else { - *dir_nr_files -= eblock->extents[ei].nr_files; - if (first_empty_blk == -1 && !*dir_nr_files) - first_empty_blk = ei + 1; - } - if (!*dir_nr_files) + } else if (eblock->extents[ei].ee_start && + eblock->extents[ei].nr_files != SIMPLEFS_FILES_PER_EXT) { + first_empty_blk = ei; break; + } } return first_empty_blk; } @@ -427,12 +479,12 @@ static int simplefs_create(struct inode *dir, struct simplefs_dir_block *dblock; char *fblock; struct buffer_head *bh, *bh2; - uint32_t dir_nr_files = 0, avail; + uint32_t avail; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; #endif - int ret = 0, alloc = false; - int bi = 0; + int ret = 0, alloc = false, hash_code; + int bi = 0, idx_bi; /* Check filename length */ if (strlen(dentry->d_name.name) > SIMPLEFS_FILENAME_LEN) @@ -472,11 +524,11 @@ static int simplefs_create(struct inode *dir, mark_buffer_dirty(bh2); brelse(bh2); - dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); - + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -491,7 +543,11 @@ static int simplefs_create(struct inode *dir, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; @@ -551,44 +607,60 @@ static int simplefs_create(struct inode *dir, return ret; } -static int simplefs_remove_from_dir(struct inode *dir, struct dentry *dentry) +static int simplefs_remove_from_dir(struct inode *dir, + struct dentry *dentry, + int *ret_ei) { struct super_block *sb = dir->i_sb; struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL, *bh2 = NULL; struct simplefs_file_ei_block *eblock = NULL; struct simplefs_dir_block *dirblk = NULL; - int ei = 0, bi = 0, fi = 0; - int ret = 0, found = false; - + int ei = 0, bi = 0, fi = 0, idx_bi; + int ret = 0, found = false, i, blk_nr_files, dir_nr_files; + uint32_t hash_code; /* Read parent directory index */ bh = sb_bread(sb, SIMPLEFS_INODE(dir)->ei_block); if (!bh) return -EIO; eblock = (struct simplefs_file_ei_block *) bh->b_data; + dir_nr_files = eblock->nr_files; + + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + ei = hash_code / SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + bi = hash_code % SIMPLEFS_MAX_BLOCKS_PER_EXTENT; + + for (; dir_nr_files; ei++) { + if (unlikely(ei >= SIMPLEFS_MAX_EXTENTS)) + ei %= SIMPLEFS_MAX_EXTENTS; - int dir_nr_files = eblock->nr_files; - for (ei = 0; dir_nr_files; ei++) { if (eblock->extents[ei].ee_start) { dir_nr_files -= eblock->extents[ei].nr_files; - for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) { + /* simplefs_extent */ + for (idx_bi = 0; idx_bi < eblock->extents[ei].ee_len; + bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[ei].ee_len)) + bi %= eblock->extents[ei].ee_len; + bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi); if (!bh2) { ret = -EIO; goto release_bh; } + /* simplefs_dir_block */ dirblk = (struct simplefs_dir_block *) bh2->b_data; - int blk_nr_files = dirblk->nr_files; + blk_nr_files = dirblk->nr_files; for (fi = 0; blk_nr_files && fi < SIMPLEFS_FILES_PER_BLOCK;) { if (dirblk->files[fi].inode) { if (dirblk->files[fi].inode == inode->i_ino && !strcmp(dirblk->files[fi].filename, dentry->d_name.name)) { - found = true; dirblk->files[fi].inode = 0; /* merge the empty data */ - for (int i = fi - 1; i >= 0; i--) { + for (i = fi - 1; i >= 0; i--) { if (dirblk->files[i].inode != 0 || i == 0) { dirblk->files[i].nr_blk += dirblk->files[fi].nr_blk; @@ -601,6 +673,7 @@ static int simplefs_remove_from_dir(struct inode *dir, struct dentry *dentry) mark_buffer_dirty(bh2); brelse(bh2); found = true; + *ret_ei = ei; goto found_data; } blk_nr_files--; @@ -633,7 +706,7 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL, *bh2 = NULL; - struct simplefs_file_ei_block *file_block = NULL; + struct simplefs_file_ei_block *eblk = NULL; char *block; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; @@ -644,9 +717,20 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) uint32_t ino = inode->i_ino; uint32_t bno = 0; - ret = simplefs_remove_from_dir(dir, dentry); + ret = simplefs_remove_from_dir(dir, dentry, &ei); + if (ret != 0) return ret; + /* if extent[i] file number is 0, reclaim the extent[i] block*/ + bh = sb_bread(sb, SIMPLEFS_INODE(dir)->ei_block); + if (!bh) + return -EIO; + + eblk = (struct simplefs_file_ei_block *) bh->b_data; + if (!eblk->extents[ei].nr_files) { + put_blocks(sbi, eblk->extents[ei].ee_start, eblk->extents[ei].ee_len); + memset(&eblk->extents[ei], 0, sizeof(struct simplefs_extent)); + } if (S_ISLNK(inode->i_mode)) goto clean_inode; @@ -683,18 +767,16 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) bh = sb_bread(sb, bno); if (!bh) goto clean_inode; - file_block = (struct simplefs_file_ei_block *) bh->b_data; - + eblk = (struct simplefs_file_ei_block *) bh->b_data; for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!file_block->extents[ei].ee_start) + if (!eblk->extents[ei].ee_start) break; - put_blocks(sbi, file_block->extents[ei].ee_start, - file_block->extents[ei].ee_len); + put_blocks(sbi, eblk->extents[ei].ee_start, eblk->extents[ei].ee_len); /* Scrub the extent */ - for (bi = 0; bi < file_block->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, file_block->extents[ei].ee_start + bi); + for (bi = 0; bi < eblk->extents[ei].ee_len; bi++) { + bh2 = sb_bread(sb, eblk->extents[ei].ee_start + bi); if (!bh2) continue; block = (char *) bh2->b_data; @@ -705,7 +787,7 @@ static int simplefs_unlink(struct inode *dir, struct dentry *dentry) } /* Scrub index block */ - memset(file_block, 0, SIMPLEFS_BLOCK_SIZE); + memset(eblk, 0, SIMPLEFS_BLOCK_SIZE); mark_buffer_dirty(bh); brelse(bh); @@ -762,18 +844,21 @@ static int simplefs_rename(struct inode *src_dir, #endif { struct super_block *sb = src_dir->i_sb; + struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); + struct simplefs_inode_info *ci_src = SIMPLEFS_INODE(src_dir); struct simplefs_inode_info *ci_dest = SIMPLEFS_INODE(dest_dir); - struct inode *src = d_inode(src_dentry); - struct buffer_head *bh_new = NULL, *bh2 = NULL; - struct simplefs_file_ei_block *eblk_dest = NULL; + struct inode *src_in = d_inode(src_dentry); + struct buffer_head *bh_fei_blk_src = NULL, *bh_fei_blk_dest = NULL, + *bh_ext = NULL; + struct simplefs_file_ei_block *eblk_src = NULL, *eblk_dest = NULL; struct simplefs_dir_block *dblock = NULL; #if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0) struct timespec64 cur_time; #endif - int new_pos = -1, ret = 0; - int ei = 0, bi = 0, fi = 0, bno = 0; + int ret = 0, new_pos = 0, idx, chk; + int src_ei = 0, dest_ei = 0, bi = 0, fi = 0; /* fail with these unsupported flags */ if (flags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) @@ -784,95 +869,94 @@ static int simplefs_rename(struct inode *src_dir, return -ENAMETOOLONG; /* Fail if dest_dentry exists or if dest_dir is full */ - bh_new = sb_bread(sb, ci_dest->ei_block); - if (!bh_new) + bh_fei_blk_dest = sb_bread(sb, ci_dest->ei_block); + if (!bh_fei_blk_dest) return -EIO; - eblk_dest = (struct simplefs_file_ei_block *) bh_new->b_data; - for (ei = 0; new_pos < 0 && ei < SIMPLEFS_MAX_EXTENTS; ei++) { - if (!eblk_dest->extents[ei].ee_start) - break; - - for (bi = 0; new_pos < 0 && bi < eblk_dest->extents[ei].ee_len; bi++) { - bh2 = sb_bread(sb, eblk_dest->extents[ei].ee_start + bi); - if (!bh2) { - ret = -EIO; - goto release_new; - } - - dblock = (struct simplefs_dir_block *) bh2->b_data; - int blk_nr_files = dblock->nr_files; - for (fi = 0; blk_nr_files;) { - /* src and target are the same dir (inode is same) */ - if (dest_dir == src_dir) { - if (dblock->files[fi].inode && - !strncmp(dblock->files[fi].filename, - src_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - strncpy(dblock->files[fi].filename, - dest_dentry->d_name.name, SIMPLEFS_FILENAME_LEN); - mark_buffer_dirty(bh2); - brelse(bh2); - goto release_new; - } - } else { - /* src and target are different, then check if the - same name in the target directory */ - if (dblock->files[fi].inode && - !strncmp(dblock->files[fi].filename, - dest_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN)) { - brelse(bh2); - ret = -EEXIST; - goto release_new; - } - /* find the empty index in target directory */ - if (new_pos < 0 && dblock->files[fi].nr_blk != 1) { - new_pos = fi + 1; - break; - } - } - blk_nr_files--; - fi += dblock->files[fi].nr_blk; - } - brelse(bh2); + eblk_dest = (struct simplefs_file_ei_block *) bh_fei_blk_dest->b_data; + /* Search for the file in directory */ + chk = __file_lookup(dest_dir, dest_dentry, &dest_ei, &bi, &fi); + if (chk != 0) { + if (chk == 1) { + ret = -EEXIST; + } else if (chk < 0) { + ret = chk; } + goto release_new; } + /* If new directory is full, fail */ - if (new_pos < 0 && eblk_dest->nr_files == SIMPLEFS_FILES_PER_EXT) { + if (eblk_dest->nr_files == SIMPLEFS_MAX_SUBFILES) { ret = -EMLINK; goto release_new; } /* insert in new parent directory */ /* Get new freeblocks for extent if needed*/ - if (new_pos < 0) { - bno = get_free_blocks(sb, SIMPLEFS_MAX_BLOCKS_PER_EXTENT); - if (!bno) { + if (!eblk_dest->extents[dest_ei].ee_start) { + ret = simplefs_put_new_ext(sb, dest_ei, eblk_dest); + switch (ret) { + case -ENOSPC: ret = -ENOSPC; goto release_new; + case -EIO: + ret = -EIO; + goto release_new; } - eblk_dest->extents[ei].ee_start = bno; - eblk_dest->extents[ei].ee_len = SIMPLEFS_MAX_BLOCKS_PER_EXTENT; - eblk_dest->extents[ei].ee_block = - ei ? eblk_dest->extents[ei - 1].ee_block + - eblk_dest->extents[ei - 1].ee_len - : 0; - bh2 = sb_bread(sb, eblk_dest->extents[ei].ee_start + 0); - if (!bh2) { + mark_buffer_dirty(bh_fei_blk_dest); + new_pos = 1; + } else { + for (idx = 0; idx < SIMPLEFS_MAX_EXTENTS; dest_ei++, idx++) { + if (unlikely(dest_ei >= SIMPLEFS_MAX_EXTENTS)) + dest_ei %= SIMPLEFS_MAX_EXTENTS; + + if (eblk_dest->extents[dest_ei].nr_files != SIMPLEFS_FILES_PER_EXT) + break; + } + } + /* copy src info into new directory*/ + for (bi = 0; bi < eblk_dest->extents[dest_ei].ee_len; bi++) { + bh_ext = sb_bread(sb, eblk_dest->extents[dest_ei].ee_start + bi); + if (!bh_ext) { ret = -EIO; goto put_block; } - dblock = (struct simplefs_dir_block *) bh2->b_data; - mark_buffer_dirty(bh_new); - new_pos = 0; + dblock = (struct simplefs_dir_block *) bh_ext->b_data; + + /* check if dir block is full*/ + if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) { + simplefs_set_file_into_dir(dblock, src_in->i_ino, + dest_dentry->d_name.name); + eblk_dest->extents[dest_ei].nr_files++; + eblk_dest->nr_files++; + mark_buffer_dirty(bh_ext); + mark_buffer_dirty(bh_fei_blk_dest); + brelse(bh_ext); + break; + } + brelse(bh_ext); + } + + /* remove target from old parent directory */ + ret = simplefs_remove_from_dir(src_dir, src_dentry, &src_ei); + if (ret != 0) + goto rm_new; + + /* if extent[i] file number is 0, reclaim the extent[i] block*/ + bh_fei_blk_src = sb_bread(sb, ci_src->ei_block); + if (!bh_fei_blk_src) { + ret = -EIO; + goto rm_new; + } + + eblk_src = (struct simplefs_file_ei_block *) bh_fei_blk_src->b_data; + if (!eblk_src->extents[src_ei].nr_files) { + put_blocks(sbi, eblk_src->extents[src_ei].ee_start, + eblk_src->extents[src_ei].ee_len); + memset(&eblk_src->extents[src_ei], 0, sizeof(struct simplefs_extent)); + mark_buffer_dirty(bh_fei_blk_src); } - dblock->files[new_pos].inode = src->i_ino; - strncpy(dblock->files[new_pos].filename, dest_dentry->d_name.name, - SIMPLEFS_FILENAME_LEN); - mark_buffer_dirty(bh2); - brelse(bh2); /* Update new parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) @@ -886,16 +970,11 @@ static int simplefs_rename(struct inode *src_dir, current_time(dest_dir); #endif - if (S_ISDIR(src->i_mode)) + if (S_ISDIR(src_in->i_mode)) inc_nlink(dest_dir); mark_inode_dirty(dest_dir); - /* remove target from old parent directory */ - ret = simplefs_remove_from_dir(src_dir, src_dentry); - if (ret != 0) - goto release_new; - - /* Update old parent inode metadata */ + /* Update old parent inode metadata */ #if SIMPLEFS_AT_LEAST(6, 7, 0) simple_inode_init_ts(src_dir); #elif SIMPLEFS_AT_LEAST(6, 6, 0) @@ -907,20 +986,24 @@ static int simplefs_rename(struct inode *src_dir, current_time(src_dir); #endif - if (S_ISDIR(src->i_mode)) + if (S_ISDIR(src_in->i_mode)) drop_nlink(src_dir); mark_inode_dirty(src_dir); - return ret; + goto release_new; + +rm_new: + simplefs_remove_from_dir(dest_dir, dest_dentry, &dest_ei); put_block: - if (eblk_dest->extents[ei].ee_start) { - put_blocks(SIMPLEFS_SB(sb), eblk_dest->extents[ei].ee_start, - eblk_dest->extents[ei].ee_len); - memset(&eblk_dest->extents[ei], 0, sizeof(struct simplefs_extent)); + if (eblk_dest->extents[dest_ei].ee_start && new_pos) { + put_blocks(SIMPLEFS_SB(sb), eblk_dest->extents[dest_ei].ee_start, + eblk_dest->extents[dest_ei].ee_len); + memset(&eblk_dest->extents[dest_ei], 0, sizeof(struct simplefs_extent)); } + release_new: - brelse(bh_new); + brelse(bh_fei_blk_dest); return ret; } @@ -986,8 +1069,8 @@ static int simplefs_link(struct dentry *src_dentry, struct simplefs_dir_block *dblock; struct buffer_head *bh = NULL, *bh2 = NULL; int ret = 0, alloc = false; - int ei = 0, bi = 0; - uint32_t avail; + int ei = 0, bi = 0, idx_bi; + uint32_t avail, hash_code; bh = sb_bread(sb, ci_dir->ei_block); if (!bh) @@ -1000,11 +1083,13 @@ static int simplefs_link(struct dentry *src_dentry, goto end; } - int dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -1019,12 +1104,17 @@ static int simplefs_link(struct dentry *src_dentry, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; goto put_block; } + dblock = (struct simplefs_dir_block *) bh2->b_data; if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) break; @@ -1034,7 +1124,7 @@ static int simplefs_link(struct dentry *src_dentry, /* write the file info into simplefs_dir_block */ simplefs_set_file_into_dir(dblock, old_inode->i_ino, dentry->d_name.name); - + eblock->extents[avail].nr_files++; eblock->nr_files++; mark_buffer_dirty(bh2); mark_buffer_dirty(bh); @@ -1082,8 +1172,8 @@ static int simplefs_symlink(struct inode *dir, struct simplefs_dir_block *dblock = NULL; struct buffer_head *bh = NULL, *bh2 = NULL; int ret = 0, alloc = false; - int ei = 0, bi = 0; - uint32_t avail; + int ei = 0, bi = 0, idx_bi; + uint32_t avail, hash_code; /* Check if symlink content is not too long */ if (l > sizeof(ci->i_data)) @@ -1101,11 +1191,13 @@ static int simplefs_symlink(struct inode *dir, goto end; } - int dir_nr_files = eblock->nr_files; - avail = simplefs_get_available_ext_idx(&dir_nr_files, eblock); + + hash_code = simplefs_hash(dentry) % + (SIMPLEFS_MAX_EXTENTS * SIMPLEFS_MAX_BLOCKS_PER_EXTENT); + avail = simplefs_get_available_ext_idx(eblock, hash_code); /* if there is not any empty space, alloc new one */ - if (!dir_nr_files && !eblock->extents[avail].ee_start) { + if (!eblock->extents[avail].ee_start) { ret = simplefs_put_new_ext(sb, avail, eblock); switch (ret) { case -ENOSPC: @@ -1120,12 +1212,17 @@ static int simplefs_symlink(struct inode *dir, /* TODO: fix from 8 to dynamic value */ /* Find which simplefs_dir_block has free space */ - for (bi = 0; bi < eblock->extents[avail].ee_len; bi++) { + bi = hash_code % eblock->extents[avail].ee_len; + for (idx_bi = 0; idx_bi < eblock->extents[avail].ee_len; bi++, idx_bi++) { + if (unlikely(bi >= eblock->extents[avail].ee_len)) + bi %= eblock->extents[avail].ee_len; + bh2 = sb_bread(sb, eblock->extents[avail].ee_start + bi); if (!bh2) { ret = -EIO; goto put_block; } + dblock = (struct simplefs_dir_block *) bh2->b_data; if (dblock->nr_files != SIMPLEFS_FILES_PER_BLOCK) break; @@ -1136,6 +1233,7 @@ static int simplefs_symlink(struct inode *dir, /* write the file info into simplefs_dir_block */ simplefs_set_file_into_dir(dblock, inode->i_ino, dentry->d_name.name); + eblock->extents[avail].nr_files++; eblock->nr_files++; mark_buffer_dirty(bh2); mark_buffer_dirty(bh); diff --git a/simplefs.h b/simplefs.h index f99353f..66f9d88 100644 --- a/simplefs.h +++ b/simplefs.h @@ -115,6 +115,9 @@ struct dentry *simplefs_mount(struct file_system_type *fs_type, const char *dev_name, void *data); +/* hash.c */ +uint32_t simplefs_hash(struct dentry *dentry); + /* file functions */ extern const struct file_operations simplefs_file_ops; extern const struct file_operations simplefs_dir_ops;