ext4: main fast-commit commit path
This patch adds main fast commit commit path handlers. The overall patch can be divided into two inter-related parts: (A) Metadata updates tracking This part consists of helper functions to track changes that need to be committed during a commit operation. These updates are maintained by Ext4 in different in-memory queues. Following are the APIs and their short description that are implemented in this patch: - ext4_fc_track_link/unlink/creat() - Track unlink. link and creat operations - ext4_fc_track_range() - Track changed logical block offsets inodes - ext4_fc_track_inode() - Track inodes - ext4_fc_mark_ineligible() - Mark file system fast commit ineligible() - ext4_fc_start_update() / ext4_fc_stop_update() / ext4_fc_start_ineligible() / ext4_fc_stop_ineligible() These functions are useful for co-ordinating inode updates with commits. (B) Main commit Path This part consists of functions to convert updates tracked in in-memory data structures into on-disk commits. Function ext4_fc_commit() is the main entry point to commit path. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Harshad Shirwadkar <harshadshirwadkar@gmail.com> Link: https://lore.kernel.org/r/20201015203802.3597742-6-harshadshirwadkar@gmail.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
ff780b91ef
commit
aa75f4d3da
|
@ -242,6 +242,7 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||||
handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
|
handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
|
||||||
if (IS_ERR(handle))
|
if (IS_ERR(handle))
|
||||||
return PTR_ERR(handle);
|
return PTR_ERR(handle);
|
||||||
|
ext4_fc_start_update(inode);
|
||||||
|
|
||||||
if ((type == ACL_TYPE_ACCESS) && acl) {
|
if ((type == ACL_TYPE_ACCESS) && acl) {
|
||||||
error = posix_acl_update_mode(inode, &mode, &acl);
|
error = posix_acl_update_mode(inode, &mode, &acl);
|
||||||
|
@ -259,6 +260,7 @@ ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||||
}
|
}
|
||||||
out_stop:
|
out_stop:
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||||
goto retry;
|
goto retry;
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -1021,6 +1021,31 @@ struct ext4_inode_info {
|
||||||
|
|
||||||
struct list_head i_orphan; /* unlinked but open inodes */
|
struct list_head i_orphan; /* unlinked but open inodes */
|
||||||
|
|
||||||
|
/* Fast commit related info */
|
||||||
|
|
||||||
|
struct list_head i_fc_list; /*
|
||||||
|
* inodes that need fast commit
|
||||||
|
* protected by sbi->s_fc_lock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fast commit subtid when this inode was committed */
|
||||||
|
unsigned int i_fc_committed_subtid;
|
||||||
|
|
||||||
|
/* Start of lblk range that needs to be committed in this fast commit */
|
||||||
|
ext4_lblk_t i_fc_lblk_start;
|
||||||
|
|
||||||
|
/* End of lblk range that needs to be committed in this fast commit */
|
||||||
|
ext4_lblk_t i_fc_lblk_len;
|
||||||
|
|
||||||
|
/* Number of ongoing updates on this inode */
|
||||||
|
atomic_t i_fc_updates;
|
||||||
|
|
||||||
|
/* Fast commit wait queue for this inode */
|
||||||
|
wait_queue_head_t i_fc_wait;
|
||||||
|
|
||||||
|
/* Protect concurrent accesses on i_fc_lblk_start, i_fc_lblk_len */
|
||||||
|
struct mutex i_fc_lock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i_disksize keeps track of what the inode size is ON DISK, not
|
* i_disksize keeps track of what the inode size is ON DISK, not
|
||||||
* in memory. During truncate, i_size is set to the new size by
|
* in memory. During truncate, i_size is set to the new size by
|
||||||
|
@ -1141,6 +1166,10 @@ struct ext4_inode_info {
|
||||||
#define EXT4_VALID_FS 0x0001 /* Unmounted cleanly */
|
#define EXT4_VALID_FS 0x0001 /* Unmounted cleanly */
|
||||||
#define EXT4_ERROR_FS 0x0002 /* Errors detected */
|
#define EXT4_ERROR_FS 0x0002 /* Errors detected */
|
||||||
#define EXT4_ORPHAN_FS 0x0004 /* Orphans being recovered */
|
#define EXT4_ORPHAN_FS 0x0004 /* Orphans being recovered */
|
||||||
|
#define EXT4_FC_INELIGIBLE 0x0008 /* Fast commit ineligible */
|
||||||
|
#define EXT4_FC_COMMITTING 0x0010 /* File system underoing a fast
|
||||||
|
* commit.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Misc. filesystem flags
|
* Misc. filesystem flags
|
||||||
|
@ -1613,6 +1642,30 @@ struct ext4_sb_info {
|
||||||
/* Record the errseq of the backing block device */
|
/* Record the errseq of the backing block device */
|
||||||
errseq_t s_bdev_wb_err;
|
errseq_t s_bdev_wb_err;
|
||||||
spinlock_t s_bdev_wb_lock;
|
spinlock_t s_bdev_wb_lock;
|
||||||
|
|
||||||
|
/* Ext4 fast commit stuff */
|
||||||
|
atomic_t s_fc_subtid;
|
||||||
|
atomic_t s_fc_ineligible_updates;
|
||||||
|
/*
|
||||||
|
* After commit starts, the main queue gets locked, and the further
|
||||||
|
* updates get added in the staging queue.
|
||||||
|
*/
|
||||||
|
#define FC_Q_MAIN 0
|
||||||
|
#define FC_Q_STAGING 1
|
||||||
|
struct list_head s_fc_q[2]; /* Inodes staged for fast commit
|
||||||
|
* that have data changes in them.
|
||||||
|
*/
|
||||||
|
struct list_head s_fc_dentry_q[2]; /* directory entry updates */
|
||||||
|
unsigned int s_fc_bytes;
|
||||||
|
/*
|
||||||
|
* Main fast commit lock. This lock protects accesses to the
|
||||||
|
* following fields:
|
||||||
|
* ei->i_fc_list, s_fc_dentry_q, s_fc_q, s_fc_bytes, s_fc_bh.
|
||||||
|
*/
|
||||||
|
spinlock_t s_fc_lock;
|
||||||
|
struct buffer_head *s_fc_bh;
|
||||||
|
struct ext4_fc_stats s_fc_stats;
|
||||||
|
u64 s_fc_avg_commit_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||||
|
@ -1723,6 +1776,7 @@ enum {
|
||||||
EXT4_STATE_EXT_PRECACHED, /* extents have been precached */
|
EXT4_STATE_EXT_PRECACHED, /* extents have been precached */
|
||||||
EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */
|
EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */
|
||||||
EXT4_STATE_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */
|
EXT4_STATE_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */
|
||||||
|
EXT4_STATE_FC_COMMITTING, /* Fast commit ongoing */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
||||||
|
@ -2682,6 +2736,22 @@ extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
|
||||||
/* fast_commit.c */
|
/* fast_commit.c */
|
||||||
|
|
||||||
void ext4_fc_init(struct super_block *sb, journal_t *journal);
|
void ext4_fc_init(struct super_block *sb, journal_t *journal);
|
||||||
|
void ext4_fc_init_inode(struct inode *inode);
|
||||||
|
void ext4_fc_track_range(struct inode *inode, ext4_lblk_t start,
|
||||||
|
ext4_lblk_t end);
|
||||||
|
void ext4_fc_track_unlink(struct inode *inode, struct dentry *dentry);
|
||||||
|
void ext4_fc_track_link(struct inode *inode, struct dentry *dentry);
|
||||||
|
void ext4_fc_track_create(struct inode *inode, struct dentry *dentry);
|
||||||
|
void ext4_fc_track_inode(struct inode *inode);
|
||||||
|
void ext4_fc_mark_ineligible(struct super_block *sb, int reason);
|
||||||
|
void ext4_fc_start_ineligible(struct super_block *sb, int reason);
|
||||||
|
void ext4_fc_stop_ineligible(struct super_block *sb);
|
||||||
|
void ext4_fc_start_update(struct inode *inode);
|
||||||
|
void ext4_fc_stop_update(struct inode *inode);
|
||||||
|
void ext4_fc_del(struct inode *inode);
|
||||||
|
int ext4_fc_commit(journal_t *journal, tid_t commit_tid);
|
||||||
|
int __init ext4_fc_init_dentry_cache(void);
|
||||||
|
|
||||||
/* mballoc.c */
|
/* mballoc.c */
|
||||||
extern const struct seq_operations ext4_mb_seq_groups_ops;
|
extern const struct seq_operations ext4_mb_seq_groups_ops;
|
||||||
extern long ext4_mb_stats;
|
extern long ext4_mb_stats;
|
||||||
|
|
|
@ -3723,6 +3723,7 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
|
||||||
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
||||||
out:
|
out:
|
||||||
ext4_ext_show_leaf(inode, path);
|
ext4_ext_show_leaf(inode, path);
|
||||||
|
ext4_fc_track_range(inode, ee_block, ee_block + ee_len - 1);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3794,6 +3795,7 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
|
||||||
if (*allocated > map->m_len)
|
if (*allocated > map->m_len)
|
||||||
*allocated = map->m_len;
|
*allocated = map->m_len;
|
||||||
map->m_len = *allocated;
|
map->m_len = *allocated;
|
||||||
|
ext4_fc_track_range(inode, ee_block, ee_block + ee_len - 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4327,7 +4329,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
|
||||||
map->m_len = ar.len;
|
map->m_len = ar.len;
|
||||||
allocated = map->m_len;
|
allocated = map->m_len;
|
||||||
ext4_ext_show_leaf(inode, path);
|
ext4_ext_show_leaf(inode, path);
|
||||||
|
ext4_fc_track_range(inode, map->m_lblk, map->m_lblk + map->m_len - 1);
|
||||||
out:
|
out:
|
||||||
ext4_ext_drop_refs(path);
|
ext4_ext_drop_refs(path);
|
||||||
kfree(path);
|
kfree(path);
|
||||||
|
@ -4600,7 +4602,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
||||||
ret = ext4_mark_inode_dirty(handle, inode);
|
ret = ext4_mark_inode_dirty(handle, inode);
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
goto out_handle;
|
goto out_handle;
|
||||||
|
ext4_fc_track_range(inode, offset >> inode->i_sb->s_blocksize_bits,
|
||||||
|
(offset + len - 1) >> inode->i_sb->s_blocksize_bits);
|
||||||
/* Zero out partial block at the edges of the range */
|
/* Zero out partial block at the edges of the range */
|
||||||
ret = ext4_zero_partial_blocks(handle, inode, offset, len);
|
ret = ext4_zero_partial_blocks(handle, inode, offset, len);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
|
@ -4648,23 +4651,34 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||||
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
|
FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
|
||||||
FALLOC_FL_INSERT_RANGE))
|
FALLOC_FL_INSERT_RANGE))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
ext4_fc_track_range(inode, offset >> blkbits,
|
||||||
|
(offset + len - 1) >> blkbits);
|
||||||
|
|
||||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
ext4_fc_start_update(inode);
|
||||||
return ext4_punch_hole(inode, offset, len);
|
|
||||||
|
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||||
|
ret = ext4_punch_hole(inode, offset, len);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ext4_convert_inline_data(inode);
|
ret = ext4_convert_inline_data(inode);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto exit;
|
||||||
|
|
||||||
if (mode & FALLOC_FL_COLLAPSE_RANGE)
|
if (mode & FALLOC_FL_COLLAPSE_RANGE) {
|
||||||
return ext4_collapse_range(inode, offset, len);
|
ret = ext4_collapse_range(inode, offset, len);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode & FALLOC_FL_INSERT_RANGE)
|
if (mode & FALLOC_FL_INSERT_RANGE) {
|
||||||
return ext4_insert_range(inode, offset, len);
|
ret = ext4_insert_range(inode, offset, len);
|
||||||
|
goto exit;
|
||||||
if (mode & FALLOC_FL_ZERO_RANGE)
|
}
|
||||||
return ext4_zero_range(file, offset, len, mode);
|
|
||||||
|
|
||||||
|
if (mode & FALLOC_FL_ZERO_RANGE) {
|
||||||
|
ret = ext4_zero_range(file, offset, len, mode);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
trace_ext4_fallocate_enter(inode, offset, len, mode);
|
trace_ext4_fallocate_enter(inode, offset, len, mode);
|
||||||
lblk = offset >> blkbits;
|
lblk = offset >> blkbits;
|
||||||
|
|
||||||
|
@ -4698,12 +4712,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
|
if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
|
||||||
ret = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal,
|
ret = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
|
||||||
EXT4_I(inode)->i_sync_tid);
|
EXT4_I(inode)->i_sync_tid);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
|
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
|
||||||
|
exit:
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5291,6 +5307,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
|
||||||
ret = PTR_ERR(handle);
|
ret = PTR_ERR(handle);
|
||||||
goto out_mmap;
|
goto out_mmap;
|
||||||
}
|
}
|
||||||
|
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||||
|
|
||||||
down_write(&EXT4_I(inode)->i_data_sem);
|
down_write(&EXT4_I(inode)->i_data_sem);
|
||||||
ext4_discard_preallocations(inode, 0);
|
ext4_discard_preallocations(inode, 0);
|
||||||
|
@ -5329,6 +5346,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
|
||||||
|
|
||||||
out_stop:
|
out_stop:
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
ext4_fc_stop_ineligible(sb);
|
||||||
out_mmap:
|
out_mmap:
|
||||||
up_write(&EXT4_I(inode)->i_mmap_sem);
|
up_write(&EXT4_I(inode)->i_mmap_sem);
|
||||||
out_mutex:
|
out_mutex:
|
||||||
|
@ -5429,6 +5447,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
|
||||||
ret = PTR_ERR(handle);
|
ret = PTR_ERR(handle);
|
||||||
goto out_mmap;
|
goto out_mmap;
|
||||||
}
|
}
|
||||||
|
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||||
|
|
||||||
/* Expand file to avoid data loss if there is error while shifting */
|
/* Expand file to avoid data loss if there is error while shifting */
|
||||||
inode->i_size += len;
|
inode->i_size += len;
|
||||||
|
@ -5503,6 +5522,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
|
||||||
|
|
||||||
out_stop:
|
out_stop:
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
ext4_fc_stop_ineligible(sb);
|
||||||
out_mmap:
|
out_mmap:
|
||||||
up_write(&EXT4_I(inode)->i_mmap_sem);
|
up_write(&EXT4_I(inode)->i_mmap_sem);
|
||||||
out_mutex:
|
out_mutex:
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,4 +6,114 @@
|
||||||
/* Number of blocks in journal area to allocate for fast commits */
|
/* Number of blocks in journal area to allocate for fast commits */
|
||||||
#define EXT4_NUM_FC_BLKS 256
|
#define EXT4_NUM_FC_BLKS 256
|
||||||
|
|
||||||
|
/* Fast commit tags */
|
||||||
|
#define EXT4_FC_TAG_ADD_RANGE 0x0001
|
||||||
|
#define EXT4_FC_TAG_DEL_RANGE 0x0002
|
||||||
|
#define EXT4_FC_TAG_CREAT 0x0003
|
||||||
|
#define EXT4_FC_TAG_LINK 0x0004
|
||||||
|
#define EXT4_FC_TAG_UNLINK 0x0005
|
||||||
|
#define EXT4_FC_TAG_INODE 0x0006
|
||||||
|
#define EXT4_FC_TAG_PAD 0x0007
|
||||||
|
#define EXT4_FC_TAG_TAIL 0x0008
|
||||||
|
#define EXT4_FC_TAG_HEAD 0x0009
|
||||||
|
|
||||||
|
#define EXT4_FC_SUPPORTED_FEATURES 0x0
|
||||||
|
|
||||||
|
/* On disk fast commit tlv value structures */
|
||||||
|
|
||||||
|
/* Fast commit on disk tag length structure */
|
||||||
|
struct ext4_fc_tl {
|
||||||
|
__le16 fc_tag;
|
||||||
|
__le16 fc_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Value structure for tag EXT4_FC_TAG_HEAD. */
|
||||||
|
struct ext4_fc_head {
|
||||||
|
__le32 fc_features;
|
||||||
|
__le32 fc_tid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Value structure for EXT4_FC_TAG_ADD_RANGE. */
|
||||||
|
struct ext4_fc_add_range {
|
||||||
|
__le32 fc_ino;
|
||||||
|
__u8 fc_ex[12];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Value structure for tag EXT4_FC_TAG_DEL_RANGE. */
|
||||||
|
struct ext4_fc_del_range {
|
||||||
|
__le32 fc_ino;
|
||||||
|
__le32 fc_lblk;
|
||||||
|
__le32 fc_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the value structure for tags EXT4_FC_TAG_CREAT, EXT4_FC_TAG_LINK
|
||||||
|
* and EXT4_FC_TAG_UNLINK.
|
||||||
|
*/
|
||||||
|
struct ext4_fc_dentry_info {
|
||||||
|
__le32 fc_parent_ino;
|
||||||
|
__le32 fc_ino;
|
||||||
|
u8 fc_dname[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */
|
||||||
|
struct ext4_fc_inode {
|
||||||
|
__le32 fc_ino;
|
||||||
|
__u8 fc_raw_inode[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Value structure for tag EXT4_FC_TAG_TAIL. */
|
||||||
|
struct ext4_fc_tail {
|
||||||
|
__le32 fc_tid;
|
||||||
|
__le32 fc_crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In memory list of dentry updates that are performed on the file
|
||||||
|
* system used by fast commit code.
|
||||||
|
*/
|
||||||
|
struct ext4_fc_dentry_update {
|
||||||
|
int fcd_op; /* Type of update create / unlink / link */
|
||||||
|
int fcd_parent; /* Parent inode number */
|
||||||
|
int fcd_ino; /* Inode number */
|
||||||
|
struct qstr fcd_name; /* Dirent name */
|
||||||
|
unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */
|
||||||
|
struct list_head fcd_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fast commit reason codes
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
/*
|
||||||
|
* Commit status codes:
|
||||||
|
*/
|
||||||
|
EXT4_FC_REASON_OK = 0,
|
||||||
|
EXT4_FC_REASON_INELIGIBLE,
|
||||||
|
EXT4_FC_REASON_ALREADY_COMMITTED,
|
||||||
|
EXT4_FC_REASON_FC_START_FAILED,
|
||||||
|
EXT4_FC_REASON_FC_FAILED,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fast commit ineligiblity reasons:
|
||||||
|
*/
|
||||||
|
EXT4_FC_REASON_XATTR = 0,
|
||||||
|
EXT4_FC_REASON_CROSS_RENAME,
|
||||||
|
EXT4_FC_REASON_JOURNAL_FLAG_CHANGE,
|
||||||
|
EXT4_FC_REASON_MEM,
|
||||||
|
EXT4_FC_REASON_SWAP_BOOT,
|
||||||
|
EXT4_FC_REASON_RESIZE,
|
||||||
|
EXT4_FC_REASON_RENAME_DIR,
|
||||||
|
EXT4_FC_REASON_FALLOC_RANGE,
|
||||||
|
EXT4_FC_COMMIT_FAILED,
|
||||||
|
EXT4_FC_REASON_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ext4_fc_stats {
|
||||||
|
unsigned int fc_ineligible_reason_count[EXT4_FC_REASON_MAX];
|
||||||
|
unsigned long fc_num_commits;
|
||||||
|
unsigned long fc_ineligible_commits;
|
||||||
|
unsigned long fc_numblks;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* __FAST_COMMIT_H__ */
|
#endif /* __FAST_COMMIT_H__ */
|
||||||
|
|
|
@ -260,6 +260,7 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
|
||||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
ext4_fc_start_update(inode);
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
ret = ext4_write_checks(iocb, from);
|
ret = ext4_write_checks(iocb, from);
|
||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
|
@ -271,6 +272,7 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
|
||||||
|
|
||||||
out:
|
out:
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
if (likely(ret > 0)) {
|
if (likely(ret > 0)) {
|
||||||
iocb->ki_pos += ret;
|
iocb->ki_pos += ret;
|
||||||
ret = generic_write_sync(iocb, ret);
|
ret = generic_write_sync(iocb, ret);
|
||||||
|
@ -534,7 +536,9 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext4_fc_start_update(inode);
|
||||||
ret = ext4_orphan_add(handle, inode);
|
ret = ext4_orphan_add(handle, inode);
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -656,8 +660,8 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
#endif
|
#endif
|
||||||
if (iocb->ki_flags & IOCB_DIRECT)
|
if (iocb->ki_flags & IOCB_DIRECT)
|
||||||
return ext4_dio_write_iter(iocb, from);
|
return ext4_dio_write_iter(iocb, from);
|
||||||
|
else
|
||||||
return ext4_buffered_write_iter(iocb, from);
|
return ext4_buffered_write_iter(iocb, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_FS_DAX
|
#ifdef CONFIG_FS_DAX
|
||||||
|
@ -757,6 +761,7 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
if (!daxdev_mapping_supported(vma, dax_dev))
|
if (!daxdev_mapping_supported(vma, dax_dev))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
ext4_fc_start_update(inode);
|
||||||
file_accessed(file);
|
file_accessed(file);
|
||||||
if (IS_DAX(file_inode(file))) {
|
if (IS_DAX(file_inode(file))) {
|
||||||
vma->vm_ops = &ext4_dax_vm_ops;
|
vma->vm_ops = &ext4_dax_vm_ops;
|
||||||
|
@ -764,6 +769,7 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
} else {
|
} else {
|
||||||
vma->vm_ops = &ext4_file_vm_ops;
|
vma->vm_ops = &ext4_file_vm_ops;
|
||||||
}
|
}
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ static int ext4_fsync_journal(struct inode *inode, bool datasync,
|
||||||
!jbd2_trans_will_send_data_barrier(journal, commit_tid))
|
!jbd2_trans_will_send_data_barrier(journal, commit_tid))
|
||||||
*needs_barrier = true;
|
*needs_barrier = true;
|
||||||
|
|
||||||
return jbd2_complete_transaction(journal, commit_tid);
|
return ext4_fc_commit(journal, commit_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -729,6 +729,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ext4_fc_track_range(inode, map->m_lblk,
|
||||||
|
map->m_lblk + map->m_len - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
|
@ -3300,9 +3302,14 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)
|
||||||
{
|
{
|
||||||
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
|
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
|
||||||
|
|
||||||
if (journal)
|
if (journal) {
|
||||||
return !jbd2_transaction_committed(journal,
|
if (jbd2_transaction_committed(journal,
|
||||||
EXT4_I(inode)->i_datasync_tid);
|
EXT4_I(inode)->i_datasync_tid))
|
||||||
|
return true;
|
||||||
|
return atomic_read(&EXT4_SB(inode->i_sb)->s_fc_subtid) >=
|
||||||
|
EXT4_I(inode)->i_fc_committed_subtid;
|
||||||
|
}
|
||||||
|
|
||||||
/* Any metadata buffers to write? */
|
/* Any metadata buffers to write? */
|
||||||
if (!list_empty(&inode->i_mapping->private_list))
|
if (!list_empty(&inode->i_mapping->private_list))
|
||||||
return true;
|
return true;
|
||||||
|
@ -4097,6 +4104,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
|
||||||
|
|
||||||
up_write(&EXT4_I(inode)->i_data_sem);
|
up_write(&EXT4_I(inode)->i_data_sem);
|
||||||
}
|
}
|
||||||
|
ext4_fc_track_range(inode, first_block, stop_block);
|
||||||
if (IS_SYNC(inode))
|
if (IS_SYNC(inode))
|
||||||
ext4_handle_sync(handle);
|
ext4_handle_sync(handle);
|
||||||
|
|
||||||
|
@ -4716,6 +4724,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
|
||||||
for (block = 0; block < EXT4_N_BLOCKS; block++)
|
for (block = 0; block < EXT4_N_BLOCKS; block++)
|
||||||
ei->i_data[block] = raw_inode->i_block[block];
|
ei->i_data[block] = raw_inode->i_block[block];
|
||||||
INIT_LIST_HEAD(&ei->i_orphan);
|
INIT_LIST_HEAD(&ei->i_orphan);
|
||||||
|
ext4_fc_init_inode(&ei->vfs_inode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set transaction id's of transactions that have to be committed
|
* Set transaction id's of transactions that have to be committed
|
||||||
|
@ -5162,7 +5171,7 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||||
if (wbc->sync_mode != WB_SYNC_ALL || wbc->for_sync)
|
if (wbc->sync_mode != WB_SYNC_ALL || wbc->for_sync)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal,
|
err = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
|
||||||
EXT4_I(inode)->i_sync_tid);
|
EXT4_I(inode)->i_sync_tid);
|
||||||
} else {
|
} else {
|
||||||
struct ext4_iloc iloc;
|
struct ext4_iloc iloc;
|
||||||
|
@ -5291,6 +5300,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
ext4_fc_start_update(inode);
|
||||||
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
|
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
|
||||||
(ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
|
(ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
|
@ -5314,6 +5324,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
/* Update corresponding info in inode so that everything is in
|
/* Update corresponding info in inode so that everything is in
|
||||||
|
@ -5336,11 +5347,15 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
|
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
|
||||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||||
|
|
||||||
if (attr->ia_size > sbi->s_bitmap_maxbytes)
|
if (attr->ia_size > sbi->s_bitmap_maxbytes) {
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return -EFBIG;
|
return -EFBIG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!S_ISREG(inode->i_mode))
|
if (!S_ISREG(inode->i_mode)) {
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
|
if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
|
||||||
inode_inc_iversion(inode);
|
inode_inc_iversion(inode);
|
||||||
|
@ -5364,7 +5379,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
rc = ext4_break_layouts(inode);
|
rc = ext4_break_layouts(inode);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
up_write(&EXT4_I(inode)->i_mmap_sem);
|
up_write(&EXT4_I(inode)->i_mmap_sem);
|
||||||
return rc;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attr->ia_size != inode->i_size) {
|
if (attr->ia_size != inode->i_size) {
|
||||||
|
@ -5385,6 +5400,21 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
inode->i_mtime = current_time(inode);
|
inode->i_mtime = current_time(inode);
|
||||||
inode->i_ctime = inode->i_mtime;
|
inode->i_ctime = inode->i_mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shrink)
|
||||||
|
ext4_fc_track_range(inode,
|
||||||
|
(attr->ia_size > 0 ? attr->ia_size - 1 : 0) >>
|
||||||
|
inode->i_sb->s_blocksize_bits,
|
||||||
|
(oldsize > 0 ? oldsize - 1 : 0) >>
|
||||||
|
inode->i_sb->s_blocksize_bits);
|
||||||
|
else
|
||||||
|
ext4_fc_track_range(
|
||||||
|
inode,
|
||||||
|
(oldsize > 0 ? oldsize - 1 : oldsize) >>
|
||||||
|
inode->i_sb->s_blocksize_bits,
|
||||||
|
(attr->ia_size > 0 ? attr->ia_size - 1 : 0) >>
|
||||||
|
inode->i_sb->s_blocksize_bits);
|
||||||
|
|
||||||
down_write(&EXT4_I(inode)->i_data_sem);
|
down_write(&EXT4_I(inode)->i_data_sem);
|
||||||
EXT4_I(inode)->i_disksize = attr->ia_size;
|
EXT4_I(inode)->i_disksize = attr->ia_size;
|
||||||
rc = ext4_mark_inode_dirty(handle, inode);
|
rc = ext4_mark_inode_dirty(handle, inode);
|
||||||
|
@ -5443,9 +5473,11 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
rc = posix_acl_chmod(inode, inode->i_mode);
|
rc = posix_acl_chmod(inode, inode->i_mode);
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
ext4_std_error(inode->i_sb, error);
|
if (error)
|
||||||
|
ext4_std_error(inode->i_sb, error);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = rc;
|
error = rc;
|
||||||
|
ext4_fc_stop_update(inode);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5627,6 +5659,8 @@ int ext4_mark_iloc_dirty(handle_t *handle,
|
||||||
put_bh(iloc->bh);
|
put_bh(iloc->bh);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
ext4_fc_track_inode(inode);
|
||||||
|
|
||||||
if (IS_I_VERSION(inode))
|
if (IS_I_VERSION(inode))
|
||||||
inode_inc_iversion(inode);
|
inode_inc_iversion(inode);
|
||||||
|
|
||||||
|
@ -5950,6 +5984,8 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
|
||||||
if (IS_ERR(handle))
|
if (IS_ERR(handle))
|
||||||
return PTR_ERR(handle);
|
return PTR_ERR(handle);
|
||||||
|
|
||||||
|
ext4_fc_mark_ineligible(inode->i_sb,
|
||||||
|
EXT4_FC_REASON_JOURNAL_FLAG_CHANGE);
|
||||||
err = ext4_mark_inode_dirty(handle, inode);
|
err = ext4_mark_inode_dirty(handle, inode);
|
||||||
ext4_handle_sync(handle);
|
ext4_handle_sync(handle);
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
|
|
@ -165,6 +165,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT);
|
||||||
|
|
||||||
/* Protect extent tree against block allocations via delalloc */
|
/* Protect extent tree against block allocations via delalloc */
|
||||||
ext4_double_down_write_data_sem(inode, inode_bl);
|
ext4_double_down_write_data_sem(inode, inode_bl);
|
||||||
|
@ -247,6 +248,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
|
||||||
|
|
||||||
err_out1:
|
err_out1:
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
ext4_fc_stop_ineligible(sb);
|
||||||
ext4_double_up_write_data_sem(inode, inode_bl);
|
ext4_double_up_write_data_sem(inode, inode_bl);
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
|
@ -807,7 +809,7 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filp);
|
struct inode *inode = file_inode(filp);
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
|
@ -1074,6 +1076,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
|
|
||||||
err = ext4_resize_fs(sb, n_blocks_count);
|
err = ext4_resize_fs(sb, n_blocks_count);
|
||||||
if (EXT4_SB(sb)->s_journal) {
|
if (EXT4_SB(sb)->s_journal) {
|
||||||
|
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE);
|
||||||
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
|
||||||
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
|
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
|
||||||
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
|
||||||
|
@ -1308,6 +1311,17 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
ext4_fc_start_update(file_inode(filp));
|
||||||
|
ret = __ext4_ioctl(filp, cmd, arg);
|
||||||
|
ext4_fc_stop_update(file_inode(filp));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2611,7 +2611,7 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
bool excl)
|
bool excl)
|
||||||
{
|
{
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
struct inode *inode;
|
struct inode *inode, *inode_save;
|
||||||
int err, credits, retries = 0;
|
int err, credits, retries = 0;
|
||||||
|
|
||||||
err = dquot_initialize(dir);
|
err = dquot_initialize(dir);
|
||||||
|
@ -2629,7 +2629,11 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
inode->i_op = &ext4_file_inode_operations;
|
inode->i_op = &ext4_file_inode_operations;
|
||||||
inode->i_fop = &ext4_file_operations;
|
inode->i_fop = &ext4_file_operations;
|
||||||
ext4_set_aops(inode);
|
ext4_set_aops(inode);
|
||||||
|
inode_save = inode;
|
||||||
|
ihold(inode_save);
|
||||||
err = ext4_add_nondir(handle, dentry, &inode);
|
err = ext4_add_nondir(handle, dentry, &inode);
|
||||||
|
ext4_fc_track_create(inode_save, dentry);
|
||||||
|
iput(inode_save);
|
||||||
}
|
}
|
||||||
if (handle)
|
if (handle)
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
@ -2644,7 +2648,7 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
|
||||||
umode_t mode, dev_t rdev)
|
umode_t mode, dev_t rdev)
|
||||||
{
|
{
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
struct inode *inode;
|
struct inode *inode, *inode_save;
|
||||||
int err, credits, retries = 0;
|
int err, credits, retries = 0;
|
||||||
|
|
||||||
err = dquot_initialize(dir);
|
err = dquot_initialize(dir);
|
||||||
|
@ -2661,7 +2665,12 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
|
||||||
if (!IS_ERR(inode)) {
|
if (!IS_ERR(inode)) {
|
||||||
init_special_inode(inode, inode->i_mode, rdev);
|
init_special_inode(inode, inode->i_mode, rdev);
|
||||||
inode->i_op = &ext4_special_inode_operations;
|
inode->i_op = &ext4_special_inode_operations;
|
||||||
|
inode_save = inode;
|
||||||
|
ihold(inode_save);
|
||||||
err = ext4_add_nondir(handle, dentry, &inode);
|
err = ext4_add_nondir(handle, dentry, &inode);
|
||||||
|
if (!err)
|
||||||
|
ext4_fc_track_create(inode_save, dentry);
|
||||||
|
iput(inode_save);
|
||||||
}
|
}
|
||||||
if (handle)
|
if (handle)
|
||||||
ext4_journal_stop(handle);
|
ext4_journal_stop(handle);
|
||||||
|
@ -2825,7 +2834,9 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
iput(inode);
|
iput(inode);
|
||||||
goto out_retry;
|
goto out_retry;
|
||||||
}
|
}
|
||||||
|
ext4_fc_track_create(inode, dentry);
|
||||||
ext4_inc_count(dir);
|
ext4_inc_count(dir);
|
||||||
|
|
||||||
ext4_update_dx_flag(dir);
|
ext4_update_dx_flag(dir);
|
||||||
err = ext4_mark_inode_dirty(handle, dir);
|
err = ext4_mark_inode_dirty(handle, dir);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -3165,6 +3176,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
goto end_rmdir;
|
goto end_rmdir;
|
||||||
ext4_dec_count(dir);
|
ext4_dec_count(dir);
|
||||||
ext4_update_dx_flag(dir);
|
ext4_update_dx_flag(dir);
|
||||||
|
ext4_fc_track_unlink(inode, dentry);
|
||||||
retval = ext4_mark_inode_dirty(handle, dir);
|
retval = ext4_mark_inode_dirty(handle, dir);
|
||||||
|
|
||||||
#ifdef CONFIG_UNICODE
|
#ifdef CONFIG_UNICODE
|
||||||
|
@ -3251,6 +3263,8 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
inode->i_ctime = current_time(inode);
|
inode->i_ctime = current_time(inode);
|
||||||
retval = ext4_mark_inode_dirty(handle, inode);
|
retval = ext4_mark_inode_dirty(handle, inode);
|
||||||
|
|
||||||
|
if (!retval)
|
||||||
|
ext4_fc_track_unlink(d_inode(dentry), dentry);
|
||||||
#ifdef CONFIG_UNICODE
|
#ifdef CONFIG_UNICODE
|
||||||
/* VFS negative dentries are incompatible with Encoding and
|
/* VFS negative dentries are incompatible with Encoding and
|
||||||
* Case-insensitiveness. Eventually we'll want avoid
|
* Case-insensitiveness. Eventually we'll want avoid
|
||||||
|
@ -3872,6 +3886,22 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
retval = ext4_mark_inode_dirty(handle, old.dir);
|
retval = ext4_mark_inode_dirty(handle, old.dir);
|
||||||
if (unlikely(retval))
|
if (unlikely(retval))
|
||||||
goto end_rename;
|
goto end_rename;
|
||||||
|
|
||||||
|
if (S_ISDIR(old.inode->i_mode)) {
|
||||||
|
/*
|
||||||
|
* We disable fast commits here that's because the
|
||||||
|
* replay code is not yet capable of changing dot dot
|
||||||
|
* dirents in directories.
|
||||||
|
*/
|
||||||
|
ext4_fc_mark_ineligible(old.inode->i_sb,
|
||||||
|
EXT4_FC_REASON_RENAME_DIR);
|
||||||
|
} else {
|
||||||
|
if (new.inode)
|
||||||
|
ext4_fc_track_unlink(new.inode, new.dentry);
|
||||||
|
ext4_fc_track_link(old.inode, new.dentry);
|
||||||
|
ext4_fc_track_unlink(old.inode, old.dentry);
|
||||||
|
}
|
||||||
|
|
||||||
if (new.inode) {
|
if (new.inode) {
|
||||||
retval = ext4_mark_inode_dirty(handle, new.inode);
|
retval = ext4_mark_inode_dirty(handle, new.inode);
|
||||||
if (unlikely(retval))
|
if (unlikely(retval))
|
||||||
|
@ -4015,7 +4045,8 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
retval = ext4_mark_inode_dirty(handle, new.inode);
|
retval = ext4_mark_inode_dirty(handle, new.inode);
|
||||||
if (unlikely(retval))
|
if (unlikely(retval))
|
||||||
goto end_rename;
|
goto end_rename;
|
||||||
|
ext4_fc_mark_ineligible(new.inode->i_sb,
|
||||||
|
EXT4_FC_REASON_CROSS_RENAME);
|
||||||
if (old.dir_bh) {
|
if (old.dir_bh) {
|
||||||
retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
|
retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
|
|
@ -1331,6 +1331,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
|
||||||
ei->i_datasync_tid = 0;
|
ei->i_datasync_tid = 0;
|
||||||
atomic_set(&ei->i_unwritten, 0);
|
atomic_set(&ei->i_unwritten, 0);
|
||||||
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
|
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
|
||||||
|
ext4_fc_init_inode(&ei->vfs_inode);
|
||||||
|
mutex_init(&ei->i_fc_lock);
|
||||||
return &ei->vfs_inode;
|
return &ei->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1348,6 +1350,10 @@ static int ext4_drop_inode(struct inode *inode)
|
||||||
static void ext4_free_in_core_inode(struct inode *inode)
|
static void ext4_free_in_core_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
fscrypt_free_inode(inode);
|
fscrypt_free_inode(inode);
|
||||||
|
if (!list_empty(&(EXT4_I(inode)->i_fc_list))) {
|
||||||
|
pr_warn("%s: inode %ld still in fc list",
|
||||||
|
__func__, inode->i_ino);
|
||||||
|
}
|
||||||
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
|
kmem_cache_free(ext4_inode_cachep, EXT4_I(inode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1373,6 +1379,7 @@ static void init_once(void *foo)
|
||||||
init_rwsem(&ei->i_data_sem);
|
init_rwsem(&ei->i_data_sem);
|
||||||
init_rwsem(&ei->i_mmap_sem);
|
init_rwsem(&ei->i_mmap_sem);
|
||||||
inode_init_once(&ei->vfs_inode);
|
inode_init_once(&ei->vfs_inode);
|
||||||
|
ext4_fc_init_inode(&ei->vfs_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init init_inodecache(void)
|
static int __init init_inodecache(void)
|
||||||
|
@ -1401,6 +1408,7 @@ static void destroy_inodecache(void)
|
||||||
|
|
||||||
void ext4_clear_inode(struct inode *inode)
|
void ext4_clear_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
ext4_fc_del(inode);
|
||||||
invalidate_inode_buffers(inode);
|
invalidate_inode_buffers(inode);
|
||||||
clear_inode(inode);
|
clear_inode(inode);
|
||||||
ext4_discard_preallocations(inode, 0);
|
ext4_discard_preallocations(inode, 0);
|
||||||
|
@ -4744,6 +4752,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
|
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
|
||||||
mutex_init(&sbi->s_orphan_lock);
|
mutex_init(&sbi->s_orphan_lock);
|
||||||
|
|
||||||
|
/* Initialize fast commit stuff */
|
||||||
|
atomic_set(&sbi->s_fc_subtid, 0);
|
||||||
|
atomic_set(&sbi->s_fc_ineligible_updates, 0);
|
||||||
|
INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_MAIN]);
|
||||||
|
INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_STAGING]);
|
||||||
|
INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_MAIN]);
|
||||||
|
INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_STAGING]);
|
||||||
|
sbi->s_fc_bytes = 0;
|
||||||
|
sbi->s_mount_state &= ~EXT4_FC_INELIGIBLE;
|
||||||
|
sbi->s_mount_state &= ~EXT4_FC_COMMITTING;
|
||||||
|
spin_lock_init(&sbi->s_fc_lock);
|
||||||
|
memset(&sbi->s_fc_stats, 0, sizeof(sbi->s_fc_stats));
|
||||||
|
|
||||||
sb->s_root = NULL;
|
sb->s_root = NULL;
|
||||||
|
|
||||||
needs_recovery = (es->s_last_orphan != 0 ||
|
needs_recovery = (es->s_last_orphan != 0 ||
|
||||||
|
@ -6515,6 +6536,10 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
out:
|
out:
|
||||||
if (inode->i_size < off + len) {
|
if (inode->i_size < off + len) {
|
||||||
|
ext4_fc_track_range(inode,
|
||||||
|
(inode->i_size > 0 ? inode->i_size - 1 : 0)
|
||||||
|
>> inode->i_sb->s_blocksize_bits,
|
||||||
|
(off + len) >> inode->i_sb->s_blocksize_bits);
|
||||||
i_size_write(inode, off + len);
|
i_size_write(inode, off + len);
|
||||||
EXT4_I(inode)->i_disksize = inode->i_size;
|
EXT4_I(inode)->i_disksize = inode->i_size;
|
||||||
err2 = ext4_mark_inode_dirty(handle, inode);
|
err2 = ext4_mark_inode_dirty(handle, inode);
|
||||||
|
@ -6643,6 +6668,11 @@ static int __init ext4_init_fs(void)
|
||||||
err = init_inodecache();
|
err = init_inodecache();
|
||||||
if (err)
|
if (err)
|
||||||
goto out1;
|
goto out1;
|
||||||
|
|
||||||
|
err = ext4_fc_init_dentry_cache();
|
||||||
|
if (err)
|
||||||
|
goto out05;
|
||||||
|
|
||||||
register_as_ext3();
|
register_as_ext3();
|
||||||
register_as_ext2();
|
register_as_ext2();
|
||||||
err = register_filesystem(&ext4_fs_type);
|
err = register_filesystem(&ext4_fs_type);
|
||||||
|
@ -6653,6 +6683,7 @@ static int __init ext4_init_fs(void)
|
||||||
out:
|
out:
|
||||||
unregister_as_ext2();
|
unregister_as_ext2();
|
||||||
unregister_as_ext3();
|
unregister_as_ext3();
|
||||||
|
out05:
|
||||||
destroy_inodecache();
|
destroy_inodecache();
|
||||||
out1:
|
out1:
|
||||||
ext4_exit_mballoc();
|
ext4_exit_mballoc();
|
||||||
|
|
|
@ -2419,6 +2419,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
|
||||||
if (IS_SYNC(inode))
|
if (IS_SYNC(inode))
|
||||||
ext4_handle_sync(handle);
|
ext4_handle_sync(handle);
|
||||||
}
|
}
|
||||||
|
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
brelse(is.iloc.bh);
|
brelse(is.iloc.bh);
|
||||||
|
@ -2496,6 +2497,7 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
error = error2;
|
error = error2;
|
||||||
}
|
}
|
||||||
|
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -2928,6 +2930,7 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
||||||
error);
|
error);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
|
||||||
}
|
}
|
||||||
error = 0;
|
error = 0;
|
||||||
cleanup:
|
cleanup:
|
||||||
|
|
|
@ -95,6 +95,16 @@ TRACE_DEFINE_ENUM(ES_REFERENCED_B);
|
||||||
{ FALLOC_FL_COLLAPSE_RANGE, "COLLAPSE_RANGE"}, \
|
{ FALLOC_FL_COLLAPSE_RANGE, "COLLAPSE_RANGE"}, \
|
||||||
{ FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"})
|
{ FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"})
|
||||||
|
|
||||||
|
#define show_fc_reason(reason) \
|
||||||
|
__print_symbolic(reason, \
|
||||||
|
{ EXT4_FC_REASON_XATTR, "XATTR"}, \
|
||||||
|
{ EXT4_FC_REASON_CROSS_RENAME, "CROSS_RENAME"}, \
|
||||||
|
{ EXT4_FC_REASON_JOURNAL_FLAG_CHANGE, "JOURNAL_FLAG_CHANGE"}, \
|
||||||
|
{ EXT4_FC_REASON_MEM, "NO_MEM"}, \
|
||||||
|
{ EXT4_FC_REASON_SWAP_BOOT, "SWAP_BOOT"}, \
|
||||||
|
{ EXT4_FC_REASON_RESIZE, "RESIZE"}, \
|
||||||
|
{ EXT4_FC_REASON_RENAME_DIR, "RENAME_DIR"}, \
|
||||||
|
{ EXT4_FC_REASON_FALLOC_RANGE, "FALLOC_RANGE"})
|
||||||
|
|
||||||
TRACE_EVENT(ext4_other_inode_update_time,
|
TRACE_EVENT(ext4_other_inode_update_time,
|
||||||
TP_PROTO(struct inode *inode, ino_t orig_ino),
|
TP_PROTO(struct inode *inode, ino_t orig_ino),
|
||||||
|
@ -2791,6 +2801,168 @@ TRACE_EVENT(ext4_lazy_itable_init,
|
||||||
MAJOR(__entry->dev), MINOR(__entry->dev), __entry->group)
|
MAJOR(__entry->dev), MINOR(__entry->dev), __entry->group)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(ext4_fc_commit_start,
|
||||||
|
TP_PROTO(struct super_block *sb),
|
||||||
|
|
||||||
|
TP_ARGS(sb),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dev = sb->s_dev;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("fast_commit started on dev %d,%d",
|
||||||
|
MAJOR(__entry->dev), MINOR(__entry->dev))
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(ext4_fc_commit_stop,
|
||||||
|
TP_PROTO(struct super_block *sb, int nblks, int reason),
|
||||||
|
|
||||||
|
TP_ARGS(sb, nblks, reason),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
__field(int, nblks)
|
||||||
|
__field(int, reason)
|
||||||
|
__field(int, num_fc)
|
||||||
|
__field(int, num_fc_ineligible)
|
||||||
|
__field(int, nblks_agg)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dev = sb->s_dev;
|
||||||
|
__entry->nblks = nblks;
|
||||||
|
__entry->reason = reason;
|
||||||
|
__entry->num_fc = EXT4_SB(sb)->s_fc_stats.fc_num_commits;
|
||||||
|
__entry->num_fc_ineligible =
|
||||||
|
EXT4_SB(sb)->s_fc_stats.fc_ineligible_commits;
|
||||||
|
__entry->nblks_agg = EXT4_SB(sb)->s_fc_stats.fc_numblks;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("fc on [%d,%d] nblks %d, reason %d, fc = %d, ineligible = %d, agg_nblks %d",
|
||||||
|
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||||
|
__entry->nblks, __entry->reason, __entry->num_fc,
|
||||||
|
__entry->num_fc_ineligible, __entry->nblks_agg)
|
||||||
|
);
|
||||||
|
|
||||||
|
#define FC_REASON_NAME_STAT(reason) \
|
||||||
|
show_fc_reason(reason), \
|
||||||
|
__entry->sbi->s_fc_stats.fc_ineligible_reason_count[reason]
|
||||||
|
|
||||||
|
TRACE_EVENT(ext4_fc_stats,
|
||||||
|
TP_PROTO(struct super_block *sb),
|
||||||
|
|
||||||
|
TP_ARGS(sb),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
__field(struct ext4_sb_info *, sbi)
|
||||||
|
__field(int, count)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->dev = sb->s_dev;
|
||||||
|
__entry->sbi = EXT4_SB(sb);
|
||||||
|
),
|
||||||
|
|
||||||
|
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; "
|
||||||
|
"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_MEM),
|
||||||
|
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),
|
||||||
|
__entry->sbi->s_fc_stats.fc_num_commits,
|
||||||
|
__entry->sbi->s_fc_stats.fc_ineligible_commits,
|
||||||
|
__entry->sbi->s_fc_stats.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) \
|
||||||
|
)
|
||||||
|
|
||||||
|
DEFINE_TRACE_DENTRY_EVENT(create);
|
||||||
|
DEFINE_TRACE_DENTRY_EVENT(link);
|
||||||
|
DEFINE_TRACE_DENTRY_EVENT(unlink);
|
||||||
|
|
||||||
|
TRACE_EVENT(ext4_fc_track_inode,
|
||||||
|
TP_PROTO(struct inode *inode, int ret),
|
||||||
|
|
||||||
|
TP_ARGS(inode, 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",
|
||||||
|
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||||
|
__entry->ino, __entry->error)
|
||||||
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(ext4_fc_track_range,
|
||||||
|
TP_PROTO(struct inode *inode, long start, long end, int ret),
|
||||||
|
|
||||||
|
TP_ARGS(inode, start, end, ret),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(dev_t, dev)
|
||||||
|
__field(int, ino)
|
||||||
|
__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_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)
|
||||||
|
);
|
||||||
|
|
||||||
#endif /* _TRACE_EXT4_H */
|
#endif /* _TRACE_EXT4_H */
|
||||||
|
|
||||||
/* This part must be outside protection */
|
/* This part must be outside protection */
|
||||||
|
|
Loading…
Reference in New Issue