Skip to content

Commit

Permalink
Implement external journal device initialization
Browse files Browse the repository at this point in the history
During fill super, call simplefs_parse_options to find the external
device path and set it as the journal device. Additionally, add the
`make journal` parameter in the Makefile to create an external journal
device.
  • Loading branch information
jason50123 committed Jun 20, 2024
1 parent 79a2831 commit 71898cd
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 3 deletions.
13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
5 changes: 5 additions & 0 deletions simplefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
* | blocks | rest of the blocks
* +---------------+
*/
#ifdef __KERNEL__
#include <linux/jbd2.h>
#endif

struct simplefs_inode {
uint32_t i_mode; /* File mode */
Expand Down Expand Up @@ -69,6 +72,8 @@ struct simplefs_sb_info {
uint32_t nr_free_blocks; /* Number of free blocks */

#ifdef __KERNEL__
journal_t *journal;
struct bdev_handle *s_journal_bdev_handle;
unsigned long *ifree_bitmap; /* In-memory free inodes bitmap */
unsigned long *bfree_bitmap; /* In-memory free blocks bitmap */
#endif
Expand Down
263 changes: 262 additions & 1 deletion super.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#include <linux/slab.h>
#include <linux/statfs.h>

#include <linux/blkdev.h>
#include <linux/jbd2.h>
#include <linux/namei.h>
#include <linux/parser.h>

#include "simplefs.h"

struct dentry *simplefs_mount(struct file_system_type *fs_type,
Expand Down Expand Up @@ -196,6 +201,251 @@ static int simplefs_statfs(struct dentry *dentry, struct kstatfs *stat)
return 0;
}

/* journal relation code */


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;
}
// sbi = (struct simplefs_sb_info *) (bh->b_data + offset);

*j_start = sb_block;

/*
*Fix me: This number is calculated by dividing the external device capacity
*(8MB) by the external device block size (4096). Currently, I don't know
*how to obtain the capacity size of the external device.
*/

*j_len = 2048;
brelse(bh);

return bdev_handle;

out_bdev:
bdev_release(bdev_handle);
return ERR_PTR(errno);
}


#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);
}

#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;
pr_info("simplefs_load_journal: loading journal from device number %lu\n",
journal_devnum);
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);
}
pr_info("simplefs_load_journal: journal obtained successfully\n");

err = jbd2_journal_load(journal);
if (err) {
pr_err("error loading journal, error %d\n", err);
goto err_out;
}

sbi->journal = journal;
pr_info("simplefs_load_journal: journal loaded successfully\n");

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, ",")) != NULL) {
if (!*p)
continue;

args[0].to = args[0].from = NULL;
token = match_token(p, tokens, args);

pr_info("simplefs_parse_options: token '%s' parsed as %d\n", p, token);

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;

pr_info("simplefs_parse_options: parsing journal path\n");

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;
}
}
}

pr_info("simplefs_parse_options: options parsed successfully\n");
return 0;
}
static struct super_operations simplefs_super_ops = {
.put_super = simplefs_put_super,
.alloc_inode = simplefs_alloc_inode,
Expand Down Expand Up @@ -319,6 +569,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:
Expand All @@ -331,6 +592,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;
}

0 comments on commit 71898cd

Please sign in to comment.