Commit 58130a6c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linus_fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "Bug fixes for the new ext4 fast commit feature, plus a fix for the
  'data=journal' bug fix.

  Also use the generic casefolding support which has now landed in
  fs/libfs.c for 5.10"

* tag 'ext4_for_linus_fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: indicate that fast_commit is available via /sys/fs/ext4/feature/...
  ext4: use generic casefolding support
  ext4: do not use extent after put_bh
  ext4: use IS_ERR() for error checking of path
  ext4: fix mmap write protection for data=journal mode
  jbd2: fix a kernel-doc markup
  ext4: use s_mount_flags instead of s_mount_state for fast commit state
  ext4: make num of fast commit blocks configurable
  ext4: properly check for dirty state in ext4_inode_datasync_dirty()
  ext4: fix double locking in ext4_fc_commit_dentry_updates()
parents 23859ae4 6694875e
Loading
Loading
Loading
Loading
+2 −62
Original line number Diff line number Diff line
@@ -669,68 +669,8 @@ const struct file_operations ext4_dir_operations = {
};

#ifdef CONFIG_UNICODE
static int ext4_d_compare(const struct dentry *dentry, unsigned int len,
			  const char *str, const struct qstr *name)
{
	struct qstr qstr = {.name = str, .len = len };
	const struct dentry *parent = READ_ONCE(dentry->d_parent);
	const struct inode *inode = d_inode_rcu(parent);
	char strbuf[DNAME_INLINE_LEN];

	if (!inode || !IS_CASEFOLDED(inode) ||
	    !EXT4_SB(inode->i_sb)->s_encoding) {
		if (len != name->len)
			return -1;
		return memcmp(str, name->name, len);
	}

	/*
	 * If the dentry name is stored in-line, then it may be concurrently
	 * modified by a rename.  If this happens, the VFS will eventually retry
	 * the lookup, so it doesn't matter what ->d_compare() returns.
	 * However, it's unsafe to call utf8_strncasecmp() with an unstable
	 * string.  Therefore, we have to copy the name into a temporary buffer.
	 */
	if (len <= DNAME_INLINE_LEN - 1) {
		memcpy(strbuf, str, len);
		strbuf[len] = 0;
		qstr.name = strbuf;
		/* prevent compiler from optimizing out the temporary buffer */
		barrier();
	}

	return ext4_ci_compare(inode, name, &qstr, false);
}

static int ext4_d_hash(const struct dentry *dentry, struct qstr *str)
{
	const struct ext4_sb_info *sbi = EXT4_SB(dentry->d_sb);
	const struct unicode_map *um = sbi->s_encoding;
	const struct inode *inode = d_inode_rcu(dentry);
	unsigned char *norm;
	int len, ret = 0;

	if (!inode || !IS_CASEFOLDED(inode) || !um)
		return 0;

	norm = kmalloc(PATH_MAX, GFP_ATOMIC);
	if (!norm)
		return -ENOMEM;

	len = utf8_casefold(um, str, norm, PATH_MAX);
	if (len < 0) {
		if (ext4_has_strict_mode(sbi))
			ret = -EINVAL;
		goto out;
	}
	str->hash = full_name_hash(dentry, norm, len);
out:
	kfree(norm);
	return ret;
}

const struct dentry_operations ext4_dentry_ops = {
	.d_hash = ext4_d_hash,
	.d_compare = ext4_d_compare,
	.d_hash = generic_ci_d_hash,
	.d_compare = generic_ci_d_compare,
};
#endif
+4 −16
Original line number Diff line number Diff line
@@ -1166,10 +1166,6 @@ 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.
						 */
#define EXT4_FC_REPLAY			0x0020	/* Fast commit replay ongoing */

/*
@@ -1431,6 +1427,10 @@ struct ext4_super_block {
 */
#define EXT4_MF_MNTDIR_SAMPLED		0x0001
#define EXT4_MF_FS_ABORTED		0x0002	/* Fatal error detected */
#define EXT4_MF_FC_INELIGIBLE		0x0004	/* Fast commit ineligible */
#define EXT4_MF_FC_COMMITTING		0x0008	/* File system underoing a fast
						 * commit.
						 */

#ifdef CONFIG_FS_ENCRYPTION
#define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_policy.policy != NULL)
@@ -1443,14 +1443,6 @@ struct ext4_super_block {

#define EXT4_ENC_UTF8_12_1	1

/*
 * Flags for ext4_sb_info.s_encoding_flags.
 */
#define EXT4_ENC_STRICT_MODE_FL	(1 << 0)

#define ext4_has_strict_mode(sbi) \
	(sbi->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL)

/*
 * fourth extended-fs super-block data in memory
 */
@@ -1500,10 +1492,6 @@ struct ext4_sb_info {
	struct kobject s_kobj;
	struct completion s_kobj_unregister;
	struct super_block *s_sb;
#ifdef CONFIG_UNICODE
	struct unicode_map *s_encoding;
	__u16 s_encoding_flags;
#endif

	/* Journaling */
	struct journal_s *s_journal;
+15 −15
Original line number Diff line number Diff line
@@ -1471,16 +1471,16 @@ static int ext4_ext_search_left(struct inode *inode,
}

/*
 * search the closest allocated block to the right for *logical
 * and returns it at @logical + it's physical address at @phys
 * if *logical is the largest allocated block, the function
 * returns 0 at @phys
 * return value contains 0 (success) or error code
 * Search the closest allocated block to the right for *logical
 * and returns it at @logical + it's physical address at @phys.
 * If not exists, return 0 and @phys is set to 0. We will return
 * 1 which means we found an allocated block and ret_ex is valid.
 * Or return a (< 0) error code.
 */
static int ext4_ext_search_right(struct inode *inode,
				 struct ext4_ext_path *path,
				 ext4_lblk_t *logical, ext4_fsblk_t *phys,
				 struct ext4_extent **ret_ex)
				 struct ext4_extent *ret_ex)
{
	struct buffer_head *bh = NULL;
	struct ext4_extent_header *eh;
@@ -1574,10 +1574,11 @@ static int ext4_ext_search_right(struct inode *inode,
found_extent:
	*logical = le32_to_cpu(ex->ee_block);
	*phys = ext4_ext_pblock(ex);
	*ret_ex = ex;
	if (ret_ex)
		*ret_ex = *ex;
	if (bh)
		put_bh(bh);
	return 0;
	return 1;
}

/*
@@ -2868,8 +2869,8 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
			 */
			lblk = ex_end + 1;
			err = ext4_ext_search_right(inode, path, &lblk, &pblk,
						    &ex);
			if (err)
						    NULL);
			if (err < 0)
				goto out;
			if (pblk) {
				partial.pclu = EXT4_B2C(sbi, pblk);
@@ -4039,7 +4040,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
			struct ext4_map_blocks *map, int flags)
{
	struct ext4_ext_path *path = NULL;
	struct ext4_extent newex, *ex, *ex2;
	struct ext4_extent newex, *ex, ex2;
	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
	ext4_fsblk_t newblock = 0, pblk;
	int err = 0, depth, ret;
@@ -4175,15 +4176,14 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
	if (err)
		goto out;
	ar.lright = map->m_lblk;
	ex2 = NULL;
	err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2);
	if (err)
	if (err < 0)
		goto out;

	/* Check if the extent after searching to the right implies a
	 * cluster we can use. */
	if ((sbi->s_cluster_ratio > 1) && ex2 &&
	    get_implied_cluster_alloc(inode->i_sb, map, ex2, path)) {
	if ((sbi->s_cluster_ratio > 1) && err &&
	    get_implied_cluster_alloc(inode->i_sb, map, &ex2, path)) {
		ar.len = allocated = map->m_len;
		newblock = map->m_pblk;
		goto got_allocated_blocks;
+24 −13
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason)
	    (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))
		return;

	sbi->s_mount_state |= EXT4_FC_INELIGIBLE;
	sbi->s_mount_flags |= EXT4_MF_FC_INELIGIBLE;
	WARN_ON(reason >= EXT4_FC_REASON_MAX);
	sbi->s_fc_stats.fc_ineligible_reason_count[reason]++;
}
@@ -292,7 +292,7 @@ void ext4_fc_start_ineligible(struct super_block *sb, int reason)
}

/*
 * Stop a fast commit ineligible update. We set EXT4_FC_INELIGIBLE flag here
 * Stop a fast commit ineligible update. We set EXT4_MF_FC_INELIGIBLE flag here
 * to ensure that after stopping the ineligible update, at least one full
 * commit takes place.
 */
@@ -302,13 +302,13 @@ void ext4_fc_stop_ineligible(struct super_block *sb)
	    (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))
		return;

	EXT4_SB(sb)->s_mount_state |= EXT4_FC_INELIGIBLE;
	EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FC_INELIGIBLE;
	atomic_dec(&EXT4_SB(sb)->s_fc_ineligible_updates);
}

static inline int ext4_fc_is_ineligible(struct super_block *sb)
{
	return (EXT4_SB(sb)->s_mount_state & EXT4_FC_INELIGIBLE) ||
	return (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FC_INELIGIBLE) ||
		atomic_read(&EXT4_SB(sb)->s_fc_ineligible_updates);
}

@@ -358,7 +358,7 @@ static int ext4_fc_track_template(
	spin_lock(&sbi->s_fc_lock);
	if (list_empty(&EXT4_I(inode)->i_fc_list))
		list_add_tail(&EXT4_I(inode)->i_fc_list,
				(sbi->s_mount_state & EXT4_FC_COMMITTING) ?
				(sbi->s_mount_flags & EXT4_MF_FC_COMMITTING) ?
				&sbi->s_fc_q[FC_Q_STAGING] :
				&sbi->s_fc_q[FC_Q_MAIN]);
	spin_unlock(&sbi->s_fc_lock);
@@ -411,7 +411,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
	node->fcd_name.len = dentry->d_name.len;

	spin_lock(&sbi->s_fc_lock);
	if (sbi->s_mount_state & EXT4_FC_COMMITTING)
	if (sbi->s_mount_flags & EXT4_MF_FC_COMMITTING)
		list_add_tail(&node->fcd_list,
				&sbi->s_fc_dentry_q[FC_Q_STAGING]);
	else
@@ -846,7 +846,7 @@ static int ext4_fc_submit_inode_data_all(journal_t *journal)
	int ret = 0;

	spin_lock(&sbi->s_fc_lock);
	sbi->s_mount_state |= EXT4_FC_COMMITTING;
	sbi->s_mount_flags |= EXT4_MF_FC_COMMITTING;
	list_for_each(pos, &sbi->s_fc_q[FC_Q_MAIN]) {
		ei = list_entry(pos, struct ext4_inode_info, i_fc_list);
		ext4_set_inode_state(&ei->vfs_inode, EXT4_STATE_FC_COMMITTING);
@@ -964,7 +964,6 @@ static int ext4_fc_commit_dentry_updates(journal_t *journal, u32 *crc)
			fc_dentry->fcd_parent, fc_dentry->fcd_ino,
			fc_dentry->fcd_name.len,
			fc_dentry->fcd_name.name, crc)) {
			spin_lock(&sbi->s_fc_lock);
			ret = -ENOSPC;
			goto lock_and_exit;
		}
@@ -1191,8 +1190,8 @@ static void ext4_fc_cleanup(journal_t *journal, int full)
	list_splice_init(&sbi->s_fc_q[FC_Q_STAGING],
				&sbi->s_fc_q[FC_Q_STAGING]);

	sbi->s_mount_state &= ~EXT4_FC_COMMITTING;
	sbi->s_mount_state &= ~EXT4_FC_INELIGIBLE;
	sbi->s_mount_flags &= ~EXT4_MF_FC_COMMITTING;
	sbi->s_mount_flags &= ~EXT4_MF_FC_INELIGIBLE;

	if (full)
		sbi->s_fc_bytes = 0;
@@ -1617,8 +1616,10 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
		if (ret == 0) {
			/* Range is not mapped */
			path = ext4_find_extent(inode, cur, NULL, 0);
			if (!path)
				continue;
			if (IS_ERR(path)) {
				iput(inode);
				return 0;
			}
			memset(&newex, 0, sizeof(newex));
			newex.ee_block = cpu_to_le32(cur);
			ext4_ext_store_pblock(
@@ -2078,6 +2079,8 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,

void ext4_fc_init(struct super_block *sb, journal_t *journal)
{
	int num_fc_blocks;

	/*
	 * We set replay callback even if fast commit disabled because we may
	 * could still have fast commit blocks that need to be replayed even if
@@ -2087,7 +2090,15 @@ void ext4_fc_init(struct super_block *sb, journal_t *journal)
	if (!test_opt2(sb, JOURNAL_FAST_COMMIT))
		return;
	journal->j_fc_cleanup_callback = ext4_fc_cleanup;
	if (jbd2_fc_init(journal, EXT4_NUM_FC_BLKS)) {
	if (!buffer_uptodate(journal->j_sb_buffer)
		&& ext4_read_bh_lock(journal->j_sb_buffer, REQ_META | REQ_PRIO,
					true)) {
		ext4_msg(sb, KERN_ERR, "I/O error on journal");
		return;
	}
	num_fc_blocks = be32_to_cpu(journal->j_superblock->s_num_fc_blks);
	if (jbd2_fc_init(journal, num_fc_blocks ? num_fc_blocks :
					EXT4_NUM_FC_BLKS)) {
		pr_warn("Error while enabling fast commits, turning off.");
		ext4_clear_feature_fast_commit(sb);
	}
+1 −1
Original line number Diff line number Diff line
@@ -275,7 +275,7 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
		   struct dx_hash_info *hinfo)
{
#ifdef CONFIG_UNICODE
	const struct unicode_map *um = EXT4_SB(dir->i_sb)->s_encoding;
	const struct unicode_map *um = dir->i_sb->s_encoding;
	int r, dlen;
	unsigned char *buff;
	struct qstr qstr = {.name = name, .len = len };
Loading