2021SC@SDUSC
super.c(1)
接下来分析另一个核心部分super.c文件。
这个文件中存的是文件系统里超级块(super_block)的相关函数,对于文件系统有极大的重要性。并且这个文件代码量也很大,还是分次进行介绍。
锁排序
注意i_mmap_sem (EXT4_I(inode)->i_mmap_sem)和i_mmap_rwsem (inode->i_mmap_rwsem)之间的区别
页面错误路径:
mmap_lock -> sb_start_pagefault -> i_mmap_sem (r) -> transaction start -> page lock -> i_data_sem (rw)
缓冲写路径:
sb_start_write -> i_mutex -> mmap_lock
b_start_write -> i_mutex -> transaction start -> page lock -> i_data_sem (rw)
截断:
sb_start_write -> i_mutex -> i_mmap_sem (w) -> i_mmap_rwsem (w) ->page lock
sb_start_write -> i_mutex -> i_mmap_sem (w) -> transaction start -> i_data_sem (rw)
直接输入输出:
sb_start_write -> i_mutex -> mmap_lock
b_start_write -> i_mutex -> transaction start -> i_data_sem (rw)
写页面:
transaction start -> page lock(s) -> i_data_sem (rw)
/* super.c里会使用到的函数的声明 */
static struct ext4_lazy_init *ext4_li_info;
static DEFINE_MUTEX(ext4_li_mtx);
static struct ratelimit_state ext4_mount_msg_ratelimit;
static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
unsigned long journal_devnum);
static int ext4_show_options(struct seq_file *seq, struct dentry *root);
static void ext4_update_super(struct super_block *sb);
static int ext4_commit_super(struct super_block *sb);
static int ext4_mark_recovery_complete(struct super_block *sb,
struct ext4_super_block *es);
static int ext4_clear_journal_err(struct super_block *sb,
struct ext4_super_block *es);
static int ext4_sync_fs(struct super_block *sb, int wait);
static int ext4_remount(struct super_block *sb, int *flags, char *data);
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
static int ext4_unfreeze(struct super_block *sb);
static int ext4_freeze(struct super_block *sb);
static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data);
static inline int ext2_feature_set_ok(struct super_block *sb);
static inline int ext3_feature_set_ok(struct super_block *sb);
static int ext4_feature_set_ok(struct super_block *sb, int readonly);
static void ext4_destroy_lazyinit_thread(void);
static void ext4_unregister_li_request(struct super_block *sb);
static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
unsigned int journal_inum);
/*
*工作方式类似于__bread_gfp(),但它使用ERR_PTR作为错误返回。
*目前使用sb_bread是不可能区分ENOMEM和EIO情况的(因为这两种情况都会导致NULL返回)。
*/
static struct buffer_head *__ext4_sb_bread_gfp(struct super_block *sb,
sector_t block, int op_flags,
gfp_t gfp)
{
struct buffer_head *bh;
int ret;
bh = sb_getblk_gfp(sb, block, gfp);
if (bh == NULL)
return ERR_PTR(-ENOMEM);
if (ext4_buffer_uptodate(bh))
return bh;
ret = ext4_read_bh_lock(bh, REQ_META | op_flags, true);
if (ret) {
put_bh(bh);
return ERR_PTR(ret);
}
return bh;
}
/*
del_gendisk()函数不初始化特定于磁盘的数据结构,包括bdi结构,而不告诉其他人。
一旦发生这种情况,任何调用mark_buffer_dirty()的尝试(例如,通过ext4_commit_super)都将导致内核OOPS。
在del_gendisk()中放入适当的钩子来通知VFS和文件系统层之前,这是一个防止出现这些错误的杂项。
*/
static int block_device_ejected(struct super_block *sb)
{
struct inode *bd_inode = sb->s_bdev->bd_inode;
struct backing_dev_info *bdi = inode_to_bdi(bd_inode);
return bdi->dev == NULL;
}
static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn)
{
struct super_block *sb = journal->j_private;
struct ext4_sb_info *sbi = EXT4_SB(sb);
int error = is_journal_aborted(journal);
struct ext4_journal_cb_entry *jce;
BUG_ON(txn->t_state == T_FINISHED);
ext4_process_freed_data(sb, txn->t_tid);
spin_lock(&sbi->s_md_lock);
while (!list_empty(&txn->t_private_list)) {
jce = list_entry(txn->t_private_list.next,
struct ext4_journal_cb_entry, jce_list);
list_del_init(&jce->jce_list);
spin_unlock(&sbi->s_md_lock);
jce->jce_func(sb, jce, error);
spin_lock(&sbi->s_md_lock);
}
spin_unlock(&sbi->s_md_lock);
}
/*
*这个write_cache_pages()的write_cache_pages回调函数处理了一些页面清理后的情况。
* write_cache_pages()已经检查脏页并调用clear_page_dirty_for_io(),这是我们想要的,写保护页。
*然而,我们可能不得不重脏一个页面(见下面)。
*/
static int ext4_journalled_writepage_callback(struct page *page,
struct writeback_control *wbc,