diff --git a/Makefile b/Makefile index cff6d56..8d10f24 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ all: $(MKFS) IMAGE ?= test.img IMAGESIZE ?= 200 +JOURNAL ?= journal.img +JOURNALSIZE ?= 8 + # To test max files(40920) in directory, the image size should be at least 159.85 MiB # 40920 * 4096(block size) ~= 159.85 MiB @@ -20,12 +23,18 @@ $(IMAGE): $(MKFS) dd if=/dev/zero of=${IMAGE} bs=1M count=${IMAGESIZE} ./$< $(IMAGE) +journal: $(JOURNAL) + +$(JOURNAL): + dd if=/dev/zero of=$(JOURNAL) bs=1M count=$(JOURNALSIZE) + mke2fs -b 4096 -O journal_dev $(JOURNAL) + check: all script/test.sh $(IMAGE) $(IMAGESIZE) $(MKFS) clean: make -C $(KDIR) M=$(PWD) clean rm -f *~ $(PWD)/*.ur-safe - rm -f $(MKFS) $(IMAGE) + rm -f $(MKFS) $(IMAGE) $(JOURNAL) -.PHONY: all clean +.PHONY: all clean journal diff --git a/file.c b/file.c index 73131e8..668a5d5 100644 --- a/file.c +++ b/file.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,20 @@ static int simplefs_file_get_block(struct inode *inode, bh_index = sb_bread(sb, ci->ei_block); if (!bh_index) return -EIO; + + handle_t *handle = journal_current_handle(); + if (!handle) { + brelse(bh_index); + return -EIO; + } + + ret = jbd2_journal_get_write_access(handle, bh_index); + if (ret) { + brelse(bh_index); + pr_info("Can't get write access for bh\n"); + return ret; + } + index = (struct simplefs_file_ei_block *) bh_index->b_data; extent = simplefs_ext_search(index, iblock); @@ -63,6 +78,13 @@ static int simplefs_file_get_block(struct inode *inode, extent ? index->extents[extent - 1].ee_block + index->extents[extent - 1].ee_len : 0; + ret = jbd2_journal_dirty_metadata(handle, bh_index); + if (ret) { + brelse(bh_index); + return ret; + } + mark_buffer_dirty_inode(bh_index, inode); + } else { bno = index->extents[extent].ee_start + iblock - index->extents[extent].ee_block; @@ -133,6 +155,7 @@ static int simplefs_write_begin(struct file *file, struct simplefs_sb_info *sbi = SIMPLEFS_SB(file->f_inode->i_sb); int err; uint32_t nr_allocs = 0; + handle_t *handle; /* Check if the write can be completed (enough space?) */ if (pos + len > SIMPLEFS_MAX_FILESIZE) @@ -146,6 +169,16 @@ static int simplefs_write_begin(struct file *file, if (nr_allocs > sbi->nr_free_blocks) return -ENOSPC; + /* handle journal starting here */ + /* + * FIXME: the metadata type we should store into journal + * In the current situation, we only record the location of the extent + * and write that metadata to the journal. + */ + handle = jbd2_journal_start(sbi->journal, 1); + if (IS_ERR(handle)) + return PTR_ERR(handle); + /* prepare the write */ #if SIMPLEFS_AT_LEAST(5, 19, 0) err = block_write_begin(mapping, pos, len, pagep, simplefs_file_get_block); @@ -174,6 +207,16 @@ static int simplefs_write_end(struct file *file, struct inode *inode = file->f_inode; struct simplefs_inode_info *ci = SIMPLEFS_INODE(inode); struct super_block *sb = inode->i_sb; + + handle_t *handle; + + /* handle journal start here */ + handle = journal_current_handle(); + if (!handle) { + pr_err("can't get journal handle\n"); + return -EIO; + } + #if SIMPLEFS_AT_LEAST(6, 6, 0) struct timespec64 cur_time; #endif @@ -223,6 +266,14 @@ static int simplefs_write_end(struct file *file, nr_blocks_old - inode->i_blocks); goto end; } + + int retval = jbd2_journal_get_write_access(handle, bh_index); + if (WARN_ON(retval)) { + brelse(bh_index); + pr_info("cant get journal write access\n"); + return retval; + } + index = (struct simplefs_file_ei_block *) bh_index->b_data; first_ext = simplefs_ext_search(index, inode->i_blocks - 1); @@ -238,9 +289,13 @@ static int simplefs_write_end(struct file *file, index->extents[i].ee_len); memset(&index->extents[i], 0, sizeof(struct simplefs_extent)); } + jbd2_journal_dirty_metadata(handle, bh_index); mark_buffer_dirty(bh_index); brelse(bh_index); } + + jbd2_journal_stop(handle); + end: return ret; } diff --git a/simplefs.h b/simplefs.h index 8e7c543..6062ca2 100644 --- a/simplefs.h +++ b/simplefs.h @@ -37,6 +37,9 @@ * | blocks | rest of the blocks * +---------------+ */ +#ifdef __KERNEL__ +#include +#endif struct simplefs_inode { uint32_t i_mode; /* File mode */ @@ -69,6 +72,10 @@ struct simplefs_sb_info { uint32_t nr_free_blocks; /* Number of free blocks */ #ifdef __KERNEL__ + journal_t *journal; + struct block_device *s_journal_bdev; /* v6.5 external journal device */ + struct bdev_handle + *s_journal_bdev_handle; /* v6.8 external journal device */ unsigned long *ifree_bitmap; /* In-memory free inodes bitmap */ unsigned long *bfree_bitmap; /* In-memory free blocks bitmap */ #endif diff --git a/super.c b/super.c index e966ec9..a46d106 100644 --- a/super.c +++ b/super.c @@ -7,6 +7,11 @@ #include #include +#include +#include +#include +#include + #include "simplefs.h" struct dentry *simplefs_mount(struct file_system_type *fs_type, @@ -196,6 +201,320 @@ static int simplefs_statfs(struct dentry *dentry, struct kstatfs *stat) return 0; } +/* journal relation code */ + +#if SIMPLEFS_AT_LEAST(6, 8, 0) +static struct bdev_handle *simplefs_get_journal_blkdev( + struct super_block *sb, + dev_t jdev, + unsigned long long *j_start, + unsigned long long *j_len) +{ + struct buffer_head *bh; + struct bdev_handle *bdev_handle; + struct block_device *bdev; + int hblock, blocksize; + unsigned long long sb_block; + unsigned long offset; + int errno; + + bdev_handle = bdev_open_by_dev( + jdev, BLK_OPEN_READ | BLK_OPEN_WRITE | BLK_OPEN_RESTRICT_WRITES, sb, + &fs_holder_ops); + + if (IS_ERR(bdev_handle)) { + printk(KERN_ERR + "failed to open journal device unknown-block(%u,%u) %ld\n", + MAJOR(jdev), MINOR(jdev), PTR_ERR(bdev_handle)); + return bdev_handle; + } + + bdev = bdev_handle->bdev; + blocksize = sb->s_blocksize; + hblock = bdev_logical_block_size(bdev); + + if (blocksize < hblock) { + pr_err("blocksize too small for journal device\n"); + errno = -EINVAL; + goto out_bdev; + } + + sb_block = SIMPLEFS_BLOCK_SIZE / blocksize; + offset = SIMPLEFS_BLOCK_SIZE % blocksize; + set_blocksize(bdev, blocksize); + bh = __bread(bdev, sb_block, blocksize); + + if (!bh) { + pr_err("couldn't read superblock of external journal\n"); + errno = -EINVAL; + goto out_bdev; + } + + *j_start = sb_block; + + /* + * FIXME: + * Since I currently cannot find where the variable for the size of the + * external block device is stored, I am using device size (8MB) / device + * block size (4096 bytes) = 2048 to fill in j_len. + */ + + *j_len = 2048; + brelse(bh); + + return bdev_handle; + +out_bdev: + bdev_release(bdev_handle); + return ERR_PTR(errno); +} +#endif + +#if SIMPLEFS_AT_LEAST(6, 8, 0) +static journal_t *simplefs_get_dev_journal(struct super_block *sb, + dev_t journal_dev) +{ + journal_t *journal; + unsigned long long j_start; + unsigned long long j_len; + struct bdev_handle *bdev_handle; + int errno = 0; + + pr_info("simplefs_get_dev_journal: getting journal for device %u:%u\n", + MAJOR(journal_dev), MINOR(journal_dev)); + + struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); + + bdev_handle = + simplefs_get_journal_blkdev(sb, journal_dev, &j_start, &j_len); + if (IS_ERR(bdev_handle)) { + pr_err( + "simplefs_get_dev_journal: failed to get journal block device, " + "error %ld\n", + PTR_ERR(bdev_handle)); + return ERR_CAST(bdev_handle); + } + + pr_info( + "simplefs_get_dev_journal: journal block device obtained, start=%llu, " + "len=%llu\n", + j_start, j_len); + + journal = jbd2_journal_init_dev(bdev_handle->bdev, sb->s_bdev, j_start, + j_len, sb->s_blocksize); + if (IS_ERR(journal)) { + pr_err( + "simplefs_get_dev_journal: failed to initialize journal, error " + "%ld\n", + PTR_ERR(journal)); + errno = PTR_ERR(journal); + goto out_bdev; + } + + journal->j_private = sb; + sbi->s_journal_bdev_handle = bdev_handle; + + pr_info("simplefs_get_dev_journal: journal initialized successfully\n"); + + return journal; + +out_bdev: + bdev_release(bdev_handle); + return ERR_PTR(errno); +} +#elif SIMPLEFS_AT_LEAST(6, 5, 0) +static journal_t *simplefs_get_dev_journal(struct super_block *sb, + dev_t journal_dev) +{ + struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); + struct buffer_head *bh; + struct block_device *bdev; + int hblock, blocksize; + unsigned long long sb_block, start, len; + unsigned long offset; + journal_t *journal; + int errno = 0; + +#if SIMPLEFS_AT_LEAST(6, 5, 0) + bdev = blkdev_get_by_dev(journal_dev, BLK_OPEN_READ | BLK_OPEN_WRITE, sb, + NULL); +#elif SIMPLEFS_AT_LEAST(5, 10, 0) + bdev = blkdev_get_by_dev(journal_dev, FMODE_READ | FMODE_WRITE | FMODE_EXCL, + sb); +#endif + + if (IS_ERR(bdev)) { + printk(KERN_ERR "failed to open block device (%u:%u), error: %ld\n", + MAJOR(journal_dev), MINOR(journal_dev), PTR_ERR(bdev)); + return ERR_CAST(bdev); + } + + blocksize = sb->s_blocksize; + hblock = bdev_logical_block_size(bdev); + + if (blocksize < hblock) { + pr_err("blocksize too small for journal device\n"); + errno = -EINVAL; + goto out_bdev; + } + + sb_block = SIMPLEFS_BLOCK_SIZE / blocksize; + offset = SIMPLEFS_BLOCK_SIZE % blocksize; + set_blocksize(bdev, blocksize); + bh = __bread(bdev, sb_block, blocksize); + + if (!bh) { + pr_err("couldn't read superblock of external journal\n"); + errno = -EINVAL; + goto out_bdev; + } + + /* + * FIXME: + * Since I currently cannot find where the variable for the size of the + * external block device is stored, I am using device size (8MB) / device + * block size (4096 bytes) = 2048 to fill in j_len. + */ + len = 2048; + start = sb_block; + brelse(bh); + + journal = jbd2_journal_init_dev(bdev, sb->s_bdev, start, len, blocksize); + if (IS_ERR(journal)) { + pr_err( + "simplefs_get_dev_journal: failed to initialize journal, error " + "%ld\n", + PTR_ERR(journal)); + errno = PTR_ERR(journal); + goto out_bdev; + } + + sbi->s_journal_bdev = bdev; + journal->j_private = sb; + return journal; + + +out_bdev: +#if SIMPLEFS_AT_LEAST(6, 5, 0) + blkdev_put(bdev, sb); +#elif SIMPLEFS_AT_LEAST(5, 10, 0) + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); +#endif + return NULL; +} +#endif + +static int simplefs_load_journal(struct super_block *sb, + unsigned long journal_devnum) +{ + journal_t *journal; + struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb); + dev_t journal_dev; + int err = 0; + journal_dev = new_decode_dev(journal_devnum); + + journal = simplefs_get_dev_journal(sb, journal_dev); + if (IS_ERR(journal)) { + pr_err("Failed to get journal from device, error %ld\n", + PTR_ERR(journal)); + return PTR_ERR(journal); + } + + err = jbd2_journal_load(journal); + if (err) { + pr_err("error loading journal, error %d\n", err); + goto err_out; + } + + sbi->journal = journal; + + return 0; + +err_out: + jbd2_journal_destroy(journal); + return err; +} + +/* we use SIMPLEFS_OPT_JOURNAL_PATH case to load external journal device now */ +#define SIMPLEFS_OPT_JOURNAL_DEV 1 +#define SIMPLEFS_OPT_JOURNAL_PATH 2 +static const match_table_t tokens = { + {SIMPLEFS_OPT_JOURNAL_DEV, "journal_dev=%u"}, + {SIMPLEFS_OPT_JOURNAL_PATH, "journal_path=%s"}, +}; +static int simplefs_parse_options(struct super_block *sb, char *options) +{ + substring_t args[MAX_OPT_ARGS]; + int token, ret = 0, arg; + char *p; + + pr_info("simplefs_parse_options: parsing options '%s'\n", options); + + while ((p = strsep(&options, ","))) { + if (!*p) + continue; + + args[0].to = args[0].from = NULL; + token = match_token(p, tokens, args); + + switch (token) { + case SIMPLEFS_OPT_JOURNAL_DEV: + if (args->from && match_int(args, &arg)) { + pr_err("simplefs_parse_options: match_int failed\n"); + return 1; + } + printk(KERN_INFO "Loading journal devnum: %i\n", arg); + if ((ret = simplefs_load_journal(sb, arg))) { + pr_err( + "simplefs_parse_options: simplefs_load_journal failed with " + "%d\n", + ret); + return ret; + } + break; + + case SIMPLEFS_OPT_JOURNAL_PATH: { + char *journal_path; + struct inode *journal_inode; + struct path path; + + journal_path = match_strdup(&args[0]); + if (!journal_path) { + pr_err("simplefs_parse_options: match_strdup failed\n"); + return -ENOMEM; + } + ret = kern_path(journal_path, LOOKUP_FOLLOW, &path); + if (ret) { + pr_err( + "simplefs_parse_options: kern_path failed with error %d\n", + ret); + kfree(journal_path); + return ret; + } + + journal_inode = path.dentry->d_inode; + + path_put(&path); + kfree(journal_path); + + if (S_ISBLK(journal_inode->i_mode)) { + unsigned long journal_devnum = + new_encode_dev(journal_inode->i_rdev); + if ((ret = simplefs_load_journal(sb, journal_devnum))) { + pr_err( + "simplefs_parse_options: simplefs_load_journal failed " + "with %d\n", + ret); + return ret; + } + } + break; + } + } + } + + return 0; +} static struct super_operations simplefs_super_ops = { .put_super = simplefs_put_super, .alloc_inode = simplefs_alloc_inode, @@ -319,6 +638,17 @@ int simplefs_fill_super(struct super_block *sb, void *data, int silent) goto iput; } + ret = simplefs_parse_options(sb, data); + if (ret) { + pr_err( + "simplefs_fill_super: simplefs_parse_options failed with error " + "%d\n", + ret); + goto release; + } + + pr_info("simplefs_fill_super: successfully loaded superblock\n"); + return 0; iput: @@ -331,6 +661,6 @@ int simplefs_fill_super(struct super_block *sb, void *data, int silent) kfree(sbi); release: brelse(bh); - + pr_err("simplefs_fill_super: failed to load superblock\n"); return ret; }