mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-09 07:23:14 +00:00
Fix some bugs in converting ext4 to use the new mount API, as well as
more bug fixes and clean ups in the ext4 fast_commit feature (most notably, in the tracepoints). In the jbd2 layer, the t_handle_lock spinlock has been removed, with the last place where it was actually needed replaced with an atomic cmpxchg. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEOrBXt+eNlFyMVZH70292m8EYBPAFAmI5QPsACgkQ0292m8EY BPD99Q//ZO7L0XUicNEsuxvtXDj4GBWg/R62JXcHsIetIRhCpM4+L2vS3kZVXCBh Pd+QMYsfAhuXFK3yK5MfvAb+XJ2e0iVNaw9ke2ueAsrGidlKWOstbMWWqEa4Dcr2 age1DdrRB5hXIFxOg+Qz8bPMpP9XfxEiMqT2K7PgB+R/Z2chBbwPJ/G1VmdOMNGe FzxZgvXFY5EIJgjqj09ioD5pUSlAj5sgI4y/Dcv4liW11kvgBxg7Mm8ZouoPRHa3 gZF5mj/40izhD8gYPmZJtHFFYuqBBLsrmJnjUF2Y2yvZt5I14j4w/5OfcjuJqWn6 t2mlnk8/o6ZHzyuUV0/8/x35rZBlwqMq9ep6L9pN07XVB0/xOMG7a59pWOElhFGp sG/ELhP251ZE+glWC9/X1VjnhM+agduiWAUY0wF0X62LM3sw6sVO3q6hsI1EKunN O5VlvzPXNTZn9bcKzXzpvbxP9vF6s4exf32rMxd1rfSptE6myavGwss7MlB1HM7v xiM/A4AZT/qlUH9eOJxF1aGcswpxnFwTwHKZqBVW32iyG9mbqRT2lKajx4WstowU pdI4X7YfF7GhI5tMFR5S4Fa9FiOivO4EEc6z/db753kVuxSjwtyDd8ip56ZuJUhU LqpwZO2bh+UhR0vRO0pQH/ZqCzCfCaJTGtBwAvJUwo3g/zRTmoc= =naSC -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: "Fix some bugs in converting ext4 to use the new mount API, as well as more bug fixes and clean ups in the ext4 fast_commit feature (most notably, in the tracepoints). In the jbd2 layer, the t_handle_lock spinlock has been removed, with the last place where it was actually needed replaced with an atomic cmpxchg" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (35 commits) ext4: fix kernel doc warnings ext4: fix remaining two trace events to use same printk convention ext4: add commit tid info in ext4_fc_commit_start/stop trace events ext4: add commit_tid info in jbd debug log ext4: add transaction tid info in fc_track events ext4: add new trace event in ext4_fc_cleanup ext4: return early for non-eligible fast_commit track events ext4: do not call FC trace event in ext4_fc_commit() if FS does not support FC ext4: convert ext4_fc_track_dentry type events to use event class ext4: fix ext4_fc_stats trace point ext4: remove unused enum EXT4_FC_COMMIT_FAILED ext4: warn when dirtying page w/o buffers in data=journal mode doc: fixed a typo in ext4 documentation ext4: make mb_optimize_scan performance mount option work with extents ext4: make mb_optimize_scan option work with set/unset mount cmd ext4: don't BUG if someone dirty pages without asking ext4 first ext4: remove redundant assignment to variable split_flag1 ext4: fix underflow in ext4_max_bitmap_size() ext4: fix ext4_mb_clear_bb() kernel-doc comment ext4: fix fs corruption when tring to remove a non-empty directory with IO error ...
This commit is contained in:
commit
9b03992f0c
@ -39,7 +39,7 @@ For 32-bit filesystems, limits are as follows:
|
||||
- 4TiB
|
||||
- 8TiB
|
||||
- 16TiB
|
||||
- 256PiB
|
||||
- 256TiB
|
||||
* - Blocks Per Block Group
|
||||
- 8,192
|
||||
- 16,384
|
||||
|
@ -411,6 +411,7 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
|
||||
* ext4_read_block_bitmap_nowait()
|
||||
* @sb: super block
|
||||
* @block_group: given block group
|
||||
* @ignore_locked: ignore locked buffers
|
||||
*
|
||||
* Read the bitmap for a given block_group,and validate the
|
||||
* bits for block/inode/inode tables are set in the bitmaps
|
||||
|
@ -292,15 +292,10 @@ void ext4_release_system_zone(struct super_block *sb)
|
||||
call_rcu(&system_blks->rcu, ext4_destroy_system_zone);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if the passed-in block region (start_blk,
|
||||
* start_blk+count) is valid; 0 if some part of the block region
|
||||
* overlaps with some other filesystem metadata blocks.
|
||||
*/
|
||||
int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk,
|
||||
unsigned int count)
|
||||
int ext4_sb_block_valid(struct super_block *sb, struct inode *inode,
|
||||
ext4_fsblk_t start_blk, unsigned int count)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct ext4_system_blocks *system_blks;
|
||||
struct ext4_system_zone *entry;
|
||||
struct rb_node *n;
|
||||
@ -329,7 +324,9 @@ int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk,
|
||||
else if (start_blk >= (entry->start_blk + entry->count))
|
||||
n = n->rb_right;
|
||||
else {
|
||||
ret = (entry->ino == inode->i_ino);
|
||||
ret = 0;
|
||||
if (inode)
|
||||
ret = (entry->ino == inode->i_ino);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -338,6 +335,17 @@ int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if the passed-in block region (start_blk,
|
||||
* start_blk+count) is valid; 0 if some part of the block region
|
||||
* overlaps with some other filesystem metadata blocks.
|
||||
*/
|
||||
int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk,
|
||||
unsigned int count)
|
||||
{
|
||||
return ext4_sb_block_valid(inode->i_sb, inode, start_blk, count);
|
||||
}
|
||||
|
||||
int ext4_check_blockref(const char *function, unsigned int line,
|
||||
struct inode *inode, __le32 *p, unsigned int max)
|
||||
{
|
||||
|
@ -1046,6 +1046,8 @@ struct ext4_inode_info {
|
||||
|
||||
/* Fast commit related info */
|
||||
|
||||
/* For tracking dentry create updates */
|
||||
struct list_head i_fc_dilist;
|
||||
struct list_head i_fc_list; /*
|
||||
* inodes that need fast commit
|
||||
* protected by sbi->s_fc_lock.
|
||||
@ -1279,7 +1281,7 @@ struct ext4_inode_info {
|
||||
#define ext4_find_next_zero_bit find_next_zero_bit_le
|
||||
#define ext4_find_next_bit find_next_bit_le
|
||||
|
||||
extern void ext4_set_bits(void *bm, int cur, int len);
|
||||
extern void mb_set_bits(void *bm, int cur, int len);
|
||||
|
||||
/*
|
||||
* Maximal mount counts between two filesystem checks
|
||||
@ -3707,6 +3709,9 @@ extern int ext4_inode_block_valid(struct inode *inode,
|
||||
unsigned int count);
|
||||
extern int ext4_check_blockref(const char *, unsigned int,
|
||||
struct inode *, __le32 *, unsigned int);
|
||||
extern int ext4_sb_block_valid(struct super_block *sb, struct inode *inode,
|
||||
ext4_fsblk_t start_blk, unsigned int count);
|
||||
|
||||
|
||||
/* extents.c */
|
||||
struct ext4_ext_path;
|
||||
|
@ -3368,7 +3368,6 @@ static int ext4_split_extent(handle_t *handle,
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
unwritten = ext4_ext_is_unwritten(ex);
|
||||
split_flag1 = 0;
|
||||
|
||||
if (map->m_lblk >= ee_block) {
|
||||
split_flag1 = split_flag & EXT4_EXT_DATA_VALID2;
|
||||
|
@ -199,6 +199,7 @@ void ext4_fc_init_inode(struct inode *inode)
|
||||
ext4_fc_reset_inode(inode);
|
||||
ext4_clear_inode_state(inode, EXT4_STATE_FC_COMMITTING);
|
||||
INIT_LIST_HEAD(&ei->i_fc_list);
|
||||
INIT_LIST_HEAD(&ei->i_fc_dilist);
|
||||
init_waitqueue_head(&ei->i_fc_wait);
|
||||
atomic_set(&ei->i_fc_updates, 0);
|
||||
}
|
||||
@ -279,6 +280,8 @@ void ext4_fc_stop_update(struct inode *inode)
|
||||
void ext4_fc_del(struct inode *inode)
|
||||
{
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
struct ext4_fc_dentry_update *fc_dentry;
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY))
|
||||
@ -286,7 +289,7 @@ void ext4_fc_del(struct inode *inode)
|
||||
|
||||
restart:
|
||||
spin_lock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
||||
if (list_empty(&ei->i_fc_list)) {
|
||||
if (list_empty(&ei->i_fc_list) && list_empty(&ei->i_fc_dilist)) {
|
||||
spin_unlock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
||||
return;
|
||||
}
|
||||
@ -295,8 +298,33 @@ void ext4_fc_del(struct inode *inode)
|
||||
ext4_fc_wait_committing_inode(inode);
|
||||
goto restart;
|
||||
}
|
||||
list_del_init(&ei->i_fc_list);
|
||||
spin_unlock(&EXT4_SB(inode->i_sb)->s_fc_lock);
|
||||
|
||||
if (!list_empty(&ei->i_fc_list))
|
||||
list_del_init(&ei->i_fc_list);
|
||||
|
||||
/*
|
||||
* Since this inode is getting removed, let's also remove all FC
|
||||
* dentry create references, since it is not needed to log it anyways.
|
||||
*/
|
||||
if (list_empty(&ei->i_fc_dilist)) {
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
fc_dentry = list_first_entry(&ei->i_fc_dilist, struct ext4_fc_dentry_update, fcd_dilist);
|
||||
WARN_ON(fc_dentry->fcd_op != EXT4_FC_TAG_CREAT);
|
||||
list_del_init(&fc_dentry->fcd_list);
|
||||
list_del_init(&fc_dentry->fcd_dilist);
|
||||
|
||||
WARN_ON(!list_empty(&ei->i_fc_dilist));
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
|
||||
if (fc_dentry->fcd_name.name &&
|
||||
fc_dentry->fcd_name.len > DNAME_INLINE_LEN)
|
||||
kfree(fc_dentry->fcd_name.name);
|
||||
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -351,13 +379,6 @@ static int ext4_fc_track_template(
|
||||
tid_t tid = 0;
|
||||
int ret;
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return -EINVAL;
|
||||
|
||||
tid = handle->h_transaction->t_tid;
|
||||
mutex_lock(&ei->i_fc_lock);
|
||||
if (tid == ei->i_sync_tid) {
|
||||
@ -427,7 +448,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
|
||||
node->fcd_name.name = node->fcd_iname;
|
||||
}
|
||||
node->fcd_name.len = dentry->d_name.len;
|
||||
|
||||
INIT_LIST_HEAD(&node->fcd_dilist);
|
||||
spin_lock(&sbi->s_fc_lock);
|
||||
if (sbi->s_journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
|
||||
sbi->s_journal->j_flags & JBD2_FAST_COMMIT_ONGOING)
|
||||
@ -435,6 +456,20 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
|
||||
&sbi->s_fc_dentry_q[FC_Q_STAGING]);
|
||||
else
|
||||
list_add_tail(&node->fcd_list, &sbi->s_fc_dentry_q[FC_Q_MAIN]);
|
||||
|
||||
/*
|
||||
* This helps us keep a track of all fc_dentry updates which is part of
|
||||
* this ext4 inode. So in case the inode is getting unlinked, before
|
||||
* even we get a chance to fsync, we could remove all fc_dentry
|
||||
* references while evicting the inode in ext4_fc_del().
|
||||
* Also with this, we don't need to loop over all the inodes in
|
||||
* sbi->s_fc_q to get the corresponding inode in
|
||||
* ext4_fc_commit_dentry_updates().
|
||||
*/
|
||||
if (dentry_update->op == EXT4_FC_TAG_CREAT) {
|
||||
WARN_ON(!list_empty(&ei->i_fc_dilist));
|
||||
list_add_tail(&node->fcd_dilist, &ei->i_fc_dilist);
|
||||
}
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
mutex_lock(&ei->i_fc_lock);
|
||||
|
||||
@ -452,12 +487,22 @@ void __ext4_fc_track_unlink(handle_t *handle,
|
||||
|
||||
ret = ext4_fc_track_template(handle, inode, __track_dentry_update,
|
||||
(void *)&args, 0);
|
||||
trace_ext4_fc_track_unlink(inode, dentry, ret);
|
||||
trace_ext4_fc_track_unlink(handle, inode, dentry, ret);
|
||||
}
|
||||
|
||||
void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry)
|
||||
{
|
||||
__ext4_fc_track_unlink(handle, d_inode(dentry), dentry);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return;
|
||||
|
||||
__ext4_fc_track_unlink(handle, inode, dentry);
|
||||
}
|
||||
|
||||
void __ext4_fc_track_link(handle_t *handle,
|
||||
@ -471,12 +516,22 @@ void __ext4_fc_track_link(handle_t *handle,
|
||||
|
||||
ret = ext4_fc_track_template(handle, inode, __track_dentry_update,
|
||||
(void *)&args, 0);
|
||||
trace_ext4_fc_track_link(inode, dentry, ret);
|
||||
trace_ext4_fc_track_link(handle, inode, dentry, ret);
|
||||
}
|
||||
|
||||
void ext4_fc_track_link(handle_t *handle, struct dentry *dentry)
|
||||
{
|
||||
__ext4_fc_track_link(handle, d_inode(dentry), dentry);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return;
|
||||
|
||||
__ext4_fc_track_link(handle, inode, dentry);
|
||||
}
|
||||
|
||||
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
|
||||
@ -490,12 +545,22 @@ void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
|
||||
|
||||
ret = ext4_fc_track_template(handle, inode, __track_dentry_update,
|
||||
(void *)&args, 0);
|
||||
trace_ext4_fc_track_create(inode, dentry, ret);
|
||||
trace_ext4_fc_track_create(handle, inode, dentry, ret);
|
||||
}
|
||||
|
||||
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
|
||||
{
|
||||
__ext4_fc_track_create(handle, d_inode(dentry), dentry);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return;
|
||||
|
||||
__ext4_fc_track_create(handle, inode, dentry);
|
||||
}
|
||||
|
||||
/* __track_fn for inode tracking */
|
||||
@ -511,6 +576,7 @@ static int __track_inode(struct inode *inode, void *arg, bool update)
|
||||
|
||||
void ext4_fc_track_inode(handle_t *handle, struct inode *inode)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
int ret;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
@ -522,8 +588,15 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return;
|
||||
|
||||
ret = ext4_fc_track_template(handle, inode, __track_inode, NULL, 1);
|
||||
trace_ext4_fc_track_inode(inode, ret);
|
||||
trace_ext4_fc_track_inode(handle, inode, ret);
|
||||
}
|
||||
|
||||
struct __track_range_args {
|
||||
@ -561,18 +634,26 @@ static int __track_range(struct inode *inode, void *arg, bool update)
|
||||
void ext4_fc_track_range(handle_t *handle, struct inode *inode, ext4_lblk_t start,
|
||||
ext4_lblk_t end)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
struct __track_range_args args;
|
||||
int ret;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
return;
|
||||
|
||||
if (!test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT) ||
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return;
|
||||
|
||||
args.start = start;
|
||||
args.end = end;
|
||||
|
||||
ret = ext4_fc_track_template(handle, inode, __track_range, &args, 1);
|
||||
|
||||
trace_ext4_fc_track_range(inode, start, end, ret);
|
||||
trace_ext4_fc_track_range(handle, inode, start, end, ret);
|
||||
}
|
||||
|
||||
static void ext4_fc_submit_bh(struct super_block *sb, bool is_tail)
|
||||
@ -954,7 +1035,7 @@ __releases(&sbi->s_fc_lock)
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct ext4_fc_dentry_update *fc_dentry, *fc_dentry_n;
|
||||
struct inode *inode;
|
||||
struct ext4_inode_info *ei, *ei_n;
|
||||
struct ext4_inode_info *ei;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&sbi->s_fc_dentry_q[FC_Q_MAIN]))
|
||||
@ -970,21 +1051,16 @@ __releases(&sbi->s_fc_lock)
|
||||
spin_lock(&sbi->s_fc_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
inode = NULL;
|
||||
list_for_each_entry_safe(ei, ei_n, &sbi->s_fc_q[FC_Q_MAIN],
|
||||
i_fc_list) {
|
||||
if (ei->vfs_inode.i_ino == fc_dentry->fcd_ino) {
|
||||
inode = &ei->vfs_inode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we don't find inode in our list, then it was deleted,
|
||||
* in which case, we don't need to record it's create tag.
|
||||
* With fcd_dilist we need not loop in sbi->s_fc_q to get the
|
||||
* corresponding inode pointer
|
||||
*/
|
||||
if (!inode)
|
||||
continue;
|
||||
WARN_ON(list_empty(&fc_dentry->fcd_dilist));
|
||||
ei = list_first_entry(&fc_dentry->fcd_dilist,
|
||||
struct ext4_inode_info, i_fc_dilist);
|
||||
inode = &ei->vfs_inode;
|
||||
WARN_ON(inode->i_ino != fc_dentry->fcd_ino);
|
||||
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
|
||||
/*
|
||||
@ -1088,11 +1164,12 @@ static int ext4_fc_perform_commit(journal_t *journal)
|
||||
}
|
||||
|
||||
static void ext4_fc_update_stats(struct super_block *sb, int status,
|
||||
u64 commit_time, int nblks)
|
||||
u64 commit_time, int nblks, tid_t commit_tid)
|
||||
{
|
||||
struct ext4_fc_stats *stats = &EXT4_SB(sb)->s_fc_stats;
|
||||
|
||||
jbd_debug(1, "Fast commit ended with status = %d", status);
|
||||
jbd_debug(1, "Fast commit ended with status = %d for tid %u",
|
||||
status, commit_tid);
|
||||
if (status == EXT4_FC_STATUS_OK) {
|
||||
stats->fc_num_commits++;
|
||||
stats->fc_numblks += nblks;
|
||||
@ -1110,7 +1187,7 @@ static void ext4_fc_update_stats(struct super_block *sb, int status,
|
||||
} else {
|
||||
stats->fc_skipped_commits++;
|
||||
}
|
||||
trace_ext4_fc_commit_stop(sb, nblks, status);
|
||||
trace_ext4_fc_commit_stop(sb, nblks, status, commit_tid);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1128,13 +1205,13 @@ int ext4_fc_commit(journal_t *journal, tid_t commit_tid)
|
||||
int status = EXT4_FC_STATUS_OK, fc_bufs_before = 0;
|
||||
ktime_t start_time, commit_time;
|
||||
|
||||
trace_ext4_fc_commit_start(sb);
|
||||
|
||||
start_time = ktime_get();
|
||||
|
||||
if (!test_opt2(sb, JOURNAL_FAST_COMMIT))
|
||||
return jbd2_complete_transaction(journal, commit_tid);
|
||||
|
||||
trace_ext4_fc_commit_start(sb, commit_tid);
|
||||
|
||||
start_time = ktime_get();
|
||||
|
||||
restart_fc:
|
||||
ret = jbd2_fc_begin_commit(journal, commit_tid);
|
||||
if (ret == -EALREADY) {
|
||||
@ -1142,14 +1219,16 @@ int ext4_fc_commit(journal_t *journal, tid_t commit_tid)
|
||||
if (atomic_read(&sbi->s_fc_subtid) <= subtid &&
|
||||
commit_tid > journal->j_commit_sequence)
|
||||
goto restart_fc;
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_SKIPPED, 0, 0);
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_SKIPPED, 0, 0,
|
||||
commit_tid);
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
/*
|
||||
* Commit couldn't start. Just update stats and perform a
|
||||
* full commit.
|
||||
*/
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_FAILED, 0, 0);
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_FAILED, 0, 0,
|
||||
commit_tid);
|
||||
return jbd2_complete_transaction(journal, commit_tid);
|
||||
}
|
||||
|
||||
@ -1181,12 +1260,12 @@ int ext4_fc_commit(journal_t *journal, tid_t commit_tid)
|
||||
* don't react too strongly to vast changes in the commit time
|
||||
*/
|
||||
commit_time = ktime_to_ns(ktime_sub(ktime_get(), start_time));
|
||||
ext4_fc_update_stats(sb, status, commit_time, nblks);
|
||||
ext4_fc_update_stats(sb, status, commit_time, nblks, commit_tid);
|
||||
return ret;
|
||||
|
||||
fallback:
|
||||
ret = jbd2_fc_end_commit_fallback(journal);
|
||||
ext4_fc_update_stats(sb, status, 0, 0);
|
||||
ext4_fc_update_stats(sb, status, 0, 0, commit_tid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1204,6 +1283,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid)
|
||||
if (full && sbi->s_fc_bh)
|
||||
sbi->s_fc_bh = NULL;
|
||||
|
||||
trace_ext4_fc_cleanup(journal, full, tid);
|
||||
jbd2_fc_release_bufs(journal);
|
||||
|
||||
spin_lock(&sbi->s_fc_lock);
|
||||
@ -1228,6 +1308,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid)
|
||||
struct ext4_fc_dentry_update,
|
||||
fcd_list);
|
||||
list_del_init(&fc_dentry->fcd_list);
|
||||
list_del_init(&fc_dentry->fcd_dilist);
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
|
||||
if (fc_dentry->fcd_name.name &&
|
||||
@ -1875,8 +1956,8 @@ bool ext4_fc_replay_check_excluded(struct super_block *sb, ext4_fsblk_t blk)
|
||||
if (state->fc_regions[i].ino == 0 ||
|
||||
state->fc_regions[i].len == 0)
|
||||
continue;
|
||||
if (blk >= state->fc_regions[i].pblk &&
|
||||
blk < state->fc_regions[i].pblk + state->fc_regions[i].len)
|
||||
if (in_range(blk, state->fc_regions[i].pblk,
|
||||
state->fc_regions[i].len))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -93,7 +93,6 @@ enum {
|
||||
EXT4_FC_REASON_RENAME_DIR,
|
||||
EXT4_FC_REASON_FALLOC_RANGE,
|
||||
EXT4_FC_REASON_INODE_JOURNAL_DATA,
|
||||
EXT4_FC_COMMIT_FAILED,
|
||||
EXT4_FC_REASON_MAX
|
||||
};
|
||||
|
||||
@ -109,6 +108,7 @@ struct ext4_fc_dentry_update {
|
||||
struct qstr fcd_name; /* Dirent name */
|
||||
unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */
|
||||
struct list_head fcd_list;
|
||||
struct list_head fcd_dilist;
|
||||
};
|
||||
|
||||
struct ext4_fc_stats {
|
||||
|
@ -1783,19 +1783,20 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
|
||||
void *inline_pos;
|
||||
unsigned int offset;
|
||||
struct ext4_dir_entry_2 *de;
|
||||
bool ret = true;
|
||||
bool ret = false;
|
||||
|
||||
err = ext4_get_inode_loc(dir, &iloc);
|
||||
if (err) {
|
||||
EXT4_ERROR_INODE_ERR(dir, -err,
|
||||
"error %d getting inode %lu block",
|
||||
err, dir->i_ino);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
down_read(&EXT4_I(dir)->xattr_sem);
|
||||
if (!ext4_has_inline_data(dir)) {
|
||||
*has_inline_data = 0;
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1804,7 +1805,6 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
|
||||
ext4_warning(dir->i_sb,
|
||||
"bad inline directory (dir #%lu) - no `..'",
|
||||
dir->i_ino);
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1823,16 +1823,15 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
|
||||
dir->i_ino, le32_to_cpu(de->inode),
|
||||
le16_to_cpu(de->rec_len), de->name_len,
|
||||
inline_size);
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
if (le32_to_cpu(de->inode)) {
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
offset += ext4_rec_len_from_disk(de->rec_len, inline_size);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
out:
|
||||
up_read(&EXT4_I(dir)->xattr_sem);
|
||||
brelse(iloc.bh);
|
||||
|
@ -1993,6 +1993,15 @@ static int ext4_writepage(struct page *page,
|
||||
else
|
||||
len = PAGE_SIZE;
|
||||
|
||||
/* Should never happen but for bugs in other kernel subsystems */
|
||||
if (!page_has_buffers(page)) {
|
||||
ext4_warning_inode(inode,
|
||||
"page %lu does not have buffers attached", page->index);
|
||||
ClearPageDirty(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
page_bufs = page_buffers(page);
|
||||
/*
|
||||
* We cannot do block allocation or other extent handling in this
|
||||
@ -2594,6 +2603,22 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
|
||||
wait_on_page_writeback(page);
|
||||
BUG_ON(PageWriteback(page));
|
||||
|
||||
/*
|
||||
* Should never happen but for buggy code in
|
||||
* other subsystems that call
|
||||
* set_page_dirty() without properly warning
|
||||
* the file system first. See [1] for more
|
||||
* information.
|
||||
*
|
||||
* [1] https://lore.kernel.org/linux-mm/20180103100430.GE4911@quack2.suse.cz
|
||||
*/
|
||||
if (!page_has_buffers(page)) {
|
||||
ext4_warning_inode(mpd->inode, "page %lu does not have buffers attached", page->index);
|
||||
ClearPageDirty(page);
|
||||
unlock_page(page);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mpd->map.m_len == 0)
|
||||
mpd->first_page = page->index;
|
||||
mpd->next_page = page->index + 1;
|
||||
@ -3548,10 +3573,11 @@ const struct iomap_ops ext4_iomap_report_ops = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Pages can be marked dirty completely asynchronously from ext4's journalling
|
||||
* activity. By filemap_sync_pte(), try_to_unmap_one(), etc. We cannot do
|
||||
* much here because ->set_page_dirty is called under VFS locks. The page is
|
||||
* not necessarily locked.
|
||||
* Whenever the page is being dirtied, corresponding buffers should already be
|
||||
* attached to the transaction (we take care of this in ext4_page_mkwrite() and
|
||||
* ext4_write_begin()). However we cannot move buffers to dirty transaction
|
||||
* lists here because ->set_page_dirty is called under VFS locks and the page
|
||||
* is not necessarily locked.
|
||||
*
|
||||
* We cannot just dirty the page and leave attached buffers clean, because the
|
||||
* buffers' dirty state is "definitive". We cannot just set the buffers dirty
|
||||
@ -3562,6 +3588,7 @@ const struct iomap_ops ext4_iomap_report_ops = {
|
||||
*/
|
||||
static int ext4_journalled_set_page_dirty(struct page *page)
|
||||
{
|
||||
WARN_ON_ONCE(!page_has_buffers(page));
|
||||
SetPageChecked(page);
|
||||
return __set_page_dirty_nobuffers(page);
|
||||
}
|
||||
|
@ -269,7 +269,7 @@ int ext4_update_superblocks_fn(struct super_block *sb,
|
||||
return err ? err : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Swap memory between @a and @b for @len bytes.
|
||||
*
|
||||
* @a: pointer to first memory area
|
||||
@ -290,7 +290,7 @@ static void memswap(void *a, void *b, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Swap i_data and associated attributes between @inode1 and @inode2.
|
||||
* This function is used for the primary swap between inode1 and inode2
|
||||
* and also to revert this primary swap in case of errors.
|
||||
@ -344,7 +344,7 @@ void ext4_reset_inode_seed(struct inode *inode)
|
||||
ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, sizeof(gen));
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Swap the information from the given @inode and the inode
|
||||
* EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other
|
||||
* important fields of the inodes.
|
||||
|
@ -1000,7 +1000,7 @@ static inline int should_optimize_scan(struct ext4_allocation_context *ac)
|
||||
return 0;
|
||||
if (ac->ac_criteria >= 2)
|
||||
return 0;
|
||||
if (ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))
|
||||
if (!ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -1689,7 +1689,7 @@ static int mb_test_and_clear_bits(void *bm, int cur, int len)
|
||||
return zero_bit;
|
||||
}
|
||||
|
||||
void ext4_set_bits(void *bm, int cur, int len)
|
||||
void mb_set_bits(void *bm, int cur, int len)
|
||||
{
|
||||
__u32 *addr;
|
||||
|
||||
@ -1996,7 +1996,7 @@ static int mb_mark_used(struct ext4_buddy *e4b, struct ext4_free_extent *ex)
|
||||
mb_set_largest_free_order(e4b->bd_sb, e4b->bd_info);
|
||||
|
||||
mb_update_avg_fragment_size(e4b->bd_sb, e4b->bd_info);
|
||||
ext4_set_bits(e4b->bd_bitmap, ex->fe_start, len0);
|
||||
mb_set_bits(e4b->bd_bitmap, ex->fe_start, len0);
|
||||
mb_check_buddy(e4b);
|
||||
|
||||
return ret;
|
||||
@ -3825,7 +3825,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
|
||||
* We leak some of the blocks here.
|
||||
*/
|
||||
ext4_lock_group(sb, ac->ac_b_ex.fe_group);
|
||||
ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
|
||||
mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
|
||||
ac->ac_b_ex.fe_len);
|
||||
ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
|
||||
@ -3844,7 +3844,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
|
||||
mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
|
||||
ac->ac_b_ex.fe_len);
|
||||
if (ext4_has_group_desc_csum(sb) &&
|
||||
(gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
|
||||
@ -3899,69 +3899,103 @@ void ext4_mb_mark_bb(struct super_block *sb, ext4_fsblk_t block,
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
ext4_group_t group;
|
||||
ext4_grpblk_t blkoff;
|
||||
int i, clen, err;
|
||||
int i, err;
|
||||
int already;
|
||||
unsigned int clen, clen_changed, thisgrp_len;
|
||||
|
||||
clen = EXT4_B2C(sbi, len);
|
||||
while (len > 0) {
|
||||
ext4_get_group_no_and_offset(sb, block, &group, &blkoff);
|
||||
|
||||
ext4_get_group_no_and_offset(sb, block, &group, &blkoff);
|
||||
bitmap_bh = ext4_read_block_bitmap(sb, group);
|
||||
if (IS_ERR(bitmap_bh)) {
|
||||
err = PTR_ERR(bitmap_bh);
|
||||
bitmap_bh = NULL;
|
||||
goto out_err;
|
||||
/*
|
||||
* Check to see if we are freeing blocks across a group
|
||||
* boundary.
|
||||
* In case of flex_bg, this can happen that (block, len) may
|
||||
* span across more than one group. In that case we need to
|
||||
* get the corresponding group metadata to work with.
|
||||
* For this we have goto again loop.
|
||||
*/
|
||||
thisgrp_len = min_t(unsigned int, (unsigned int)len,
|
||||
EXT4_BLOCKS_PER_GROUP(sb) - EXT4_C2B(sbi, blkoff));
|
||||
clen = EXT4_NUM_B2C(sbi, thisgrp_len);
|
||||
|
||||
if (!ext4_sb_block_valid(sb, NULL, block, thisgrp_len)) {
|
||||
ext4_error(sb, "Marking blocks in system zone - "
|
||||
"Block = %llu, len = %u",
|
||||
block, thisgrp_len);
|
||||
bitmap_bh = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
bitmap_bh = ext4_read_block_bitmap(sb, group);
|
||||
if (IS_ERR(bitmap_bh)) {
|
||||
err = PTR_ERR(bitmap_bh);
|
||||
bitmap_bh = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
err = -EIO;
|
||||
gdp = ext4_get_group_desc(sb, group, &gdp_bh);
|
||||
if (!gdp)
|
||||
break;
|
||||
|
||||
ext4_lock_group(sb, group);
|
||||
already = 0;
|
||||
for (i = 0; i < clen; i++)
|
||||
if (!mb_test_bit(blkoff + i, bitmap_bh->b_data) ==
|
||||
!state)
|
||||
already++;
|
||||
|
||||
clen_changed = clen - already;
|
||||
if (state)
|
||||
mb_set_bits(bitmap_bh->b_data, blkoff, clen);
|
||||
else
|
||||
mb_clear_bits(bitmap_bh->b_data, blkoff, clen);
|
||||
if (ext4_has_group_desc_csum(sb) &&
|
||||
(gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
|
||||
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
|
||||
ext4_free_group_clusters_set(sb, gdp,
|
||||
ext4_free_clusters_after_init(sb, group, gdp));
|
||||
}
|
||||
if (state)
|
||||
clen = ext4_free_group_clusters(sb, gdp) - clen_changed;
|
||||
else
|
||||
clen = ext4_free_group_clusters(sb, gdp) + clen_changed;
|
||||
|
||||
ext4_free_group_clusters_set(sb, gdp, clen);
|
||||
ext4_block_bitmap_csum_set(sb, group, gdp, bitmap_bh);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
|
||||
ext4_unlock_group(sb, group);
|
||||
|
||||
if (sbi->s_log_groups_per_flex) {
|
||||
ext4_group_t flex_group = ext4_flex_group(sbi, group);
|
||||
struct flex_groups *fg = sbi_array_rcu_deref(sbi,
|
||||
s_flex_groups, flex_group);
|
||||
|
||||
if (state)
|
||||
atomic64_sub(clen_changed, &fg->free_clusters);
|
||||
else
|
||||
atomic64_add(clen_changed, &fg->free_clusters);
|
||||
|
||||
}
|
||||
|
||||
err = ext4_handle_dirty_metadata(NULL, NULL, bitmap_bh);
|
||||
if (err)
|
||||
break;
|
||||
sync_dirty_buffer(bitmap_bh);
|
||||
err = ext4_handle_dirty_metadata(NULL, NULL, gdp_bh);
|
||||
sync_dirty_buffer(gdp_bh);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
block += thisgrp_len;
|
||||
len -= thisgrp_len;
|
||||
brelse(bitmap_bh);
|
||||
BUG_ON(len < 0);
|
||||
}
|
||||
|
||||
err = -EIO;
|
||||
gdp = ext4_get_group_desc(sb, group, &gdp_bh);
|
||||
if (!gdp)
|
||||
goto out_err;
|
||||
|
||||
ext4_lock_group(sb, group);
|
||||
already = 0;
|
||||
for (i = 0; i < clen; i++)
|
||||
if (!mb_test_bit(blkoff + i, bitmap_bh->b_data) == !state)
|
||||
already++;
|
||||
|
||||
if (state)
|
||||
ext4_set_bits(bitmap_bh->b_data, blkoff, clen);
|
||||
else
|
||||
mb_test_and_clear_bits(bitmap_bh->b_data, blkoff, clen);
|
||||
if (ext4_has_group_desc_csum(sb) &&
|
||||
(gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) {
|
||||
gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
|
||||
ext4_free_group_clusters_set(sb, gdp,
|
||||
ext4_free_clusters_after_init(sb,
|
||||
group, gdp));
|
||||
}
|
||||
if (state)
|
||||
clen = ext4_free_group_clusters(sb, gdp) - clen + already;
|
||||
else
|
||||
clen = ext4_free_group_clusters(sb, gdp) + clen - already;
|
||||
|
||||
ext4_free_group_clusters_set(sb, gdp, clen);
|
||||
ext4_block_bitmap_csum_set(sb, group, gdp, bitmap_bh);
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
|
||||
ext4_unlock_group(sb, group);
|
||||
|
||||
if (sbi->s_log_groups_per_flex) {
|
||||
ext4_group_t flex_group = ext4_flex_group(sbi, group);
|
||||
|
||||
atomic64_sub(len,
|
||||
&sbi_array_rcu_deref(sbi, s_flex_groups,
|
||||
flex_group)->free_clusters);
|
||||
}
|
||||
|
||||
err = ext4_handle_dirty_metadata(NULL, NULL, bitmap_bh);
|
||||
if (err)
|
||||
goto out_err;
|
||||
sync_dirty_buffer(bitmap_bh);
|
||||
err = ext4_handle_dirty_metadata(NULL, NULL, gdp_bh);
|
||||
sync_dirty_buffer(gdp_bh);
|
||||
|
||||
out_err:
|
||||
brelse(bitmap_bh);
|
||||
brelse(bitmap_bh);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4433,7 +4467,7 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
|
||||
|
||||
while (n) {
|
||||
entry = rb_entry(n, struct ext4_free_data, efd_node);
|
||||
ext4_set_bits(bitmap, entry->efd_start_cluster, entry->efd_count);
|
||||
mb_set_bits(bitmap, entry->efd_start_cluster, entry->efd_count);
|
||||
n = rb_next(n);
|
||||
}
|
||||
return;
|
||||
@ -4474,7 +4508,7 @@ void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
|
||||
if (unlikely(len == 0))
|
||||
continue;
|
||||
BUG_ON(groupnr != group);
|
||||
ext4_set_bits(bitmap, start, len);
|
||||
mb_set_bits(bitmap, start, len);
|
||||
preallocated += len;
|
||||
}
|
||||
mb_debug(sb, "preallocated %d for group %u\n", preallocated, group);
|
||||
@ -5846,17 +5880,17 @@ static void ext4_free_blocks_simple(struct inode *inode, ext4_fsblk_t block,
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_free_blocks() -- Free given blocks and update quota
|
||||
* ext4_mb_clear_bb() -- helper function for freeing blocks.
|
||||
* Used by ext4_free_blocks()
|
||||
* @handle: handle for this transaction
|
||||
* @inode: inode
|
||||
* @bh: optional buffer of the block to be freed
|
||||
* @block: starting physical block to be freed
|
||||
* @count: number of blocks to be freed
|
||||
* @flags: flags used by ext4_free_blocks
|
||||
*/
|
||||
void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh, ext4_fsblk_t block,
|
||||
unsigned long count, int flags)
|
||||
static void ext4_mb_clear_bb(handle_t *handle, struct inode *inode,
|
||||
ext4_fsblk_t block, unsigned long count,
|
||||
int flags)
|
||||
{
|
||||
struct buffer_head *bitmap_bh = NULL;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@ -5873,80 +5907,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
|
||||
sbi = EXT4_SB(sb);
|
||||
|
||||
if (sbi->s_mount_state & EXT4_FC_REPLAY) {
|
||||
ext4_free_blocks_simple(inode, block, count);
|
||||
return;
|
||||
}
|
||||
|
||||
might_sleep();
|
||||
if (bh) {
|
||||
if (block)
|
||||
BUG_ON(block != bh->b_blocknr);
|
||||
else
|
||||
block = bh->b_blocknr;
|
||||
}
|
||||
|
||||
if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) &&
|
||||
!ext4_inode_block_valid(inode, block, count)) {
|
||||
ext4_error(sb, "Freeing blocks not in datazone - "
|
||||
"block = %llu, count = %lu", block, count);
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
ext4_debug("freeing block %llu\n", block);
|
||||
trace_ext4_free_blocks(inode, block, count, flags);
|
||||
|
||||
if (bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
|
||||
BUG_ON(count > 1);
|
||||
|
||||
ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
|
||||
inode, bh, block);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the extent to be freed does not begin on a cluster
|
||||
* boundary, we need to deal with partial clusters at the
|
||||
* beginning and end of the extent. Normally we will free
|
||||
* blocks at the beginning or the end unless we are explicitly
|
||||
* requested to avoid doing so.
|
||||
*/
|
||||
overflow = EXT4_PBLK_COFF(sbi, block);
|
||||
if (overflow) {
|
||||
if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
|
||||
overflow = sbi->s_cluster_ratio - overflow;
|
||||
block += overflow;
|
||||
if (count > overflow)
|
||||
count -= overflow;
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
block -= overflow;
|
||||
count += overflow;
|
||||
}
|
||||
}
|
||||
overflow = EXT4_LBLK_COFF(sbi, count);
|
||||
if (overflow) {
|
||||
if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
|
||||
if (count > overflow)
|
||||
count -= overflow;
|
||||
else
|
||||
return;
|
||||
} else
|
||||
count += sbi->s_cluster_ratio - overflow;
|
||||
}
|
||||
|
||||
if (!bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
|
||||
int i;
|
||||
int is_metadata = flags & EXT4_FREE_BLOCKS_METADATA;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
cond_resched();
|
||||
if (is_metadata)
|
||||
bh = sb_find_get_block(inode->i_sb, block + i);
|
||||
ext4_forget(handle, is_metadata, inode, bh, block + i);
|
||||
}
|
||||
}
|
||||
|
||||
do_more:
|
||||
overflow = 0;
|
||||
ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
|
||||
@ -5977,13 +5937,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
if (in_range(ext4_block_bitmap(sb, gdp), block, count) ||
|
||||
in_range(ext4_inode_bitmap(sb, gdp), block, count) ||
|
||||
in_range(block, ext4_inode_table(sb, gdp),
|
||||
sbi->s_itb_per_group) ||
|
||||
in_range(block + count - 1, ext4_inode_table(sb, gdp),
|
||||
sbi->s_itb_per_group)) {
|
||||
|
||||
if (!ext4_inode_block_valid(inode, block, count)) {
|
||||
ext4_error(sb, "Freeing blocks in system zone - "
|
||||
"Block = %llu, count = %lu", block, count);
|
||||
/* err = 0. ext4_std_error should be a no op */
|
||||
@ -6054,7 +6008,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
NULL);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
ext4_msg(sb, KERN_WARNING, "discard request in"
|
||||
" group:%d block:%d count:%lu failed"
|
||||
" group:%u block:%d count:%lu failed"
|
||||
" with %d", block_group, bit, count,
|
||||
err);
|
||||
} else
|
||||
@ -6114,6 +6068,103 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_free_blocks() -- Free given blocks and update quota
|
||||
* @handle: handle for this transaction
|
||||
* @inode: inode
|
||||
* @bh: optional buffer of the block to be freed
|
||||
* @block: starting physical block to be freed
|
||||
* @count: number of blocks to be freed
|
||||
* @flags: flags used by ext4_free_blocks
|
||||
*/
|
||||
void ext4_free_blocks(handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh, ext4_fsblk_t block,
|
||||
unsigned long count, int flags)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
unsigned int overflow;
|
||||
struct ext4_sb_info *sbi;
|
||||
|
||||
sbi = EXT4_SB(sb);
|
||||
|
||||
if (sbi->s_mount_state & EXT4_FC_REPLAY) {
|
||||
ext4_free_blocks_simple(inode, block, count);
|
||||
return;
|
||||
}
|
||||
|
||||
might_sleep();
|
||||
if (bh) {
|
||||
if (block)
|
||||
BUG_ON(block != bh->b_blocknr);
|
||||
else
|
||||
block = bh->b_blocknr;
|
||||
}
|
||||
|
||||
if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) &&
|
||||
!ext4_inode_block_valid(inode, block, count)) {
|
||||
ext4_error(sb, "Freeing blocks not in datazone - "
|
||||
"block = %llu, count = %lu", block, count);
|
||||
return;
|
||||
}
|
||||
|
||||
ext4_debug("freeing block %llu\n", block);
|
||||
trace_ext4_free_blocks(inode, block, count, flags);
|
||||
|
||||
if (bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
|
||||
BUG_ON(count > 1);
|
||||
|
||||
ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
|
||||
inode, bh, block);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the extent to be freed does not begin on a cluster
|
||||
* boundary, we need to deal with partial clusters at the
|
||||
* beginning and end of the extent. Normally we will free
|
||||
* blocks at the beginning or the end unless we are explicitly
|
||||
* requested to avoid doing so.
|
||||
*/
|
||||
overflow = EXT4_PBLK_COFF(sbi, block);
|
||||
if (overflow) {
|
||||
if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
|
||||
overflow = sbi->s_cluster_ratio - overflow;
|
||||
block += overflow;
|
||||
if (count > overflow)
|
||||
count -= overflow;
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
block -= overflow;
|
||||
count += overflow;
|
||||
}
|
||||
}
|
||||
overflow = EXT4_LBLK_COFF(sbi, count);
|
||||
if (overflow) {
|
||||
if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
|
||||
if (count > overflow)
|
||||
count -= overflow;
|
||||
else
|
||||
return;
|
||||
} else
|
||||
count += sbi->s_cluster_ratio - overflow;
|
||||
}
|
||||
|
||||
if (!bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
|
||||
int i;
|
||||
int is_metadata = flags & EXT4_FREE_BLOCKS_METADATA;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
cond_resched();
|
||||
if (is_metadata)
|
||||
bh = sb_find_get_block(inode->i_sb, block + i);
|
||||
ext4_forget(handle, is_metadata, inode, bh, block + i);
|
||||
}
|
||||
}
|
||||
|
||||
ext4_mb_clear_bb(handle, inode, block, count, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext4_group_add_blocks() -- Add given blocks to an existing group
|
||||
* @handle: handle to this transaction
|
||||
@ -6170,11 +6221,7 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
if (in_range(ext4_block_bitmap(sb, desc), block, count) ||
|
||||
in_range(ext4_inode_bitmap(sb, desc), block, count) ||
|
||||
in_range(block, ext4_inode_table(sb, desc), sbi->s_itb_per_group) ||
|
||||
in_range(block + count - 1, ext4_inode_table(sb, desc),
|
||||
sbi->s_itb_per_group)) {
|
||||
if (!ext4_sb_block_valid(sb, NULL, block, count)) {
|
||||
ext4_error(sb, "Adding blocks in system zones - "
|
||||
"Block = %llu, count = %lu",
|
||||
block, count);
|
||||
|
@ -2997,14 +2997,14 @@ bool ext4_empty_dir(struct inode *inode)
|
||||
if (inode->i_size < ext4_dir_rec_len(1, NULL) +
|
||||
ext4_dir_rec_len(2, NULL)) {
|
||||
EXT4_ERROR_INODE(inode, "invalid size");
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
/* The first directory block must not be a hole,
|
||||
* so treat it as DIRENT_HTREE
|
||||
*/
|
||||
bh = ext4_read_dirblock(inode, 0, DIRENT_HTREE);
|
||||
if (IS_ERR(bh))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
de = (struct ext4_dir_entry_2 *) bh->b_data;
|
||||
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
|
||||
@ -3012,7 +3012,7 @@ bool ext4_empty_dir(struct inode *inode)
|
||||
le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) {
|
||||
ext4_warning_inode(inode, "directory missing '.'");
|
||||
brelse(bh);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
|
||||
de = ext4_next_entry(de, sb->s_blocksize);
|
||||
@ -3021,7 +3021,7 @@ bool ext4_empty_dir(struct inode *inode)
|
||||
le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) {
|
||||
ext4_warning_inode(inode, "directory missing '..'");
|
||||
brelse(bh);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
|
||||
while (offset < inode->i_size) {
|
||||
@ -3035,7 +3035,7 @@ bool ext4_empty_dir(struct inode *inode)
|
||||
continue;
|
||||
}
|
||||
if (IS_ERR(bh))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
de = (struct ext4_dir_entry_2 *) (bh->b_data +
|
||||
(offset & (sb->s_blocksize - 1)));
|
||||
@ -3891,12 +3891,19 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
|
||||
ext4_fc_mark_ineligible(old.inode->i_sb,
|
||||
EXT4_FC_REASON_RENAME_DIR, handle);
|
||||
} else {
|
||||
struct super_block *sb = old.inode->i_sb;
|
||||
|
||||
if (new.inode)
|
||||
ext4_fc_track_unlink(handle, new.dentry);
|
||||
__ext4_fc_track_link(handle, old.inode, new.dentry);
|
||||
__ext4_fc_track_unlink(handle, old.inode, old.dentry);
|
||||
if (whiteout)
|
||||
__ext4_fc_track_create(handle, whiteout, old.dentry);
|
||||
if (test_opt2(sb, JOURNAL_FAST_COMMIT) &&
|
||||
!(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY) &&
|
||||
!(ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE))) {
|
||||
__ext4_fc_track_link(handle, old.inode, new.dentry);
|
||||
__ext4_fc_track_unlink(handle, old.inode, old.dentry);
|
||||
if (whiteout)
|
||||
__ext4_fc_track_create(handle, whiteout,
|
||||
old.dentry);
|
||||
}
|
||||
}
|
||||
|
||||
if (new.inode) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
|
||||
@ -483,7 +484,7 @@ static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle,
|
||||
}
|
||||
ext4_debug("mark block bitmap %#04llx (+%llu/%u)\n",
|
||||
first_cluster, first_cluster - start, count2);
|
||||
ext4_set_bits(bh->b_data, first_cluster - start, count2);
|
||||
mb_set_bits(bh->b_data, first_cluster - start, count2);
|
||||
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, bh);
|
||||
brelse(bh);
|
||||
@ -632,7 +633,7 @@ static int setup_new_flex_group_blocks(struct super_block *sb,
|
||||
if (overhead != 0) {
|
||||
ext4_debug("mark backup superblock %#04llx (+0)\n",
|
||||
start);
|
||||
ext4_set_bits(bh->b_data, 0,
|
||||
mb_set_bits(bh->b_data, 0,
|
||||
EXT4_NUM_B2C(sbi, overhead));
|
||||
}
|
||||
ext4_mark_bitmap_end(EXT4_B2C(sbi, group_data[i].blocks_count),
|
||||
@ -2100,7 +2101,7 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
|
||||
*/
|
||||
while (ext4_setup_next_flex_gd(sb, flex_gd, n_blocks_count,
|
||||
flexbg_size)) {
|
||||
if (jiffies - last_update_time > HZ * 10) {
|
||||
if (time_is_before_jiffies(last_update_time + HZ * 10)) {
|
||||
if (last_update_time)
|
||||
ext4_msg(sb, KERN_INFO,
|
||||
"resized to %llu blocks",
|
||||
|
@ -2021,12 +2021,12 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
|
||||
#define EXT4_SPEC_s_commit_interval (1 << 16)
|
||||
#define EXT4_SPEC_s_fc_debug_max_replay (1 << 17)
|
||||
#define EXT4_SPEC_s_sb_block (1 << 18)
|
||||
#define EXT4_SPEC_mb_optimize_scan (1 << 19)
|
||||
|
||||
struct ext4_fs_context {
|
||||
char *s_qf_names[EXT4_MAXQUOTAS];
|
||||
char *test_dummy_enc_arg;
|
||||
int s_jquota_fmt; /* Format of quota to use */
|
||||
int mb_optimize_scan;
|
||||
#ifdef CONFIG_EXT4_DEBUG
|
||||
int s_fc_debug_max_replay;
|
||||
#endif
|
||||
@ -2045,8 +2045,8 @@ struct ext4_fs_context {
|
||||
unsigned int mask_s_mount_opt;
|
||||
unsigned int vals_s_mount_opt2;
|
||||
unsigned int mask_s_mount_opt2;
|
||||
unsigned int vals_s_mount_flags;
|
||||
unsigned int mask_s_mount_flags;
|
||||
unsigned long vals_s_mount_flags;
|
||||
unsigned long mask_s_mount_flags;
|
||||
unsigned int opt_flags; /* MOPT flags */
|
||||
unsigned int spec;
|
||||
u32 s_max_batch_time;
|
||||
@ -2149,23 +2149,36 @@ static inline void ctx_set_##name(struct ext4_fs_context *ctx, \
|
||||
{ \
|
||||
ctx->mask_s_##name |= flag; \
|
||||
ctx->vals_s_##name |= flag; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define EXT4_CLEAR_CTX(name) \
|
||||
static inline void ctx_clear_##name(struct ext4_fs_context *ctx, \
|
||||
unsigned long flag) \
|
||||
{ \
|
||||
ctx->mask_s_##name |= flag; \
|
||||
ctx->vals_s_##name &= ~flag; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define EXT4_TEST_CTX(name) \
|
||||
static inline unsigned long \
|
||||
ctx_test_##name(struct ext4_fs_context *ctx, unsigned long flag) \
|
||||
{ \
|
||||
return (ctx->vals_s_##name & flag); \
|
||||
} \
|
||||
}
|
||||
|
||||
EXT4_SET_CTX(flags);
|
||||
EXT4_SET_CTX(flags); /* set only */
|
||||
EXT4_SET_CTX(mount_opt);
|
||||
EXT4_CLEAR_CTX(mount_opt);
|
||||
EXT4_TEST_CTX(mount_opt);
|
||||
EXT4_SET_CTX(mount_opt2);
|
||||
EXT4_SET_CTX(mount_flags);
|
||||
EXT4_CLEAR_CTX(mount_opt2);
|
||||
EXT4_TEST_CTX(mount_opt2);
|
||||
|
||||
static inline void ctx_set_mount_flag(struct ext4_fs_context *ctx, int bit)
|
||||
{
|
||||
set_bit(bit, &ctx->mask_s_mount_flags);
|
||||
set_bit(bit, &ctx->vals_s_mount_flags);
|
||||
}
|
||||
|
||||
static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
@ -2235,7 +2248,7 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
param->key);
|
||||
return 0;
|
||||
case Opt_abort:
|
||||
ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
|
||||
ctx_set_mount_flag(ctx, EXT4_MF_FS_ABORTED);
|
||||
return 0;
|
||||
case Opt_i_version:
|
||||
ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "5.20");
|
||||
@ -2451,12 +2464,17 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
ctx_clear_mount_opt(ctx, m->mount_opt);
|
||||
return 0;
|
||||
case Opt_mb_optimize_scan:
|
||||
if (result.int_32 != 0 && result.int_32 != 1) {
|
||||
if (result.int_32 == 1) {
|
||||
ctx_set_mount_opt2(ctx, EXT4_MOUNT2_MB_OPTIMIZE_SCAN);
|
||||
ctx->spec |= EXT4_SPEC_mb_optimize_scan;
|
||||
} else if (result.int_32 == 0) {
|
||||
ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_MB_OPTIMIZE_SCAN);
|
||||
ctx->spec |= EXT4_SPEC_mb_optimize_scan;
|
||||
} else {
|
||||
ext4_msg(NULL, KERN_WARNING,
|
||||
"mb_optimize_scan should be set to 0 or 1.");
|
||||
return -EINVAL;
|
||||
}
|
||||
ctx->mb_optimize_scan = result.int_32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3468,8 +3486,9 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
|
||||
*/
|
||||
static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
|
||||
{
|
||||
unsigned long long upper_limit, res = EXT4_NDIR_BLOCKS;
|
||||
loff_t upper_limit, res = EXT4_NDIR_BLOCKS;
|
||||
int meta_blocks;
|
||||
unsigned int ppb = 1 << (bits - 2);
|
||||
|
||||
/*
|
||||
* This is calculated to be the largest file size for a dense, block
|
||||
@ -3501,27 +3520,42 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
|
||||
|
||||
}
|
||||
|
||||
/* Compute how many blocks we can address by block tree */
|
||||
res += ppb;
|
||||
res += ppb * ppb;
|
||||
res += ((loff_t)ppb) * ppb * ppb;
|
||||
/* Compute how many metadata blocks are needed */
|
||||
meta_blocks = 1;
|
||||
meta_blocks += 1 + ppb;
|
||||
meta_blocks += 1 + ppb + ppb * ppb;
|
||||
/* Does block tree limit file size? */
|
||||
if (res + meta_blocks <= upper_limit)
|
||||
goto check_lfs;
|
||||
|
||||
res = upper_limit;
|
||||
/* How many metadata blocks are needed for addressing upper_limit? */
|
||||
upper_limit -= EXT4_NDIR_BLOCKS;
|
||||
/* indirect blocks */
|
||||
meta_blocks = 1;
|
||||
upper_limit -= ppb;
|
||||
/* double indirect blocks */
|
||||
meta_blocks += 1 + (1LL << (bits-2));
|
||||
/* tripple indirect blocks */
|
||||
meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2)));
|
||||
|
||||
upper_limit -= meta_blocks;
|
||||
upper_limit <<= bits;
|
||||
|
||||
res += 1LL << (bits-2);
|
||||
res += 1LL << (2*(bits-2));
|
||||
res += 1LL << (3*(bits-2));
|
||||
if (upper_limit < ppb * ppb) {
|
||||
meta_blocks += 1 + DIV_ROUND_UP_ULL(upper_limit, ppb);
|
||||
res -= meta_blocks;
|
||||
goto check_lfs;
|
||||
}
|
||||
meta_blocks += 1 + ppb;
|
||||
upper_limit -= ppb * ppb;
|
||||
/* tripple indirect blocks for the rest */
|
||||
meta_blocks += 1 + DIV_ROUND_UP_ULL(upper_limit, ppb) +
|
||||
DIV_ROUND_UP_ULL(upper_limit, ppb*ppb);
|
||||
res -= meta_blocks;
|
||||
check_lfs:
|
||||
res <<= bits;
|
||||
if (res > upper_limit)
|
||||
res = upper_limit;
|
||||
|
||||
if (res > MAX_LFS_FILESIZE)
|
||||
res = MAX_LFS_FILESIZE;
|
||||
|
||||
return (loff_t)res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static ext4_fsblk_t descriptor_loc(struct super_block *sb,
|
||||
@ -4369,7 +4403,6 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
|
||||
|
||||
/* Set defaults for the variables that will be set during parsing */
|
||||
ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
|
||||
ctx->mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
|
||||
|
||||
sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
|
||||
sbi->s_sectors_written_start =
|
||||
@ -5320,12 +5353,12 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
|
||||
* turned off by passing "mb_optimize_scan=0". This can also be
|
||||
* turned on forcefully by passing "mb_optimize_scan=1".
|
||||
*/
|
||||
if (ctx->mb_optimize_scan == 1)
|
||||
set_opt2(sb, MB_OPTIMIZE_SCAN);
|
||||
else if (ctx->mb_optimize_scan == 0)
|
||||
clear_opt2(sb, MB_OPTIMIZE_SCAN);
|
||||
else if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD)
|
||||
set_opt2(sb, MB_OPTIMIZE_SCAN);
|
||||
if (!(ctx->spec & EXT4_SPEC_mb_optimize_scan)) {
|
||||
if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD)
|
||||
set_opt2(sb, MB_OPTIMIZE_SCAN);
|
||||
else
|
||||
clear_opt2(sb, MB_OPTIMIZE_SCAN);
|
||||
}
|
||||
|
||||
err = ext4_mb_init(sb);
|
||||
if (err) {
|
||||
|
@ -107,7 +107,6 @@ static void jbd2_get_transaction(journal_t *journal,
|
||||
transaction->t_start_time = ktime_get();
|
||||
transaction->t_tid = journal->j_transaction_sequence++;
|
||||
transaction->t_expires = jiffies + journal->j_commit_interval;
|
||||
spin_lock_init(&transaction->t_handle_lock);
|
||||
atomic_set(&transaction->t_updates, 0);
|
||||
atomic_set(&transaction->t_outstanding_credits,
|
||||
jbd2_descriptor_blocks_per_trans(journal) +
|
||||
@ -139,26 +138,22 @@ static void jbd2_get_transaction(journal_t *journal,
|
||||
/*
|
||||
* Update transaction's maximum wait time, if debugging is enabled.
|
||||
*
|
||||
* In order for t_max_wait to be reliable, it must be protected by a
|
||||
* lock. But doing so will mean that start_this_handle() can not be
|
||||
* run in parallel on SMP systems, which limits our scalability. So
|
||||
* unless debugging is enabled, we no longer update t_max_wait, which
|
||||
* means that maximum wait time reported by the jbd2_run_stats
|
||||
* tracepoint will always be zero.
|
||||
* t_max_wait is carefully updated here with use of atomic compare exchange.
|
||||
* Note that there could be multiplre threads trying to do this simultaneously
|
||||
* hence using cmpxchg to avoid any use of locks in this case.
|
||||
* With this t_max_wait can be updated w/o enabling jbd2_journal_enable_debug.
|
||||
*/
|
||||
static inline void update_t_max_wait(transaction_t *transaction,
|
||||
unsigned long ts)
|
||||
{
|
||||
#ifdef CONFIG_JBD2_DEBUG
|
||||
if (jbd2_journal_enable_debug &&
|
||||
time_after(transaction->t_start, ts)) {
|
||||
ts = jbd2_time_diff(ts, transaction->t_start);
|
||||
spin_lock(&transaction->t_handle_lock);
|
||||
if (ts > transaction->t_max_wait)
|
||||
transaction->t_max_wait = ts;
|
||||
spin_unlock(&transaction->t_handle_lock);
|
||||
unsigned long oldts, newts;
|
||||
|
||||
if (time_after(transaction->t_start, ts)) {
|
||||
newts = jbd2_time_diff(ts, transaction->t_start);
|
||||
oldts = READ_ONCE(transaction->t_max_wait);
|
||||
while (oldts < newts)
|
||||
oldts = cmpxchg(&transaction->t_max_wait, oldts, newts);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -690,7 +685,6 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records)
|
||||
DIV_ROUND_UP(
|
||||
handle->h_revoke_credits_requested,
|
||||
journal->j_revoke_records_per_block);
|
||||
spin_lock(&transaction->t_handle_lock);
|
||||
wanted = atomic_add_return(nblocks,
|
||||
&transaction->t_outstanding_credits);
|
||||
|
||||
@ -698,7 +692,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records)
|
||||
jbd_debug(3, "denied handle %p %d blocks: "
|
||||
"transaction too large\n", handle, nblocks);
|
||||
atomic_sub(nblocks, &transaction->t_outstanding_credits);
|
||||
goto unlock;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
trace_jbd2_handle_extend(journal->j_fs_dev->bd_dev,
|
||||
@ -714,8 +708,6 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records)
|
||||
result = 0;
|
||||
|
||||
jbd_debug(3, "extended handle %p by %d\n", handle, nblocks);
|
||||
unlock:
|
||||
spin_unlock(&transaction->t_handle_lock);
|
||||
error_out:
|
||||
read_unlock(&journal->j_state_lock);
|
||||
return result;
|
||||
@ -842,27 +834,35 @@ EXPORT_SYMBOL(jbd2_journal_restart);
|
||||
*/
|
||||
void jbd2_journal_wait_updates(journal_t *journal)
|
||||
{
|
||||
transaction_t *commit_transaction = journal->j_running_transaction;
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
if (!commit_transaction)
|
||||
return;
|
||||
while (1) {
|
||||
/*
|
||||
* Note that the running transaction can get freed under us if
|
||||
* this transaction is getting committed in
|
||||
* jbd2_journal_commit_transaction() ->
|
||||
* jbd2_journal_free_transaction(). This can only happen when we
|
||||
* release j_state_lock -> schedule() -> acquire j_state_lock.
|
||||
* Hence we should everytime retrieve new j_running_transaction
|
||||
* value (after j_state_lock release acquire cycle), else it may
|
||||
* lead to use-after-free of old freed transaction.
|
||||
*/
|
||||
transaction_t *transaction = journal->j_running_transaction;
|
||||
|
||||
spin_lock(&commit_transaction->t_handle_lock);
|
||||
while (atomic_read(&commit_transaction->t_updates)) {
|
||||
DEFINE_WAIT(wait);
|
||||
if (!transaction)
|
||||
break;
|
||||
|
||||
prepare_to_wait(&journal->j_wait_updates, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
if (atomic_read(&commit_transaction->t_updates)) {
|
||||
spin_unlock(&commit_transaction->t_handle_lock);
|
||||
write_unlock(&journal->j_state_lock);
|
||||
schedule();
|
||||
write_lock(&journal->j_state_lock);
|
||||
spin_lock(&commit_transaction->t_handle_lock);
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
if (!atomic_read(&transaction->t_updates)) {
|
||||
finish_wait(&journal->j_wait_updates, &wait);
|
||||
break;
|
||||
}
|
||||
write_unlock(&journal->j_state_lock);
|
||||
schedule();
|
||||
finish_wait(&journal->j_wait_updates, &wait);
|
||||
write_lock(&journal->j_state_lock);
|
||||
}
|
||||
spin_unlock(&commit_transaction->t_handle_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -877,8 +877,6 @@ void jbd2_journal_wait_updates(journal_t *journal)
|
||||
*/
|
||||
void jbd2_journal_lock_updates(journal_t *journal)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
jbd2_might_wait_for_commit(journal);
|
||||
|
||||
write_lock(&journal->j_state_lock);
|
||||
|
@ -554,9 +554,6 @@ struct transaction_chp_stats_s {
|
||||
* ->j_list_lock
|
||||
*
|
||||
* j_state_lock
|
||||
* ->t_handle_lock
|
||||
*
|
||||
* j_state_lock
|
||||
* ->j_list_lock (journal_unmap_buffer)
|
||||
*
|
||||
*/
|
||||
|
@ -95,6 +95,17 @@ TRACE_DEFINE_ENUM(ES_REFERENCED_B);
|
||||
{ FALLOC_FL_COLLAPSE_RANGE, "COLLAPSE_RANGE"}, \
|
||||
{ FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"})
|
||||
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_XATTR);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_CROSS_RENAME);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_JOURNAL_FLAG_CHANGE);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_NOMEM);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_SWAP_BOOT);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_RESIZE);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_RENAME_DIR);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA);
|
||||
TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX);
|
||||
|
||||
#define show_fc_reason(reason) \
|
||||
__print_symbolic(reason, \
|
||||
{ EXT4_FC_REASON_XATTR, "XATTR"}, \
|
||||
@ -2643,7 +2654,7 @@ TRACE_EVENT(ext4_fc_replay_scan,
|
||||
__entry->off = off;
|
||||
),
|
||||
|
||||
TP_printk("FC scan pass on dev %d,%d: error %d, off %d",
|
||||
TP_printk("dev %d,%d error %d, off %d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->error, __entry->off)
|
||||
);
|
||||
@ -2669,32 +2680,35 @@ TRACE_EVENT(ext4_fc_replay,
|
||||
__entry->priv2 = priv2;
|
||||
),
|
||||
|
||||
TP_printk("FC Replay %d,%d: tag %d, ino %d, data1 %d, data2 %d",
|
||||
TP_printk("dev %d,%d: tag %d, ino %d, data1 %d, data2 %d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->tag, __entry->ino, __entry->priv1, __entry->priv2)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_fc_commit_start,
|
||||
TP_PROTO(struct super_block *sb),
|
||||
TP_PROTO(struct super_block *sb, tid_t commit_tid),
|
||||
|
||||
TP_ARGS(sb),
|
||||
TP_ARGS(sb, commit_tid),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(tid_t, tid)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->tid = commit_tid;
|
||||
),
|
||||
|
||||
TP_printk("fast_commit started on dev %d,%d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev))
|
||||
TP_printk("dev %d,%d tid %u", MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->tid)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_fc_commit_stop,
|
||||
TP_PROTO(struct super_block *sb, int nblks, int reason),
|
||||
TP_PROTO(struct super_block *sb, int nblks, int reason,
|
||||
tid_t commit_tid),
|
||||
|
||||
TP_ARGS(sb, nblks, reason),
|
||||
TP_ARGS(sb, nblks, reason, commit_tid),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
@ -2703,6 +2717,7 @@ TRACE_EVENT(ext4_fc_commit_stop,
|
||||
__field(int, num_fc)
|
||||
__field(int, num_fc_ineligible)
|
||||
__field(int, nblks_agg)
|
||||
__field(tid_t, tid)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@ -2713,128 +2728,193 @@ TRACE_EVENT(ext4_fc_commit_stop,
|
||||
__entry->num_fc_ineligible =
|
||||
EXT4_SB(sb)->s_fc_stats.fc_ineligible_commits;
|
||||
__entry->nblks_agg = EXT4_SB(sb)->s_fc_stats.fc_numblks;
|
||||
__entry->tid = commit_tid;
|
||||
),
|
||||
|
||||
TP_printk("fc on [%d,%d] nblks %d, reason %d, fc = %d, ineligible = %d, agg_nblks %d",
|
||||
TP_printk("dev %d,%d nblks %d, reason %d, fc = %d, ineligible = %d, agg_nblks %d, tid %u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->nblks, __entry->reason, __entry->num_fc,
|
||||
__entry->num_fc_ineligible, __entry->nblks_agg)
|
||||
__entry->num_fc_ineligible, __entry->nblks_agg, __entry->tid)
|
||||
);
|
||||
|
||||
#define FC_REASON_NAME_STAT(reason) \
|
||||
show_fc_reason(reason), \
|
||||
__entry->sbi->s_fc_stats.fc_ineligible_reason_count[reason]
|
||||
__entry->fc_ineligible_rc[reason]
|
||||
|
||||
TRACE_EVENT(ext4_fc_stats,
|
||||
TP_PROTO(struct super_block *sb),
|
||||
TP_PROTO(struct super_block *sb),
|
||||
|
||||
TP_ARGS(sb),
|
||||
TP_ARGS(sb),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(struct ext4_sb_info *, sbi)
|
||||
__field(int, count)
|
||||
),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__array(unsigned int, fc_ineligible_rc, EXT4_FC_REASON_MAX)
|
||||
__field(unsigned long, fc_commits)
|
||||
__field(unsigned long, fc_ineligible_commits)
|
||||
__field(unsigned long, fc_numblks)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->sbi = EXT4_SB(sb);
|
||||
),
|
||||
TP_fast_assign(
|
||||
int i;
|
||||
|
||||
TP_printk("dev %d:%d fc ineligible reasons:\n"
|
||||
"%s:%d, %s:%d, %s:%d, %s:%d, %s:%d, %s:%d, %s:%d, %s:%d, %s:%d; "
|
||||
"num_commits:%ld, ineligible: %ld, numblks: %ld",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_XATTR),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_CROSS_RENAME),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_JOURNAL_FLAG_CHANGE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_NOMEM),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_SWAP_BOOT),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_RESIZE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_RENAME_DIR),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_FALLOC_RANGE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_INODE_JOURNAL_DATA),
|
||||
__entry->sbi->s_fc_stats.fc_num_commits,
|
||||
__entry->sbi->s_fc_stats.fc_ineligible_commits,
|
||||
__entry->sbi->s_fc_stats.fc_numblks)
|
||||
__entry->dev = sb->s_dev;
|
||||
for (i = 0; i < EXT4_FC_REASON_MAX; i++) {
|
||||
__entry->fc_ineligible_rc[i] =
|
||||
EXT4_SB(sb)->s_fc_stats.fc_ineligible_reason_count[i];
|
||||
}
|
||||
__entry->fc_commits = EXT4_SB(sb)->s_fc_stats.fc_num_commits;
|
||||
__entry->fc_ineligible_commits =
|
||||
EXT4_SB(sb)->s_fc_stats.fc_ineligible_commits;
|
||||
__entry->fc_numblks = EXT4_SB(sb)->s_fc_stats.fc_numblks;
|
||||
),
|
||||
|
||||
TP_printk("dev %d,%d fc ineligible reasons:\n"
|
||||
"%s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u "
|
||||
"num_commits:%lu, ineligible: %lu, numblks: %lu",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_XATTR),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_CROSS_RENAME),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_JOURNAL_FLAG_CHANGE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_NOMEM),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_SWAP_BOOT),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_RESIZE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_RENAME_DIR),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_FALLOC_RANGE),
|
||||
FC_REASON_NAME_STAT(EXT4_FC_REASON_INODE_JOURNAL_DATA),
|
||||
__entry->fc_commits, __entry->fc_ineligible_commits,
|
||||
__entry->fc_numblks)
|
||||
);
|
||||
|
||||
#define DEFINE_TRACE_DENTRY_EVENT(__type) \
|
||||
TRACE_EVENT(ext4_fc_track_##__type, \
|
||||
TP_PROTO(struct inode *inode, struct dentry *dentry, int ret), \
|
||||
\
|
||||
TP_ARGS(inode, dentry, ret), \
|
||||
\
|
||||
TP_STRUCT__entry( \
|
||||
__field(dev_t, dev) \
|
||||
__field(int, ino) \
|
||||
__field(int, error) \
|
||||
), \
|
||||
\
|
||||
TP_fast_assign( \
|
||||
__entry->dev = inode->i_sb->s_dev; \
|
||||
__entry->ino = inode->i_ino; \
|
||||
__entry->error = ret; \
|
||||
), \
|
||||
\
|
||||
TP_printk("dev %d:%d, inode %d, error %d, fc_%s", \
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev), \
|
||||
__entry->ino, __entry->error, \
|
||||
#__type) \
|
||||
)
|
||||
DECLARE_EVENT_CLASS(ext4_fc_track_dentry,
|
||||
|
||||
DEFINE_TRACE_DENTRY_EVENT(create);
|
||||
DEFINE_TRACE_DENTRY_EVENT(link);
|
||||
DEFINE_TRACE_DENTRY_EVENT(unlink);
|
||||
TP_PROTO(handle_t *handle, struct inode *inode,
|
||||
struct dentry *dentry, int ret),
|
||||
|
||||
TP_ARGS(handle, inode, dentry, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(tid_t, t_tid)
|
||||
__field(ino_t, i_ino)
|
||||
__field(tid_t, i_sync_tid)
|
||||
__field(int, error)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->t_tid = handle->h_transaction->t_tid;
|
||||
__entry->i_ino = inode->i_ino;
|
||||
__entry->i_sync_tid = ei->i_sync_tid;
|
||||
__entry->error = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev %d,%d, t_tid %u, ino %lu, i_sync_tid %u, error %d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->t_tid, __entry->i_ino, __entry->i_sync_tid,
|
||||
__entry->error
|
||||
)
|
||||
);
|
||||
|
||||
#define DEFINE_EVENT_CLASS_DENTRY(__type) \
|
||||
DEFINE_EVENT(ext4_fc_track_dentry, ext4_fc_track_##__type, \
|
||||
TP_PROTO(handle_t *handle, struct inode *inode, \
|
||||
struct dentry *dentry, int ret), \
|
||||
TP_ARGS(handle, inode, dentry, ret) \
|
||||
)
|
||||
|
||||
DEFINE_EVENT_CLASS_DENTRY(create);
|
||||
DEFINE_EVENT_CLASS_DENTRY(link);
|
||||
DEFINE_EVENT_CLASS_DENTRY(unlink);
|
||||
|
||||
TRACE_EVENT(ext4_fc_track_inode,
|
||||
TP_PROTO(struct inode *inode, int ret),
|
||||
TP_PROTO(handle_t *handle, struct inode *inode, int ret),
|
||||
|
||||
TP_ARGS(inode, ret),
|
||||
TP_ARGS(handle, inode, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, ino)
|
||||
__field(int, error)
|
||||
),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(tid_t, t_tid)
|
||||
__field(ino_t, i_ino)
|
||||
__field(tid_t, i_sync_tid)
|
||||
__field(int, error)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->error = ret;
|
||||
),
|
||||
TP_fast_assign(
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
|
||||
TP_printk("dev %d:%d, inode %d, error %d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino, __entry->error)
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->t_tid = handle->h_transaction->t_tid;
|
||||
__entry->i_ino = inode->i_ino;
|
||||
__entry->i_sync_tid = ei->i_sync_tid;
|
||||
__entry->error = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev %d:%d, t_tid %u, inode %lu, i_sync_tid %u, error %d",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->t_tid, __entry->i_ino, __entry->i_sync_tid,
|
||||
__entry->error)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_fc_track_range,
|
||||
TP_PROTO(struct inode *inode, long start, long end, int ret),
|
||||
TP_PROTO(handle_t *handle, struct inode *inode,
|
||||
long start, long end, int ret),
|
||||
|
||||
TP_ARGS(inode, start, end, ret),
|
||||
TP_ARGS(handle, inode, start, end, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, ino)
|
||||
__field(long, start)
|
||||
__field(long, end)
|
||||
__field(int, error)
|
||||
),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(tid_t, t_tid)
|
||||
__field(ino_t, i_ino)
|
||||
__field(tid_t, i_sync_tid)
|
||||
__field(long, start)
|
||||
__field(long, end)
|
||||
__field(int, error)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->start = start;
|
||||
__entry->end = end;
|
||||
__entry->error = ret;
|
||||
),
|
||||
TP_fast_assign(
|
||||
struct ext4_inode_info *ei = EXT4_I(inode);
|
||||
|
||||
TP_printk("dev %d:%d, inode %d, error %d, start %ld, end %ld",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->ino, __entry->error, __entry->start,
|
||||
__entry->end)
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->t_tid = handle->h_transaction->t_tid;
|
||||
__entry->i_ino = inode->i_ino;
|
||||
__entry->i_sync_tid = ei->i_sync_tid;
|
||||
__entry->start = start;
|
||||
__entry->end = end;
|
||||
__entry->error = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev %d:%d, t_tid %u, inode %lu, i_sync_tid %u, error %d, start %ld, end %ld",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->t_tid, __entry->i_ino, __entry->i_sync_tid,
|
||||
__entry->error, __entry->start, __entry->end)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_fc_cleanup,
|
||||
TP_PROTO(journal_t *journal, int full, tid_t tid),
|
||||
|
||||
TP_ARGS(journal, full, tid),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, j_fc_off)
|
||||
__field(int, full)
|
||||
__field(tid_t, tid)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
struct super_block *sb = journal->j_private;
|
||||
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->j_fc_off = journal->j_fc_off;
|
||||
__entry->full = full;
|
||||
__entry->tid = tid;
|
||||
),
|
||||
|
||||
TP_printk("dev %d,%d, j_fc_off %d, full %d, tid %u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->j_fc_off, __entry->full, __entry->tid)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_update_sb,
|
||||
|
Loading…
Reference in New Issue
Block a user