2021SC@SDUSC

今天来分析ext4_jbd2.h 和 ext4_jbd2.c文件。

以下是ext4_jbd2.h头文件的代码分析

// SPDX-License-Identifier: GPL-2.0+
/** ext4_jbd2.h** Written by Stephen C. Tweedie <sct@redhat.com>, 1999** Copyright 1998--1999 Red Hat corp --- All Rights Reserved** Ext4-specific journaling extensions.*/#ifndef _EXT4_JBD2_H
#define _EXT4_JBD2_H#include <linux/fs.h>
#include <linux/jbd2.h>
#include "ext4.h"#define EXT4_JOURNAL(inode)  (EXT4_SB((inode)->i_sb)->s_journal)/*
定义一个事务修改一个数据块所需的块数。为了完成事务,我们可能必须接触一个索引节点、一个位图缓冲区、最多三个间接块、组和超级块摘要以及数据块。对于支持区段的fs,我们可能需要分配和修改最多5个级别的树、
数据块(对于每一个数据块,我们都需要位图+组摘要)、根存储在sb inode中*/#define EXT4_SINGLEDATA_TRANS_BLOCKS(sb)               \(ext4_has_feature_extents(sb) ? 20U : 8U)/*
扩展属性操作最多接触两个数据缓冲区、两个位图缓冲区和两个组摘要,除了已经考虑到的inode和超级块。*/#define EXT4_XATTR_TRANS_BLOCKS      6U/* 定义修改数据的事务的最小大小。这需要考虑这样一个事实,即我们可能最终也会修改两个配额文件(一个用于组,一个用于用户配额)。当然,超级块只更新一次,所以不必为配额更新再次计数。*/#define EXT4_DATA_TRANS_BLOCKS(sb)    (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + \EXT4_XATTR_TRANS_BLOCKS - 2 + \EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))/*
定义修改数据所需的元数据块的数量。
这包括超级块,inode块,配额块和xattr块*/
#define EXT4_META_TRANS_BLOCKS(sb)  (EXT4_XATTR_TRANS_BLOCKS + \EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))/*
为预期写入任何给定事务的数据量定义一个任意的限制。对于无限制的事务,比如write(2)和truncate(2),我们可以写更多的内容,
但我们总是从最大事务大小开始,然后乐观地增加事务大小。*/#define EXT4_MAX_TRANS_DATA     64U/*
一旦句柄的缓冲信用降到这么低,我们就分解一个大型截断或写入事务,
我们需要扩展交易或者开始一个新的交易。
这里为至少一个块的inode、位图、超级块、组和间接更新预留足够的空间,再加上两个配额更新。
不需要配额分配。*/#define EXT4_RESERVE_TRANS_BLOCKS 12U/*
如果我们需要在目录中插入一个条目,那么需要的积分数。
对于每一个新的索引块,我们需要4个块(旧索引块,新索引块,位图块,bg摘要)。
对于普通的htree目录,有2个级别;如果启用了largedir特性,则是3个级别。*/
#define EXT4_INDEX_EXTRA_TRANS_BLOCKS   12U#ifdef CONFIG_QUOTA
/*
配额更新所需的块数量——我们知道已经分配了结构,所以我们只需要更新数据块*/
#define EXT4_QUOTA_TRANS_BLOCKS(sb) ((ext4_quota_capable(sb)) ? 1 : 0)
/*
插入/删除配额所需的块数量——我们做一些块写,但inode, sb和group更新只做一次*/
#define EXT4_QUOTA_INIT_BLOCKS(sb) ((ext4_quota_capable(sb)) ?\(DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\+3+DQUOT_INIT_REWRITE) : 0)#define EXT4_QUOTA_DEL_BLOCKS(sb) ((ext4_quota_capable(sb)) ?\(DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\+3+DQUOT_DEL_REWRITE) : 0)
#else
#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
#define EXT4_QUOTA_INIT_BLOCKS(sb) 0
#define EXT4_QUOTA_DEL_BLOCKS(sb) 0
#endif
#define EXT4_MAXQUOTAS_TRANS_BLOCKS(sb) (EXT4_MAXQUOTAS*EXT4_QUOTA_TRANS_BLOCKS(sb))
#define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (EXT4_MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb))
#define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (EXT4_MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb))/*Ext4处理操作类型——出于日志记录的目的*/
#define EXT4_HT_MISC             0
#define EXT4_HT_INODE            1
#define EXT4_HT_WRITE_PAGE       2
#define EXT4_HT_MAP_BLOCKS       3
#define EXT4_HT_DIR              4
#define EXT4_HT_TRUNCATE         5
#define EXT4_HT_QUOTA            6
#define EXT4_HT_RESIZE           7
#define EXT4_HT_MIGRATE          8
#define EXT4_HT_MOVE_EXTENTS     9
#define EXT4_HT_XATTR           10
#define EXT4_HT_EXT_CONVERT     11
#define EXT4_HT_MAX             12/*
struct ext4_journal_cb_entry -回调信息的基本结构。
这个结构是一个用于使用你自己的回调结构的“种子”结构。
如果使用回调,则必须分配其中一个或另一个自己定义的struct,
该struct将该struct作为第一个元素,并将其传递给ext4_journal_callback_add()。*/
struct ext4_journal_cb_entry {/* 列出附加到同一句柄的其他回调的信息 */struct list_head jce_list;/* 用这个回调结构调用的函数 */void (*jce_func)(struct super_block *sb,struct ext4_journal_cb_entry *jce, int error);/* 用户数据在这里 */
};/*
Ext4_journal_callback_add:添加一个在事务提交后调用的函数
@handle:活动日志事务句柄注册回调
@func:事务提交后调用的回调函数:
@sb:事务当前文件系统的超级块
@jce:返回的日志回调数据
@rc:日志提交时的状态(0 =事务正常提交)
@jce:日志回调数据(内部和函数私有数据结构)在为其创建句柄的事务完成之后,注册的函数将在日志线程的上下文中被调用。当调用回调函数时不会持有锁,所以在回调中调用阻塞函数是安全的,
但是回调不应该阻塞或运行太长时间,否则文件系统将阻塞,等待下一个事务提交。
不能使用日志功能,否则会有死锁的风险。
同一个事务上的多个注册回调的调用顺序没有保证。*/
static inline void _ext4_journal_callback_add(handle_t *handle,struct ext4_journal_cb_entry *jce)
{/* 将jce添加到事务的私有列表中 */list_add_tail(&jce->jce_list, &handle->h_transaction->t_private_list);
}static inline void ext4_journal_callback_add(handle_t *handle,void (*func)(struct super_block *sb,struct ext4_journal_cb_entry *jce,int rc),struct ext4_journal_cb_entry *jce)
{struct ext4_sb_info *sbi =EXT4_SB(handle->h_transaction->t_journal->j_private);/* 将jce添加到事务的私有列表中 */jce->jce_func = func;spin_lock(&sbi->s_md_lock);_ext4_journal_callback_add(handle, jce);spin_unlock(&sbi->s_md_lock);
}/*
ext4_journal_callback_del:删除已注册的回调
@handle:已注册回调的活动日志事务句柄
@jce:注册日志回调条目到注销
如果对象被成功移除,返回true*/
static inline bool ext4_journal_callback_try_del(handle_t *handle,struct ext4_journal_cb_entry *jce)
{bool deleted;struct ext4_sb_info *sbi =EXT4_SB(handle->h_transaction->t_journal->j_private);spin_lock(&sbi->s_md_lock);deleted = !list_empty(&jce->jce_list);list_del_init(&jce->jce_list);spin_unlock(&sbi->s_md_lock);return deleted;
}int
ext4_mark_iloc_dirty(handle_t *handle,struct inode *inode,struct ext4_iloc *iloc);/*
在成功的情况下,我们以一个出色的引用计数结束iloc - > bh。这个以后必须清除。*/int ext4_reserve_inode_write(handle_t *handle, struct inode *inode,struct ext4_iloc *iloc);#define ext4_mark_inode_dirty(__h, __i)                   \__ext4_mark_inode_dirty((__h), (__i), __func__, __LINE__)
int __ext4_mark_inode_dirty(handle_t *handle, struct inode *inode,const char *func, unsigned int line);int ext4_expand_extra_isize(struct inode *inode,unsigned int new_extra_isize,struct ext4_iloc *iloc);
/*ext4调用JBD的包装器函数。*/
int __ext4_journal_get_write_access(const char *where, unsigned int line,handle_t *handle, struct buffer_head *bh);int __ext4_forget(const char *where, unsigned int line, handle_t *handle,int is_metadata, struct inode *inode,struct buffer_head *bh, ext4_fsblk_t blocknr);int __ext4_journal_get_create_access(const char *where, unsigned int line,handle_t *handle, struct buffer_head *bh);int __ext4_handle_dirty_metadata(const char *where, unsigned int line,handle_t *handle, struct inode *inode,struct buffer_head *bh);#define ext4_journal_get_write_access(handle, bh) \__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
#define ext4_forget(handle, is_metadata, inode, bh, block_nr) \__ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \(bh), (block_nr))
#define ext4_journal_get_create_access(handle, bh) \__ext4_journal_get_create_access(__func__, __LINE__, (handle), (bh))
#define ext4_handle_dirty_metadata(handle, inode, bh) \__ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \(bh))handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,int type, int blocks, int rsv_blocks,int revoke_creds);
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)/*
注意:不要将其用于NULL句柄。这只是为了确定是否正确分配的句柄是否使用日志。*/
static inline int ext4_handle_valid(handle_t *handle)
{if ((unsigned long)handle < EXT4_NOJOURNAL_MAX_REF_COUNT)return 0;return 1;
}static inline void ext4_handle_sync(handle_t *handle)
{if (ext4_handle_valid(handle))handle->h_sync = 1;
}static inline int ext4_handle_is_aborted(handle_t *handle)
{if (ext4_handle_valid(handle))return is_handle_aborted(handle);return 0;
}static inline int ext4_free_metadata_revoke_credits(struct super_block *sb,int blocks)
{/* 释放每个元数据块可以释放一个集群 */return blocks * EXT4_SB(sb)->s_cluster_ratio;
}static inline int ext4_trans_default_revoke_credits(struct super_block *sb)
{return ext4_free_metadata_revoke_credits(sb, 8);
}#define ext4_journal_start_sb(sb, type, nblocks)           \__ext4_journal_start_sb((sb), __LINE__, (type), (nblocks), 0,  \ext4_trans_default_revoke_credits(sb))#define ext4_journal_start(inode, type, nblocks)         \__ext4_journal_start((inode), __LINE__, (type), (nblocks), 0,  \ext4_trans_default_revoke_credits((inode)->i_sb))#define ext4_journal_start_with_reserve(inode, type, blocks, rsv_blocks)\__ext4_journal_start((inode), __LINE__, (type), (blocks), (rsv_blocks),\ext4_trans_default_revoke_credits((inode)->i_sb))#define ext4_journal_start_with_revoke(inode, type, blocks, revoke_creds) \__ext4_journal_start((inode), __LINE__, (type), (blocks), 0,   \(revoke_creds))static inline handle_t *__ext4_journal_start(struct inode *inode,unsigned int line, int type,int blocks, int rsv_blocks,int revoke_creds)
{return __ext4_journal_start_sb(inode->i_sb, line, type, blocks,rsv_blocks, revoke_creds);
}#define ext4_journal_stop(handle) \__ext4_journal_stop(__func__, __LINE__, (handle))#define ext4_journal_start_reserved(handle, type) \__ext4_journal_start_reserved((handle), __LINE__, (type))handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,int type);static inline handle_t *ext4_journal_current_handle(void)
{return journal_current_handle();
}static inline int ext4_journal_extend(handle_t *handle, int nblocks, int revoke)
{if (ext4_handle_valid(handle))return jbd2_journal_extend(handle, nblocks, revoke);return 0;
}static inline int ext4_journal_restart(handle_t *handle, int nblocks,int revoke)
{if (ext4_handle_valid(handle))return jbd2__journal_restart(handle, nblocks, revoke, GFP_NOFS);return 0;
}int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,int extend_cred, int revoke_cred);/*
确保@handle至少有@check_creds credits可用。
如果没有,事务将被扩展或重新启动,以包含至少@extend_cred credits。
在重新启动事务之前,会执行@fn以允许在事务重新启动之前进行清理。
返回值在发生错误时< 0,在句柄有足够的信用或事务扩展成功时为0,在事务必须重新启动时为1。*/
#define ext4_journal_ensure_credits_fn(handle, check_cred, extend_cred, \revoke_cred, fn) \
({                                  \__label__ __ensure_end;                        \int err = __ext4_journal_ensure_credits((handle), (check_cred),   \(extend_cred), (revoke_cred)); \\if (err <= 0)                         \goto __ensure_end;                 \err = (fn);                           \if (err < 0)                            \goto __ensure_end;                 \err = ext4_journal_restart((handle), (extend_cred), (revoke_cred)); \if (err == 0)                          \err = 1;                      \
__ensure_end:                               \err;                               \
})/*
确保给定的句柄至少有请求的可用积分,如果需要可能重新启动事务。
我们还确保事务至少有ext4_trans_default_revoke_credits(sb)撤销记录的空间,
因为释放一个或两个块是非常常见的模式,请求这一模式的成本非常低。*/
static inline int ext4_journal_ensure_credits(handle_t *handle, int credits,int revoke_creds)
{return ext4_journal_ensure_credits_fn(handle, credits, credits,revoke_creds, 0);
}static inline int ext4_journal_blocks_per_page(struct inode *inode)
{if (EXT4_JOURNAL(inode) != NULL)return jbd2_journal_blocks_per_page(inode);return 0;
}static inline int ext4_journal_force_commit(journal_t *journal)
{if (journal)return jbd2_journal_force_commit(journal);return 0;
}static inline int ext4_jbd2_inode_add_write(handle_t *handle,struct inode *inode, loff_t start_byte, loff_t length)
{if (ext4_handle_valid(handle))return jbd2_journal_inode_ranged_write(handle,EXT4_I(inode)->jinode, start_byte, length);return 0;
}static inline int ext4_jbd2_inode_add_wait(handle_t *handle,struct inode *inode, loff_t start_byte, loff_t length)
{if (ext4_handle_valid(handle))return jbd2_journal_inode_ranged_wait(handle,EXT4_I(inode)->jinode, start_byte, length);return 0;
}static inline void ext4_update_inode_fsync_trans(handle_t *handle,struct inode *inode,int datasync)
{struct ext4_inode_info *ei = EXT4_I(inode);if (ext4_handle_valid(handle) && !is_handle_aborted(handle)) {ei->i_sync_tid = handle->h_transaction->t_tid;if (datasync)ei->i_datasync_tid = handle->h_transaction->t_tid;}
}/* super.c */
int ext4_force_commit(struct super_block *sb);/** Ext4 inode日志模式*/
#define EXT4_INODE_JOURNAL_DATA_MODE    0x01 /* 期刊数据模式 */
#define EXT4_INODE_ORDERED_DATA_MODE    0x02 /* 命令数据模式 */
#define EXT4_INODE_WRITEBACK_DATA_MODE  0x04 /* 回写数据模式 */int ext4_inode_journal_mode(struct inode *inode);static inline int ext4_should_journal_data(struct inode *inode)
{return ext4_inode_journal_mode(inode) & EXT4_INODE_JOURNAL_DATA_MODE;
}static inline int ext4_should_order_data(struct inode *inode)
{return ext4_inode_journal_mode(inode) & EXT4_INODE_ORDERED_DATA_MODE;
}static inline int ext4_should_writeback_data(struct inode *inode)
{return ext4_inode_journal_mode(inode) & EXT4_INODE_WRITEBACK_DATA_MODE;
}static inline int ext4_free_data_revoke_credits(struct inode *inode, int blocks)
{if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)return 0;if (!ext4_should_journal_data(inode))return 0;/*一个区段中的数据块是连续的,只考虑区段边界上的部分集群*/return blocks + 2*(EXT4_SB(inode->i_sb)->s_cluster_ratio - 1);
}/*
这个函数控制我们是否应该尝试进入dioread_nolock代码路径,这使得避免使用i_mutex进行直接I/O读取是安全的。
这只适用于基于区段的文件,如果启用了数据日志,它就不起作用了。
因为dioread_nolock代码使用b_private将信息传递回I/O完成处理程序,这与jbd对b_private的使用冲突。*/
static inline int ext4_should_dioread_nolock(struct inode *inode)
{if (!test_opt(inode->i_sb, DIOREAD_NOLOCK))return 0;if (!S_ISREG(inode->i_mode))return 0;if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))return 0;if (ext4_should_journal_data(inode))return 0;/* 临时修复,以防止generic/422测试失败 */if (!test_opt(inode->i_sb, DELALLOC))return 0;return 1;
}#endif /* _EXT4_JBD2_H */

以下是ext4_jbd2.c的代码分析

// SPDX-License-Identifier: GPL-2.0
/** ext4和JBD之间的接口*/#include "ext4_jbd2.h"#include <trace/events/ext4.h>/*
Ext4 inode日志模式
EXT4_INODE_JOURNAL_DATA_MODE 为 期刊数据模式
EXT4_INODE_ORDERED_DATA_MODE 为 命令数据模式
EXT4_INODE_WRITEBACK_DATA_MODE 为 回写数据模式
*/
int ext4_inode_journal_mode(struct inode *inode)
{if (EXT4_JOURNAL(inode) == NULL)return EXT4_INODE_WRITEBACK_DATA_MODE;   /* 回写 *//* 不支持延迟分配的数据日志记录 */if (!S_ISREG(inode->i_mode) ||ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE) ||test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||(ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&!test_opt(inode->i_sb, DELALLOC))) {/* 不支持对加密数据进行数据日志记录 */if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode))return EXT4_INODE_ORDERED_DATA_MODE;  /* 命令 */return EXT4_INODE_JOURNAL_DATA_MODE;    /* 期刊数据 */}if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)return EXT4_INODE_ORDERED_DATA_MODE;   /* 命令 */if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)return EXT4_INODE_WRITEBACK_DATA_MODE;  /* 回写 */BUG();
}/*只增加非指针句柄值*/
static handle_t *ext4_get_nojournal(void)
{handle_t *handle = current->journal_info;unsigned long ref_cnt = (unsigned long)handle;BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT);ref_cnt++;handle = (handle_t *)ref_cnt;current->journal_info = handle;return handle;
}/*减少非指针句柄值*/
static void ext4_put_nojournal(handle_t *handle)
{unsigned long ref_cnt = (unsigned long)handle;BUG_ON(ref_cnt == 0);ref_cnt--;handle = (handle_t *)ref_cnt;current->journal_info = handle;
}/*jbd2_journal_start/end的包装器。*/
static int ext4_journal_check_start(struct super_block *sb)
{journal_t *journal;might_sleep();if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))return -EIO;if (sb_rdonly(sb))return -EROFS;WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);journal = EXT4_SB(sb)->s_journal;/*这里的特殊情况:如果日志在我们背后终止了(例如。EIO在提交线程中),那么我们仍然需要干净地将FS本身设置为只读。*/if (journal && is_journal_aborted(journal)) {ext4_abort(sb, -journal->j_errno, "Detected aborted journal");return -EROFS;}return 0;
}/*ext4调用JBD的包装器函数。期刊数据模式的开启*/
handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,int type, int blocks, int rsv_blocks,int revoke_creds)
{journal_t *journal;int err;trace_ext4_journal_start(sb, blocks, rsv_blocks, revoke_creds,_RET_IP_);err = ext4_journal_check_start(sb);if (err < 0)return ERR_PTR(err);journal = EXT4_SB(sb)->s_journal;if (!journal || (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))return ext4_get_nojournal();return jbd2__journal_start(journal, blocks, rsv_blocks, revoke_creds,GFP_NOFS, type, line);
}/*ext4调用JBD的包装器函数。期刊数据模式的停止*/
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
{struct super_block *sb;int err;int rc;if (!ext4_handle_valid(handle)) {ext4_put_nojournal(handle);return 0;}err = handle->h_err;if (!handle->h_transaction) {rc = jbd2_journal_stop(handle);return err ? err : rc;}sb = handle->h_transaction->t_journal->j_private;rc = jbd2_journal_stop(handle);if (!err)err = rc;if (err)__ext4_std_error(sb, where, line, err);return err;
}handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,int type)
{struct super_block *sb;int err;if (!ext4_handle_valid(handle))return ext4_get_nojournal();sb = handle->h_journal->j_private;trace_ext4_journal_start_reserved(sb,jbd2_handle_buffer_credits(handle), _RET_IP_);err = ext4_journal_check_start(sb);if (err < 0) {jbd2_journal_free_reserved(handle);return ERR_PTR(err);}err = jbd2_journal_start_reserved(handle, type, line);if (err < 0)return ERR_PTR(err);return handle;
}int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,int extend_cred, int revoke_cred)
{if (!ext4_handle_valid(handle))return 0;if (jbd2_handle_buffer_credits(handle) >= check_cred &&handle->h_revoke_credits >= revoke_cred)return 0;extend_cred = max(0, extend_cred - jbd2_handle_buffer_credits(handle));revoke_cred = max(0, revoke_cred - handle->h_revoke_credits);return ext4_journal_extend(handle, extend_cred, revoke_cred);
}static void ext4_journal_abort_handle(const char *caller, unsigned int line,const char *err_fn,struct buffer_head *bh,handle_t *handle, int err)
{char nbuf[16];const char *errstr = ext4_decode_error(NULL, err, nbuf);BUG_ON(!ext4_handle_valid(handle));if (bh)BUFFER_TRACE(bh, "abort");if (!handle->h_err)handle->h_err = err;if (is_handle_aborted(handle))return;printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n",caller, line, errstr, err_fn);jbd2_journal_abort_handle(handle);
}static void ext4_check_bdev_write_error(struct super_block *sb)
{struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;struct ext4_sb_info *sbi = EXT4_SB(sb);int err;/*如果块设备有写错误标志,它可能无法在后台异步写入元数据缓冲区。在这种情况下,我们可以从磁盘读取旧数据并再次将其写入,这可能会导致磁盘上的文件系统不一致。*/if (errseq_check(&mapping->wb_err, READ_ONCE(sbi->s_bdev_wb_err))) {spin_lock(&sbi->s_bdev_wb_lock);err = errseq_check_and_advance(&mapping->wb_err, &sbi->s_bdev_wb_err);spin_unlock(&sbi->s_bdev_wb_lock);if (err)ext4_error_err(sb, -err,"Error while async write back metadata");}
}int __ext4_journal_get_write_access(const char *where, unsigned int line,handle_t *handle, struct buffer_head *bh)
{int err = 0;might_sleep();if (bh->b_bdev->bd_super)ext4_check_bdev_write_error(bh->b_bdev->bd_super);if (ext4_handle_valid(handle)) {err = jbd2_journal_get_write_access(handle, bh);if (err)ext4_journal_abort_handle(where, line, __func__, bh,handle, err);}return err;
}/*
如果我们正在释放已经记录的数据,ext4 forget函数必须执行一个revoke。
元数据(如间接块)在任何情况下都必须撤销。"bh"可能是NULL:一个元数据块可能已经从内存中释放了,
但在日志中可能仍然有它的记录,并且该记录仍然需要被撤销。*/
int __ext4_forget(const char *where, unsigned int line, handle_t *handle,int is_metadata, struct inode *inode,struct buffer_head *bh, ext4_fsblk_t blocknr)
{int err;might_sleep();trace_ext4_forget(inode, is_metadata, blocknr);BUFFER_TRACE(bh, "enter");jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, ""data mode %x\n",bh, is_metadata, inode->i_mode,test_opt(inode->i_sb, DATA_FLAGS));/* 在没有日志的情况下,做一个忘记和返回 */if (!ext4_handle_valid(handle)) {bforget(bh);return 0;}/* 如果我们要写全数据日志,千万不要使用revoke函数:没有必要,而且V1超级块不支持它。否则,只需跳过对未记录的数据块的撤销。*/if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||(!is_metadata && !ext4_should_journal_data(inode))) {if (bh) {BUFFER_TRACE(bh, "call jbd2_journal_forget");err = jbd2_journal_forget(handle, bh);if (err)ext4_journal_abort_handle(where, line, __func__,bh, handle, err);return err;}return 0;}/*data!=journal && (is_metadata || should_journal_data(inode))*/BUFFER_TRACE(bh, "call jbd2_journal_revoke");err = jbd2_journal_revoke(handle, blocknr, bh);if (err) {ext4_journal_abort_handle(where, line, __func__,bh, handle, err);__ext4_error(inode->i_sb, where, line, true, -err, 0,"error %d when attempting revoke", err);}BUFFER_TRACE(bh, "exit");return err;
}int __ext4_journal_get_create_access(const char *where, unsigned int line,handle_t *handle, struct buffer_head *bh)
{int err = 0;if (ext4_handle_valid(handle)) {err = jbd2_journal_get_create_access(handle, bh);if (err)ext4_journal_abort_handle(where, line, __func__,bh, handle, err);}return err;
}int __ext4_handle_dirty_metadata(const char *where, unsigned int line,handle_t *handle, struct inode *inode,struct buffer_head *bh)
{int err = 0;might_sleep();set_buffer_meta(bh);set_buffer_prio(bh);set_buffer_uptodate(bh);if (ext4_handle_valid(handle)) {err = jbd2_journal_dirty_metadata(handle, bh);/* 错误只会由于日志中止或严重的bug而发生 */if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) {ext4_journal_abort_handle(where, line, __func__, bh,handle, err);if (inode == NULL) {pr_err("EXT4: jbd2_journal_dirty_metadata ""failed: handle type %u started at ""line %u, credits %u/%u, errcode %d",handle->h_type,handle->h_line_no,handle->h_requested_credits,jbd2_handle_buffer_credits(handle), err);return err;}ext4_error_inode(inode, where, line,bh->b_blocknr,"journal_dirty_metadata failed: ""handle type %u started at line %u, ""credits %u/%u, errcode %d",handle->h_type,handle->h_line_no,handle->h_requested_credits,jbd2_handle_buffer_credits(handle),err);}} else {if (inode)mark_buffer_dirty_inode(bh, inode);elsemark_buffer_dirty(bh);if (inode && inode_needs_sync(inode)) {sync_dirty_buffer(bh);if (buffer_req(bh) && !buffer_uptodate(bh)) {ext4_error_inode_err(inode, where, line,bh->b_blocknr, EIO,"IO error syncing itable block");err = -EIO;}}}return err;
}

Linux内核文件系统8相关推荐

  1. DM365 linux内核文件系统的烧写步骤及其uboot参数配置

    DM365 linux内核&文件系统的烧写步骤及其uboot参数配置     目录 源文档下载:http://download.csdn.net/detail/zhangjikuan/6443 ...

  2. Linux内核文件系统10

    2021SC@SDUSC inode.c(1) 今天来分析inode.c文件.有了前面对ext4_jbd2.h.acl.h等头文件的分析做基础,今天的分析将相对简单. 在看代码之前,首先要说一下ino ...

  3. 【技术分享篇】Linux内核——手把手带你实现一个Linux内核文件系统丨Linux内核源码分析

    手把手带你实现一个Linux内核文件系统 1. 内核文件系统架构分析 2. 行行珠玑,代码实现 [技术分享篇]Linux内核--手把手带你实现一个Linux内核文件系统丨Linux内核源码分析 更多L ...

  4. Linux内核文件系统

    Linux 内核文件系统 概述 文件系统这一词在不同上下文时有不同的含义: 指一种具体的文件格式.例如Linux的文件系统是Ext2,MSDOS的文件系统是FAT16,而Windows NT的文件系统 ...

  5. 索引节点inode: Linux内核文件系统之(inode)

    inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间.档名.使用者及群组等. static int eachreg_open(struct inode *inode, struct ...

  6. linux内核文件系统的架构,《深入理解Linux内核》-文件系统学习心得

    内核中要注意的是各种结构体,结构体之间的联系和各个函数以及函数之间的调用关系,参数的传递和函数的功能. 内核中数据结构的字段无外乎包括三种字段:属性字段,调用方法,指向其它结构的指针.具体如下图所示: ...

  7. Linux内核文件系统5

    2021SC@SDUSC 今天来分析ext4.h文件 #ifndef _EXT4_H #define _EXT4_H#include <linux/types.h> #include &l ...

  8. Linux 内核文件系统模块结构体关系图

    导言 很久没有更新csdn博客了,工作两年,学习了不少新东西,很多都没有来的及整理,用过不久很快就忘记了,写到博客中做个记录. 关系图 下图为去年学习文件系统时所画,有参考网上其他博主的资料,也有自己 ...

  9. Linux内核文件系统12

    2021SC@SDUSC inode.c(3) /** 强制为给定的 inode 分配所有延迟分配块.*/ int ext4_alloc_da_blocks(struct inode *inode) ...

最新文章

  1. 谷歌用AI训练“耳机线”,实现了触摸屏大多数功能
  2. 服务器双网卡冗余备份技术的实现
  3. HDU 5727 Necklace
  4. Java 编写程序 创建一个游戏【5、6两章的内容】【第5章】
  5. 数据库 MySQL 如何设置表的主键自增起始值
  6. common lisp 学习第二天 简单数据库操作
  7. volatile和原子操作
  8. c++11 数值类型和字符串的相互转换
  9. Hibernate(2012/2/27)
  10. Sql分页存储过程(支持多表分页存储)
  11. Oracle 查看表空间使用率,表空间扩展
  12. LaTeX软件安装及简易入门
  13. IT中文技术站十大网站收藏
  14. JTS Java空间几何计算、距离、最近点、subLine等计算
  15. 高中计算机操作题frontpage步骤,一级计算机操作题步骤——Frontpage操作.docx
  16. Java实现复数的加减乘除
  17. Vue | 显示切换(v-if与v-show,display,visibility与opacity)
  18. typescript完成日期转换
  19. 深度分析,皓丽M5_企业版_增强版共同点与核心区别?
  20. buffer pool详解(free链表+flush链表+lru链表)

热门文章

  1. 树莓派linux桌面分辨率,树莓派安装KALI Linux屏幕分辨率问题
  2. http://syy7.com/a/25.php,CVE-2020-7060
  3. 学习BitSet集合中set方法的小结
  4. 记录一下:获取公众号关注页、介绍页链接
  5. 阿里云孙成浩:生而为云,连接增长——洛神云网络3.0持续演进
  6. 面相对象的学习,对象的封装与继承
  7. Lua 字符串格式化
  8. 新概念英语第四册31-40课(转)
  9. 最适合 Apple Silicon 的 Tensorflow 环境搭建
  10. 别再抄文案了,别再搬运了,很多自媒体短视频大号都封了