forked from mirrors/linux
		
	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
					
				
					 13 changed files with 1707 additions and 29 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	if (IS_ERR(handle))
 | 
			
		||||
		return PTR_ERR(handle);
 | 
			
		||||
	ext4_fc_start_update(inode);
 | 
			
		||||
 | 
			
		||||
	if ((type == ACL_TYPE_ACCESS) && 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:
 | 
			
		||||
	ext4_journal_stop(handle);
 | 
			
		||||
	ext4_fc_stop_update(inode);
 | 
			
		||||
	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
 | 
			
		||||
		goto retry;
 | 
			
		||||
	return error;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1021,6 +1021,31 @@ struct ext4_inode_info {
 | 
			
		|||
 | 
			
		||||
	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
 | 
			
		||||
	 * 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_ERROR_FS			0x0002	/* Errors detected */
 | 
			
		||||
#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
 | 
			
		||||
| 
						 | 
				
			
			@ -1613,6 +1642,30 @@ struct ext4_sb_info {
 | 
			
		|||
	/* Record the errseq of the backing block device */
 | 
			
		||||
	errseq_t s_bdev_wb_err;
 | 
			
		||||
	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)
 | 
			
		||||
| 
						 | 
				
			
			@ -1723,6 +1776,7 @@ enum {
 | 
			
		|||
	EXT4_STATE_EXT_PRECACHED,	/* extents have been precached */
 | 
			
		||||
	EXT4_STATE_LUSTRE_EA_INODE,	/* Lustre-style ea_inode */
 | 
			
		||||
	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)				\
 | 
			
		||||
| 
						 | 
				
			
			@ -2682,6 +2736,22 @@ extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
 | 
			
		|||
/* fast_commit.c */
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
extern const struct seq_operations ext4_mb_seq_groups_ops;
 | 
			
		||||
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);
 | 
			
		||||
out:
 | 
			
		||||
	ext4_ext_show_leaf(inode, path);
 | 
			
		||||
	ext4_fc_track_range(inode, ee_block, ee_block + ee_len - 1);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3794,6 +3795,7 @@ convert_initialized_extent(handle_t *handle, struct inode *inode,
 | 
			
		|||
	if (*allocated > map->m_len)
 | 
			
		||||
		*allocated = map->m_len;
 | 
			
		||||
	map->m_len = *allocated;
 | 
			
		||||
	ext4_fc_track_range(inode, ee_block, ee_block + ee_len - 1);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4327,7 +4329,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 | 
			
		|||
	map->m_len = ar.len;
 | 
			
		||||
	allocated = map->m_len;
 | 
			
		||||
	ext4_ext_show_leaf(inode, path);
 | 
			
		||||
 | 
			
		||||
	ext4_fc_track_range(inode, map->m_lblk, map->m_lblk + map->m_len - 1);
 | 
			
		||||
out:
 | 
			
		||||
	ext4_ext_drop_refs(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);
 | 
			
		||||
	if (unlikely(ret))
 | 
			
		||||
		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 */
 | 
			
		||||
	ret = ext4_zero_partial_blocks(handle, inode, offset, len);
 | 
			
		||||
	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_INSERT_RANGE))
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
	ext4_fc_track_range(inode, offset >> blkbits,
 | 
			
		||||
			(offset + len - 1) >> blkbits);
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_PUNCH_HOLE)
 | 
			
		||||
		return ext4_punch_hole(inode, offset, len);
 | 
			
		||||
	ext4_fc_start_update(inode);
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_PUNCH_HOLE) {
 | 
			
		||||
		ret = ext4_punch_hole(inode, offset, len);
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = ext4_convert_inline_data(inode);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_COLLAPSE_RANGE)
 | 
			
		||||
		return ext4_collapse_range(inode, offset, len);
 | 
			
		||||
	if (mode & FALLOC_FL_COLLAPSE_RANGE) {
 | 
			
		||||
		ret = ext4_collapse_range(inode, offset, len);
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_INSERT_RANGE)
 | 
			
		||||
		return ext4_insert_range(inode, offset, len);
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_ZERO_RANGE)
 | 
			
		||||
		return ext4_zero_range(file, offset, len, mode);
 | 
			
		||||
	if (mode & FALLOC_FL_INSERT_RANGE) {
 | 
			
		||||
		ret = ext4_insert_range(inode, offset, len);
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mode & FALLOC_FL_ZERO_RANGE) {
 | 
			
		||||
		ret = ext4_zero_range(file, offset, len, mode);
 | 
			
		||||
		goto exit;
 | 
			
		||||
	}
 | 
			
		||||
	trace_ext4_fallocate_enter(inode, offset, len, mode);
 | 
			
		||||
	lblk = offset >> blkbits;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4698,12 +4712,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
 | 
			
		|||
		goto out;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	inode_unlock(inode);
 | 
			
		||||
	trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
 | 
			
		||||
exit:
 | 
			
		||||
	ext4_fc_stop_update(inode);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5291,6 +5307,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
 | 
			
		|||
		ret = PTR_ERR(handle);
 | 
			
		||||
		goto out_mmap;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_start_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
 | 
			
		||||
 | 
			
		||||
	down_write(&EXT4_I(inode)->i_data_sem);
 | 
			
		||||
	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:
 | 
			
		||||
	ext4_journal_stop(handle);
 | 
			
		||||
	ext4_fc_stop_ineligible(sb);
 | 
			
		||||
out_mmap:
 | 
			
		||||
	up_write(&EXT4_I(inode)->i_mmap_sem);
 | 
			
		||||
out_mutex:
 | 
			
		||||
| 
						 | 
				
			
			@ -5429,6 +5447,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
 | 
			
		|||
		ret = PTR_ERR(handle);
 | 
			
		||||
		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 */
 | 
			
		||||
	inode->i_size += len;
 | 
			
		||||
| 
						 | 
				
			
			@ -5503,6 +5522,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
 | 
			
		|||
 | 
			
		||||
out_stop:
 | 
			
		||||
	ext4_journal_stop(handle);
 | 
			
		||||
	ext4_fc_stop_ineligible(sb);
 | 
			
		||||
out_mmap:
 | 
			
		||||
	up_write(&EXT4_I(inode)->i_mmap_sem);
 | 
			
		||||
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 */
 | 
			
		||||
#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__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -260,6 +260,7 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
 | 
			
		|||
	if (iocb->ki_flags & IOCB_NOWAIT)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	ext4_fc_start_update(inode);
 | 
			
		||||
	inode_lock(inode);
 | 
			
		||||
	ret = ext4_write_checks(iocb, from);
 | 
			
		||||
	if (ret <= 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -271,6 +272,7 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
 | 
			
		|||
 | 
			
		||||
out:
 | 
			
		||||
	inode_unlock(inode);
 | 
			
		||||
	ext4_fc_stop_update(inode);
 | 
			
		||||
	if (likely(ret > 0)) {
 | 
			
		||||
		iocb->ki_pos += 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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ext4_fc_start_update(inode);
 | 
			
		||||
		ret = ext4_orphan_add(handle, inode);
 | 
			
		||||
		ext4_fc_stop_update(inode);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			ext4_journal_stop(handle);
 | 
			
		||||
			goto out;
 | 
			
		||||
| 
						 | 
				
			
			@ -656,7 +660,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 | 
			
		|||
#endif
 | 
			
		||||
	if (iocb->ki_flags & IOCB_DIRECT)
 | 
			
		||||
		return ext4_dio_write_iter(iocb, from);
 | 
			
		||||
 | 
			
		||||
	else
 | 
			
		||||
		return ext4_buffered_write_iter(iocb, from);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -757,6 +761,7 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
 | 
			
		|||
	if (!daxdev_mapping_supported(vma, dax_dev))
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	ext4_fc_start_update(inode);
 | 
			
		||||
	file_accessed(file);
 | 
			
		||||
	if (IS_DAX(file_inode(file))) {
 | 
			
		||||
		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 {
 | 
			
		||||
		vma->vm_ops = &ext4_file_vm_ops;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_stop_update(inode);
 | 
			
		||||
	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))
 | 
			
		||||
		*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)
 | 
			
		||||
				return ret;
 | 
			
		||||
		}
 | 
			
		||||
		ext4_fc_track_range(inode, map->m_lblk,
 | 
			
		||||
			    map->m_lblk + map->m_len - 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	if (journal)
 | 
			
		||||
		return !jbd2_transaction_committed(journal,
 | 
			
		||||
					EXT4_I(inode)->i_datasync_tid);
 | 
			
		||||
	if (journal) {
 | 
			
		||||
		if (jbd2_transaction_committed(journal,
 | 
			
		||||
					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? */
 | 
			
		||||
	if (!list_empty(&inode->i_mapping->private_list))
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_track_range(inode, first_block, stop_block);
 | 
			
		||||
	if (IS_SYNC(inode))
 | 
			
		||||
		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++)
 | 
			
		||||
		ei->i_data[block] = raw_inode->i_block[block];
 | 
			
		||||
	INIT_LIST_HEAD(&ei->i_orphan);
 | 
			
		||||
	ext4_fc_init_inode(&ei->vfs_inode);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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)
 | 
			
		||||
			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);
 | 
			
		||||
	} else {
 | 
			
		||||
		struct ext4_iloc iloc;
 | 
			
		||||
| 
						 | 
				
			
			@ -5291,6 +5300,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 | 
			
		|||
		if (error)
 | 
			
		||||
			return error;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_start_update(inode);
 | 
			
		||||
	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))) {
 | 
			
		||||
		handle_t *handle;
 | 
			
		||||
| 
						 | 
				
			
			@ -5314,6 +5324,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 | 
			
		|||
 | 
			
		||||
		if (error) {
 | 
			
		||||
			ext4_journal_stop(handle);
 | 
			
		||||
			ext4_fc_stop_update(inode);
 | 
			
		||||
			return error;
 | 
			
		||||
		}
 | 
			
		||||
		/* 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))) {
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
		if (!S_ISREG(inode->i_mode))
 | 
			
		||||
		}
 | 
			
		||||
		if (!S_ISREG(inode->i_mode)) {
 | 
			
		||||
			ext4_fc_stop_update(inode);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
 | 
			
		||||
			inode_inc_iversion(inode);
 | 
			
		||||
| 
						 | 
				
			
			@ -5364,7 +5379,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 | 
			
		|||
		rc = ext4_break_layouts(inode);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			up_write(&EXT4_I(inode)->i_mmap_sem);
 | 
			
		||||
			return rc;
 | 
			
		||||
			goto err_out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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_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);
 | 
			
		||||
			EXT4_I(inode)->i_disksize = attr->ia_size;
 | 
			
		||||
			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);
 | 
			
		||||
 | 
			
		||||
err_out:
 | 
			
		||||
	if  (error)
 | 
			
		||||
		ext4_std_error(inode->i_sb, error);
 | 
			
		||||
	if (!error)
 | 
			
		||||
		error = rc;
 | 
			
		||||
	ext4_fc_stop_update(inode);
 | 
			
		||||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5627,6 +5659,8 @@ int ext4_mark_iloc_dirty(handle_t *handle,
 | 
			
		|||
		put_bh(iloc->bh);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_track_inode(inode);
 | 
			
		||||
 | 
			
		||||
	if (IS_I_VERSION(inode))
 | 
			
		||||
		inode_inc_iversion(inode);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5950,6 +5984,8 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
 | 
			
		|||
	if (IS_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);
 | 
			
		||||
	ext4_handle_sync(handle);
 | 
			
		||||
	ext4_journal_stop(handle);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -165,6 +165,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
 | 
			
		|||
		err = -EINVAL;
 | 
			
		||||
		goto err_out;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_start_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT);
 | 
			
		||||
 | 
			
		||||
	/* Protect extent tree against block allocations via delalloc */
 | 
			
		||||
	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:
 | 
			
		||||
	ext4_journal_stop(handle);
 | 
			
		||||
	ext4_fc_stop_ineligible(sb);
 | 
			
		||||
	ext4_double_up_write_data_sem(inode, inode_bl);
 | 
			
		||||
 | 
			
		||||
err_out:
 | 
			
		||||
| 
						 | 
				
			
			@ -807,7 +809,7 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
 | 
			
		|||
	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 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);
 | 
			
		||||
		if (EXT4_SB(sb)->s_journal) {
 | 
			
		||||
			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE);
 | 
			
		||||
			jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
 | 
			
		||||
			err2 = jbd2_journal_flush(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
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	handle_t *handle;
 | 
			
		||||
	struct inode *inode;
 | 
			
		||||
	struct inode *inode, *inode_save;
 | 
			
		||||
	int err, credits, retries = 0;
 | 
			
		||||
 | 
			
		||||
	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_fop = &ext4_file_operations;
 | 
			
		||||
		ext4_set_aops(inode);
 | 
			
		||||
		inode_save = inode;
 | 
			
		||||
		ihold(inode_save);
 | 
			
		||||
		err = ext4_add_nondir(handle, dentry, &inode);
 | 
			
		||||
		ext4_fc_track_create(inode_save, dentry);
 | 
			
		||||
		iput(inode_save);
 | 
			
		||||
	}
 | 
			
		||||
	if (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)
 | 
			
		||||
{
 | 
			
		||||
	handle_t *handle;
 | 
			
		||||
	struct inode *inode;
 | 
			
		||||
	struct inode *inode, *inode_save;
 | 
			
		||||
	int err, credits, retries = 0;
 | 
			
		||||
 | 
			
		||||
	err = dquot_initialize(dir);
 | 
			
		||||
| 
						 | 
				
			
			@ -2661,7 +2665,12 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
 | 
			
		|||
	if (!IS_ERR(inode)) {
 | 
			
		||||
		init_special_inode(inode, inode->i_mode, rdev);
 | 
			
		||||
		inode->i_op = &ext4_special_inode_operations;
 | 
			
		||||
		inode_save = inode;
 | 
			
		||||
		ihold(inode_save);
 | 
			
		||||
		err = ext4_add_nondir(handle, dentry, &inode);
 | 
			
		||||
		if (!err)
 | 
			
		||||
			ext4_fc_track_create(inode_save, dentry);
 | 
			
		||||
		iput(inode_save);
 | 
			
		||||
	}
 | 
			
		||||
	if (handle)
 | 
			
		||||
		ext4_journal_stop(handle);
 | 
			
		||||
| 
						 | 
				
			
			@ -2825,7 +2834,9 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 | 
			
		|||
		iput(inode);
 | 
			
		||||
		goto out_retry;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_track_create(inode, dentry);
 | 
			
		||||
	ext4_inc_count(dir);
 | 
			
		||||
 | 
			
		||||
	ext4_update_dx_flag(dir);
 | 
			
		||||
	err = ext4_mark_inode_dirty(handle, dir);
 | 
			
		||||
	if (err)
 | 
			
		||||
| 
						 | 
				
			
			@ -3165,6 +3176,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
 | 
			
		|||
		goto end_rmdir;
 | 
			
		||||
	ext4_dec_count(dir);
 | 
			
		||||
	ext4_update_dx_flag(dir);
 | 
			
		||||
	ext4_fc_track_unlink(inode, dentry);
 | 
			
		||||
	retval = ext4_mark_inode_dirty(handle, dir);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_UNICODE
 | 
			
		||||
| 
						 | 
				
			
			@ -3251,6 +3263,8 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
 | 
			
		|||
	inode->i_ctime = current_time(inode);
 | 
			
		||||
	retval = ext4_mark_inode_dirty(handle, inode);
 | 
			
		||||
 | 
			
		||||
	if (!retval)
 | 
			
		||||
		ext4_fc_track_unlink(d_inode(dentry), dentry);
 | 
			
		||||
#ifdef CONFIG_UNICODE
 | 
			
		||||
	/* VFS negative dentries are incompatible with Encoding and
 | 
			
		||||
	 * 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);
 | 
			
		||||
	if (unlikely(retval))
 | 
			
		||||
		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) {
 | 
			
		||||
		retval = ext4_mark_inode_dirty(handle, new.inode);
 | 
			
		||||
		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);
 | 
			
		||||
	if (unlikely(retval))
 | 
			
		||||
		goto end_rename;
 | 
			
		||||
 | 
			
		||||
	ext4_fc_mark_ineligible(new.inode->i_sb,
 | 
			
		||||
				EXT4_FC_REASON_CROSS_RENAME);
 | 
			
		||||
	if (old.dir_bh) {
 | 
			
		||||
		retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
 | 
			
		||||
		if (retval)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1331,6 +1331,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
 | 
			
		|||
	ei->i_datasync_tid = 0;
 | 
			
		||||
	atomic_set(&ei->i_unwritten, 0);
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1348,6 +1350,10 @@ static int ext4_drop_inode(struct inode *inode)
 | 
			
		|||
static void ext4_free_in_core_inode(struct 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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1373,6 +1379,7 @@ static void init_once(void *foo)
 | 
			
		|||
	init_rwsem(&ei->i_data_sem);
 | 
			
		||||
	init_rwsem(&ei->i_mmap_sem);
 | 
			
		||||
	inode_init_once(&ei->vfs_inode);
 | 
			
		||||
	ext4_fc_init_inode(&ei->vfs_inode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init init_inodecache(void)
 | 
			
		||||
| 
						 | 
				
			
			@ -1401,6 +1408,7 @@ static void destroy_inodecache(void)
 | 
			
		|||
 | 
			
		||||
void ext4_clear_inode(struct inode *inode)
 | 
			
		||||
{
 | 
			
		||||
	ext4_fc_del(inode);
 | 
			
		||||
	invalidate_inode_buffers(inode);
 | 
			
		||||
	clear_inode(inode);
 | 
			
		||||
	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 */
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
out:
 | 
			
		||||
	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);
 | 
			
		||||
		EXT4_I(inode)->i_disksize = inode->i_size;
 | 
			
		||||
		err2 = ext4_mark_inode_dirty(handle, inode);
 | 
			
		||||
| 
						 | 
				
			
			@ -6643,6 +6668,11 @@ static int __init ext4_init_fs(void)
 | 
			
		|||
	err = init_inodecache();
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto out1;
 | 
			
		||||
 | 
			
		||||
	err = ext4_fc_init_dentry_cache();
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto out05;
 | 
			
		||||
 | 
			
		||||
	register_as_ext3();
 | 
			
		||||
	register_as_ext2();
 | 
			
		||||
	err = register_filesystem(&ext4_fs_type);
 | 
			
		||||
| 
						 | 
				
			
			@ -6653,6 +6683,7 @@ static int __init ext4_init_fs(void)
 | 
			
		|||
out:
 | 
			
		||||
	unregister_as_ext2();
 | 
			
		||||
	unregister_as_ext3();
 | 
			
		||||
out05:
 | 
			
		||||
	destroy_inodecache();
 | 
			
		||||
out1:
 | 
			
		||||
	ext4_exit_mballoc();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2419,6 +2419,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
 | 
			
		|||
		if (IS_SYNC(inode))
 | 
			
		||||
			ext4_handle_sync(handle);
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
 | 
			
		||||
 | 
			
		||||
cleanup:
 | 
			
		||||
	brelse(is.iloc.bh);
 | 
			
		||||
| 
						 | 
				
			
			@ -2496,6 +2497,7 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
 | 
			
		|||
		if (error == 0)
 | 
			
		||||
			error = error2;
 | 
			
		||||
	}
 | 
			
		||||
	ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
 | 
			
		||||
 | 
			
		||||
	return error;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2928,6 +2930,7 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
 | 
			
		|||
					 error);
 | 
			
		||||
			goto cleanup;
 | 
			
		||||
		}
 | 
			
		||||
		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
 | 
			
		||||
	}
 | 
			
		||||
	error = 0;
 | 
			
		||||
cleanup:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,6 +95,16 @@ TRACE_DEFINE_ENUM(ES_REFERENCED_B);
 | 
			
		|||
	{ FALLOC_FL_COLLAPSE_RANGE,	"COLLAPSE_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,
 | 
			
		||||
	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)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
 | 
			
		||||
/* This part must be outside protection */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue