diff mbox series

[f2fs-dev] f2fs: fix potential deadlock by reordering w/ i_sem

Message ID 20230710061021.2303432-1-chao@kernel.org (mailing list archive)
State New
Headers show
Series [f2fs-dev] f2fs: fix potential deadlock by reordering w/ i_sem | expand

Commit Message

Chao Yu July 10, 2023, 6:10 a.m. UTC
syzbot reports deadlock bug as below:

-> #1 (&fi->i_sem){+.+.}-{3:3}:
       down_write+0x3a/0x50 kernel/locking/rwsem.c:1573
       f2fs_down_write fs/f2fs/f2fs.h:2133 [inline]
       f2fs_add_inline_entry+0x3a8/0x760 fs/f2fs/inline.c:644
       f2fs_add_dentry+0xba/0x1e0 fs/f2fs/dir.c:784
       f2fs_do_add_link+0x21e/0x340 fs/f2fs/dir.c:827
       f2fs_add_link fs/f2fs/f2fs.h:3554 [inline]
       f2fs_create+0x32c/0x530 fs/f2fs/namei.c:377
       lookup_open fs/namei.c:3492 [inline]
       open_last_lookups fs/namei.c:3560 [inline]
       path_openat+0x13e7/0x3180 fs/namei.c:3790
       do_filp_open+0x234/0x490 fs/namei.c:3820
       do_sys_openat2+0x13e/0x1d0 fs/open.c:1407
       do_sys_open fs/open.c:1422 [inline]
       __do_sys_open fs/open.c:1430 [inline]
       __se_sys_open fs/open.c:1426 [inline]
       __x64_sys_open+0x225/0x270 fs/open.c:1426
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x63/0xcd

-> #0 (&fi->i_xattr_sem){.+.+}-{3:3}:
       check_prev_add kernel/locking/lockdep.c:3142 [inline]
       check_prevs_add kernel/locking/lockdep.c:3261 [inline]
       validate_chain kernel/locking/lockdep.c:3876 [inline]
       __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144
       lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761
       down_read+0x47/0x2f0 kernel/locking/rwsem.c:1520
       f2fs_down_read fs/f2fs/f2fs.h:2108 [inline]
       f2fs_getxattr+0xb8/0x1460 fs/f2fs/xattr.c:532
       __f2fs_get_acl+0x52/0x8e0 fs/f2fs/acl.c:179
       f2fs_acl_create fs/f2fs/acl.c:377 [inline]
       f2fs_init_acl+0xd7/0x9a0 fs/f2fs/acl.c:420
       f2fs_init_inode_metadata+0x824/0x1190 fs/f2fs/dir.c:558
       f2fs_do_tmpfile+0x34/0x170 fs/f2fs/dir.c:839
       __f2fs_tmpfile+0x1f9/0x380 fs/f2fs/namei.c:884
       f2fs_ioc_start_atomic_write+0x4a3/0x9e0 fs/f2fs/file.c:2099
       __f2fs_ioctl+0x1b5c/0xb770
       vfs_ioctl fs/ioctl.c:51 [inline]
       __do_sys_ioctl fs/ioctl.c:870 [inline]
       __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x63/0xcd

The root cause is as below reversed lock order:
- f2fs_create
 - f2fs_add_dentry
  - f2fs_down_write(&F2FS_I(dir)->i_xattr_sem)
						- f2fs_ioc_start_atomic_write
						 - __f2fs_tmpfile
						  - f2fs_do_tmpfile
						   - f2fs_down_write(&F2FS_I(inode)->i_sem)
						   - f2fs_init_inode_metadata
						    - f2fs_init_acl
						     - __f2fs_get_acl
						      - f2fs_getxattr
						       - f2fs_down_read(&F2FS_I(dir)->i_xattr_sem)
  - f2fs_add_inline_entry
   - f2fs_down_write(&F2FS_I(inode)->i_sem)

We can break the dependency of deadlock by below change:
- use i_sem to keep order of {get,set}xattr instead of i_xattr_sem
- keep below lock order in inode operation to avoid deadlock:
 dir->i_sem -> inode->i_sem
 dir->i_sem -> dpage_lock

Fixes: 5eda1ad1aaff ("f2fs: fix deadlock in i_xattr_sem and inode page lock")
Reported-by: syzbot+e5600587fa9cbf8e3826@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-f2fs-devel/00000000000096797d06001a359d@google.com
Signed-off-by: Chao Yu <chao@kernel.org>
---
 fs/f2fs/acl.c    | 36 +++++++++++++++++++-----------------
 fs/f2fs/acl.h    |  4 ++--
 fs/f2fs/dir.c    | 22 ++++++++++++++++------
 fs/f2fs/f2fs.h   |  1 -
 fs/f2fs/super.c  |  5 ++---
 fs/f2fs/verity.c |  5 +++--
 fs/f2fs/xattr.c  | 37 +++++++++++++++++++++----------------
 fs/f2fs/xattr.h  | 19 +++++++++++++------
 8 files changed, 76 insertions(+), 53 deletions(-)

Comments

Jaegeuk Kim July 10, 2023, 5:18 p.m. UTC | #1
On 07/10, Chao Yu wrote:
> syzbot reports deadlock bug as below:
> 
> -> #1 (&fi->i_sem){+.+.}-{3:3}:
>        down_write+0x3a/0x50 kernel/locking/rwsem.c:1573
>        f2fs_down_write fs/f2fs/f2fs.h:2133 [inline]
>        f2fs_add_inline_entry+0x3a8/0x760 fs/f2fs/inline.c:644
>        f2fs_add_dentry+0xba/0x1e0 fs/f2fs/dir.c:784
>        f2fs_do_add_link+0x21e/0x340 fs/f2fs/dir.c:827
>        f2fs_add_link fs/f2fs/f2fs.h:3554 [inline]
>        f2fs_create+0x32c/0x530 fs/f2fs/namei.c:377
>        lookup_open fs/namei.c:3492 [inline]
>        open_last_lookups fs/namei.c:3560 [inline]
>        path_openat+0x13e7/0x3180 fs/namei.c:3790
>        do_filp_open+0x234/0x490 fs/namei.c:3820
>        do_sys_openat2+0x13e/0x1d0 fs/open.c:1407
>        do_sys_open fs/open.c:1422 [inline]
>        __do_sys_open fs/open.c:1430 [inline]
>        __se_sys_open fs/open.c:1426 [inline]
>        __x64_sys_open+0x225/0x270 fs/open.c:1426
>        do_syscall_x64 arch/x86/entry/common.c:50 [inline]
>        do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
>        entry_SYSCALL_64_after_hwframe+0x63/0xcd
> 
> -> #0 (&fi->i_xattr_sem){.+.+}-{3:3}:
>        check_prev_add kernel/locking/lockdep.c:3142 [inline]
>        check_prevs_add kernel/locking/lockdep.c:3261 [inline]
>        validate_chain kernel/locking/lockdep.c:3876 [inline]
>        __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144
>        lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761
>        down_read+0x47/0x2f0 kernel/locking/rwsem.c:1520
>        f2fs_down_read fs/f2fs/f2fs.h:2108 [inline]
>        f2fs_getxattr+0xb8/0x1460 fs/f2fs/xattr.c:532
>        __f2fs_get_acl+0x52/0x8e0 fs/f2fs/acl.c:179
>        f2fs_acl_create fs/f2fs/acl.c:377 [inline]
>        f2fs_init_acl+0xd7/0x9a0 fs/f2fs/acl.c:420
>        f2fs_init_inode_metadata+0x824/0x1190 fs/f2fs/dir.c:558
>        f2fs_do_tmpfile+0x34/0x170 fs/f2fs/dir.c:839
>        __f2fs_tmpfile+0x1f9/0x380 fs/f2fs/namei.c:884
>        f2fs_ioc_start_atomic_write+0x4a3/0x9e0 fs/f2fs/file.c:2099
>        __f2fs_ioctl+0x1b5c/0xb770
>        vfs_ioctl fs/ioctl.c:51 [inline]
>        __do_sys_ioctl fs/ioctl.c:870 [inline]
>        __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856
>        do_syscall_x64 arch/x86/entry/common.c:50 [inline]
>        do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
>        entry_SYSCALL_64_after_hwframe+0x63/0xcd
> 
> The root cause is as below reversed lock order:
> - f2fs_create
>  - f2fs_add_dentry
>   - f2fs_down_write(&F2FS_I(dir)->i_xattr_sem)
> 						- f2fs_ioc_start_atomic_write
> 						 - __f2fs_tmpfile
> 						  - f2fs_do_tmpfile
> 						   - f2fs_down_write(&F2FS_I(inode)->i_sem)
> 						   - f2fs_init_inode_metadata
> 						    - f2fs_init_acl
> 						     - __f2fs_get_acl
> 						      - f2fs_getxattr
> 						       - f2fs_down_read(&F2FS_I(dir)->i_xattr_sem)
>   - f2fs_add_inline_entry
>    - f2fs_down_write(&F2FS_I(inode)->i_sem)

How is it possible to get two inode are identical?

> 
> We can break the dependency of deadlock by below change:
> - use i_sem to keep order of {get,set}xattr instead of i_xattr_sem
> - keep below lock order in inode operation to avoid deadlock:
>  dir->i_sem -> inode->i_sem
>  dir->i_sem -> dpage_lock
> 
> Fixes: 5eda1ad1aaff ("f2fs: fix deadlock in i_xattr_sem and inode page lock")
> Reported-by: syzbot+e5600587fa9cbf8e3826@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/linux-f2fs-devel/00000000000096797d06001a359d@google.com
> Signed-off-by: Chao Yu <chao@kernel.org>
> ---
>  fs/f2fs/acl.c    | 36 +++++++++++++++++++-----------------
>  fs/f2fs/acl.h    |  4 ++--
>  fs/f2fs/dir.c    | 22 ++++++++++++++++------
>  fs/f2fs/f2fs.h   |  1 -
>  fs/f2fs/super.c  |  5 ++---
>  fs/f2fs/verity.c |  5 +++--
>  fs/f2fs/xattr.c  | 37 +++++++++++++++++++++----------------
>  fs/f2fs/xattr.h  | 19 +++++++++++++------
>  8 files changed, 76 insertions(+), 53 deletions(-)
> 
> diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
> index ec2aeccb69a3..af58b38e953c 100644
> --- a/fs/f2fs/acl.c
> +++ b/fs/f2fs/acl.c
> @@ -166,7 +166,7 @@ static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
>  }
>  
>  static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
> -						struct page *dpage)
> +						struct page *dpage, bool locked)
>  {
>  	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
>  	void *value = NULL;
> @@ -176,13 +176,13 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
>  	if (type == ACL_TYPE_ACCESS)
>  		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
>  
> -	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
> +	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage, locked);
>  	if (retval > 0) {
>  		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
>  		if (!value)
>  			return ERR_PTR(-ENOMEM);
>  		retval = f2fs_getxattr(inode, name_index, "", value,
> -							retval, dpage);
> +							retval, dpage, locked);
>  	}
>  
>  	if (retval > 0)
> @@ -201,7 +201,7 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
>  	if (rcu)
>  		return ERR_PTR(-ECHILD);
>  
> -	return __f2fs_get_acl(inode, type, NULL);
> +	return __f2fs_get_acl(inode, type, NULL, false);
>  }
>  
>  static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
> @@ -226,9 +226,9 @@ static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
>  	return 0;
>  }
>  
> -static int __f2fs_set_acl(struct mnt_idmap *idmap,
> -			struct inode *inode, int type,
> -			struct posix_acl *acl, struct page *ipage)
> +static int __f2fs_set_acl(struct mnt_idmap *idmap, struct inode *inode,
> +					int type, struct posix_acl *acl,
> +					struct page *ipage, bool locked)
>  {
>  	int name_index;
>  	void *value = NULL;
> @@ -266,7 +266,8 @@ static int __f2fs_set_acl(struct mnt_idmap *idmap,
>  		}
>  	}
>  
> -	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
> +	error = f2fs_setxattr(inode, name_index, "", value, size,
> +						ipage, 0, locked);
>  
>  	kfree(value);
>  	if (!error)
> @@ -284,7 +285,7 @@ int f2fs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
>  	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
>  		return -EIO;
>  
> -	return __f2fs_set_acl(idmap, inode, type, acl, NULL);
> +	return __f2fs_set_acl(idmap, inode, type, acl, NULL, false);
>  }
>  
>  /*
> @@ -362,7 +363,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
>  
>  static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>  		struct posix_acl **default_acl, struct posix_acl **acl,
> -		struct page *dpage)
> +		struct page *dpage, bool locked)
>  {
>  	struct posix_acl *p;
>  	struct posix_acl *clone;
> @@ -374,7 +375,7 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>  	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
>  		return 0;
>  
> -	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
> +	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage, locked);
>  	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
>  		*mode &= ~current_umask();
>  		return 0;
> @@ -412,28 +413,29 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>  }
>  
>  int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
> -							struct page *dpage)
> +						struct page *dpage, bool locked)
>  {
>  	struct posix_acl *default_acl = NULL, *acl = NULL;
>  	int error;
>  
> -	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
> +	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl,
> +							dpage, locked);
>  	if (error)
>  		return error;
>  
>  	f2fs_mark_inode_dirty_sync(inode, true);
>  
>  	if (default_acl) {
> -		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,
> -				       ipage);
> +		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT,
> +					default_acl, ipage, locked);
>  		posix_acl_release(default_acl);
>  	} else {
>  		inode->i_default_acl = NULL;
>  	}
>  	if (acl) {
>  		if (!error)
> -			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,
> -					       ipage);
> +			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS,
> +							acl, ipage, locked);
>  		posix_acl_release(acl);
>  	} else {
>  		inode->i_acl = NULL;
> diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h
> index 94ebfbfbdc6f..9c14b6f549c6 100644
> --- a/fs/f2fs/acl.h
> +++ b/fs/f2fs/acl.h
> @@ -37,13 +37,13 @@ extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
>  extern int f2fs_set_acl(struct mnt_idmap *, struct dentry *,
>  			struct posix_acl *, int);
>  extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
> -							struct page *);
> +							struct page *, bool);
>  #else
>  #define f2fs_get_acl	NULL
>  #define f2fs_set_acl	NULL
>  
>  static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
> -				struct page *ipage, struct page *dpage)
> +			struct page *ipage, struct page *dpage, bool locked)
>  {
>  	return 0;
>  }
> diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
> index d635c58cf5a3..4b5c62e18d67 100644
> --- a/fs/f2fs/dir.c
> +++ b/fs/f2fs/dir.c
> @@ -540,6 +540,8 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
>  	int err;
>  
>  	if (is_inode_flag_set(inode, FI_NEW_INODE)) {
> +		struct f2fs_xattr_arg xarg;
> +
>  		page = f2fs_new_inode_page(inode);
>  		if (IS_ERR(page))
>  			return page;
> @@ -555,12 +557,16 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
>  			put_page(page);
>  		}
>  
> -		err = f2fs_init_acl(inode, dir, page, dpage);
> +		err = f2fs_init_acl(inode, dir, page, dpage, true);
>  		if (err)
>  			goto put_error;
>  
> +		xarg.page = page;
> +		xarg.locked = true;
> +
>  		err = f2fs_init_security(inode, dir,
> -					 fname ? fname->usr_fname : NULL, page);
> +					 fname ? fname->usr_fname : NULL,
> +					 &xarg);
>  		if (err)
>  			goto put_error;
>  
> @@ -775,18 +781,20 @@ int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
>  {
>  	int err = -EAGAIN;
>  
> +	f2fs_down_read(&F2FS_I(dir)->i_sem);
> +
>  	if (f2fs_has_inline_dentry(dir)) {
>  		/*
> -		 * Should get i_xattr_sem to keep the lock order:
> -		 * i_xattr_sem -> inode_page lock used by f2fs_setxattr.
> +		 * Should get i_sem to keep the lock order:
> +		 * i_sem -> inode_page lock used by f2fs_setxattr.
>  		 */
> -		f2fs_down_read(&F2FS_I(dir)->i_xattr_sem);
>  		err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
> -		f2fs_up_read(&F2FS_I(dir)->i_xattr_sem);
>  	}
>  	if (err == -EAGAIN)
>  		err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
>  
> +	f2fs_up_read(&F2FS_I(dir)->i_sem);
> +
>  	f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
>  	return err;
>  }
> @@ -835,6 +843,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
>  	struct page *page;
>  	int err = 0;
>  
> +	f2fs_down_write(&F2FS_I(dir)->i_sem);
>  	f2fs_down_write(&F2FS_I(inode)->i_sem);
>  	page = f2fs_init_inode_metadata(inode, dir, NULL, NULL);
>  	if (IS_ERR(page)) {
> @@ -847,6 +856,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
>  	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>  fail:
>  	f2fs_up_write(&F2FS_I(inode)->i_sem);
> +	f2fs_up_write(&F2FS_I(dir)->i_sem);
>  	return err;
>  }
>  
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index c7cb2177b252..60ec032be48d 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -838,7 +838,6 @@ struct f2fs_inode_info {
>  
>  	/* avoid racing between foreground op and gc */
>  	struct f2fs_rwsem i_gc_rwsem[2];
> -	struct f2fs_rwsem i_xattr_sem; /* avoid racing between reading and changing EAs */
>  
>  	int i_extra_isize;		/* size of extra space located in i_addr */
>  	kprojid_t i_projid;		/* id for project quota */
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index ca31163da00a..c72fda24cffd 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1418,7 +1418,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
>  	INIT_LIST_HEAD(&fi->gdirty_list);
>  	init_f2fs_rwsem(&fi->i_gc_rwsem[READ]);
>  	init_f2fs_rwsem(&fi->i_gc_rwsem[WRITE]);
> -	init_f2fs_rwsem(&fi->i_xattr_sem);
>  
>  	/* Will be used by directory only */
>  	fi->i_dir_level = F2FS_SB(sb)->dir_level;
> @@ -3163,7 +3162,7 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
>  {
>  	return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
>  				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
> -				ctx, len, NULL);
> +				ctx, len, NULL, false);
>  }
>  
>  static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
> @@ -3183,7 +3182,7 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
>  
>  	return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
>  				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
> -				ctx, len, fs_data, XATTR_CREATE);
> +				ctx, len, fs_data, XATTR_CREATE, true);
>  }
>  
>  static const union fscrypt_policy *f2fs_get_dummy_policy(struct super_block *sb)
> diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
> index 4fc95f353a7a..e181528b1f26 100644
> --- a/fs/f2fs/verity.c
> +++ b/fs/f2fs/verity.c
> @@ -181,7 +181,7 @@ static int f2fs_end_enable_verity(struct file *filp, const void *desc,
>  	/* Set the verity xattr. */
>  	err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
>  			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
> -			    NULL, XATTR_CREATE);
> +			    NULL, XATTR_CREATE, false);
>  	if (err)
>  		goto cleanup;
>  
> @@ -226,7 +226,8 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
>  
>  	/* Get the descriptor location */
>  	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY,
> -			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL);
> +			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
> +			    NULL, false);
>  	if (res < 0 && res != -ERANGE)
>  		return res;
>  	if (res != sizeof(dloc) || dloc.version != cpu_to_le32(F2FS_VERIFY_VER)) {
> diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
> index 476b186b90a6..a6a611f8a771 100644
> --- a/fs/f2fs/xattr.c
> +++ b/fs/f2fs/xattr.c
> @@ -61,7 +61,7 @@ static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
>  		return -EINVAL;
>  	}
>  	return f2fs_getxattr(inode, handler->flags, name,
> -			     buffer, size, NULL);
> +			     buffer, size, NULL, false);
>  }
>  
>  static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
> @@ -84,7 +84,7 @@ static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
>  		return -EINVAL;
>  	}
>  	return f2fs_setxattr(inode, handler->flags, name,
> -					value, size, NULL, flags);
> +					value, size, NULL, flags, false);
>  }
>  
>  static bool f2fs_xattr_user_list(struct dentry *dentry)
> @@ -136,15 +136,16 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
>  
>  #ifdef CONFIG_F2FS_FS_SECURITY
>  static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
> -		void *page)
> +								void *fs_data)
>  {
> +	struct f2fs_xattr_arg *xarg = (struct f2fs_xattr_arg *)fs_data;
>  	const struct xattr *xattr;
>  	int err = 0;
>  
>  	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
>  		err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
>  				xattr->name, xattr->value,
> -				xattr->value_len, (struct page *)page, 0);
> +				xattr->value_len, xarg->page, 0, xarg->locked);
>  		if (err < 0)
>  			break;
>  	}
> @@ -152,10 +153,11 @@ static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
>  }
>  
>  int f2fs_init_security(struct inode *inode, struct inode *dir,
> -				const struct qstr *qstr, struct page *ipage)
> +				const struct qstr *qstr,
> +				struct f2fs_xattr_arg *xarg)
>  {
>  	return security_inode_init_security(inode, dir, qstr,
> -				&f2fs_initxattrs, ipage);
> +				&f2fs_initxattrs, xarg);
>  }
>  #endif
>  
> @@ -512,7 +514,8 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
>  }
>  
>  int f2fs_getxattr(struct inode *inode, int index, const char *name,
> -		void *buffer, size_t buffer_size, struct page *ipage)
> +				void *buffer, size_t buffer_size,
> +				struct page *ipage, bool locked)
>  {
>  	struct f2fs_xattr_entry *entry = NULL;
>  	int error;
> @@ -528,12 +531,12 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
>  	if (len > F2FS_NAME_LEN)
>  		return -ERANGE;
>  
> -	if (!ipage)
> -		f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
> +	if (!locked)
> +		f2fs_down_read(&F2FS_I(inode)->i_sem);
>  	error = lookup_all_xattrs(inode, ipage, index, len, name,
>  				&entry, &base_addr, &base_size, &is_inline);
> -	if (!ipage)
> -		f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
> +	if (!locked)
> +		f2fs_up_read(&F2FS_I(inode)->i_sem);
>  	if (error)
>  		return error;
>  
> @@ -567,9 +570,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
>  	int error;
>  	size_t rest = buffer_size;
>  
> -	f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
> +	f2fs_down_read(&F2FS_I(inode)->i_sem);
>  	error = read_all_xattrs(inode, NULL, &base_addr);
> -	f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
> +	f2fs_up_read(&F2FS_I(inode)->i_sem);
>  	if (error)
>  		return error;
>  
> @@ -775,7 +778,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
>  
>  int f2fs_setxattr(struct inode *inode, int index, const char *name,
>  				const void *value, size_t size,
> -				struct page *ipage, int flags)
> +				struct page *ipage, int flags, bool locked)
>  {
>  	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>  	int err;
> @@ -796,9 +799,11 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
>  	f2fs_balance_fs(sbi, true);
>  
>  	f2fs_lock_op(sbi);
> -	f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
> +	if (!locked)
> +		f2fs_down_write(&F2FS_I(inode)->i_sem);
>  	err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
> -	f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
> +	if (!locked)
> +		f2fs_up_write(&F2FS_I(inode)->i_sem);
>  	f2fs_unlock_op(sbi);
>  
>  	f2fs_update_time(sbi, REQ_TIME);
> diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
> index b1811c392e6f..170e4a49af31 100644
> --- a/fs/f2fs/xattr.h
> +++ b/fs/f2fs/xattr.h
> @@ -52,6 +52,11 @@ struct f2fs_xattr_entry {
>  	char    e_name[];      /* attribute name */
>  };
>  
> +struct f2fs_xattr_arg {
> +	struct page *page;	/* inode page */
> +	bool locked;		/* indicate i_sem is locked */
> +};
> +
>  #define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
>  #define XATTR_ENTRY(ptr)	((struct f2fs_xattr_entry *)(ptr))
>  #define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
> @@ -128,9 +133,9 @@ extern const struct xattr_handler f2fs_xattr_security_handler;
>  extern const struct xattr_handler *f2fs_xattr_handlers[];
>  
>  extern int f2fs_setxattr(struct inode *, int, const char *,
> -				const void *, size_t, struct page *, int);
> +				const void *, size_t, struct page *, int, bool);
>  extern int f2fs_getxattr(struct inode *, int, const char *, void *,
> -						size_t, struct page *);
> +					size_t, struct page *, bool locked);
>  extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
>  extern int f2fs_init_xattr_caches(struct f2fs_sb_info *);
>  extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
> @@ -140,13 +145,13 @@ extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
>  #define f2fs_listxattr		NULL
>  static inline int f2fs_setxattr(struct inode *inode, int index,
>  		const char *name, const void *value, size_t size,
> -		struct page *page, int flags)
> +		struct page *page, int flags, bool locked)
>  {
>  	return -EOPNOTSUPP;
>  }
>  static inline int f2fs_getxattr(struct inode *inode, int index,
>  			const char *name, void *buffer,
> -			size_t buffer_size, struct page *dpage)
> +			size_t buffer_size, struct page *dpage, bool locked)
>  {
>  	return -EOPNOTSUPP;
>  }
> @@ -156,10 +161,12 @@ static inline void f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi) { }
>  
>  #ifdef CONFIG_F2FS_FS_SECURITY
>  extern int f2fs_init_security(struct inode *, struct inode *,
> -				const struct qstr *, struct page *);
> +					const struct qstr *,
> +					struct f2fs_xattr_arg *xarg);
>  #else
>  static inline int f2fs_init_security(struct inode *inode, struct inode *dir,
> -				const struct qstr *qstr, struct page *ipage)
> +					const struct qstr *qstr,
> +					struct f2fs_xattr_arg *xarg)
>  {
>  	return 0;
>  }
> -- 
> 2.40.1
Chao Yu July 11, 2023, 7:58 a.m. UTC | #2
On 2023/7/11 1:18, Jaegeuk Kim wrote:
> On 07/10, Chao Yu wrote:
>> syzbot reports deadlock bug as below:
>>
>> -> #1 (&fi->i_sem){+.+.}-{3:3}:
>>         down_write+0x3a/0x50 kernel/locking/rwsem.c:1573
>>         f2fs_down_write fs/f2fs/f2fs.h:2133 [inline]
>>         f2fs_add_inline_entry+0x3a8/0x760 fs/f2fs/inline.c:644
>>         f2fs_add_dentry+0xba/0x1e0 fs/f2fs/dir.c:784
>>         f2fs_do_add_link+0x21e/0x340 fs/f2fs/dir.c:827
>>         f2fs_add_link fs/f2fs/f2fs.h:3554 [inline]
>>         f2fs_create+0x32c/0x530 fs/f2fs/namei.c:377
>>         lookup_open fs/namei.c:3492 [inline]
>>         open_last_lookups fs/namei.c:3560 [inline]
>>         path_openat+0x13e7/0x3180 fs/namei.c:3790
>>         do_filp_open+0x234/0x490 fs/namei.c:3820
>>         do_sys_openat2+0x13e/0x1d0 fs/open.c:1407
>>         do_sys_open fs/open.c:1422 [inline]
>>         __do_sys_open fs/open.c:1430 [inline]
>>         __se_sys_open fs/open.c:1426 [inline]
>>         __x64_sys_open+0x225/0x270 fs/open.c:1426
>>         do_syscall_x64 arch/x86/entry/common.c:50 [inline]
>>         do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
>>         entry_SYSCALL_64_after_hwframe+0x63/0xcd
>>
>> -> #0 (&fi->i_xattr_sem){.+.+}-{3:3}:
>>         check_prev_add kernel/locking/lockdep.c:3142 [inline]
>>         check_prevs_add kernel/locking/lockdep.c:3261 [inline]
>>         validate_chain kernel/locking/lockdep.c:3876 [inline]
>>         __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144
>>         lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761
>>         down_read+0x47/0x2f0 kernel/locking/rwsem.c:1520
>>         f2fs_down_read fs/f2fs/f2fs.h:2108 [inline]
>>         f2fs_getxattr+0xb8/0x1460 fs/f2fs/xattr.c:532
>>         __f2fs_get_acl+0x52/0x8e0 fs/f2fs/acl.c:179
>>         f2fs_acl_create fs/f2fs/acl.c:377 [inline]
>>         f2fs_init_acl+0xd7/0x9a0 fs/f2fs/acl.c:420
>>         f2fs_init_inode_metadata+0x824/0x1190 fs/f2fs/dir.c:558
>>         f2fs_do_tmpfile+0x34/0x170 fs/f2fs/dir.c:839
>>         __f2fs_tmpfile+0x1f9/0x380 fs/f2fs/namei.c:884
>>         f2fs_ioc_start_atomic_write+0x4a3/0x9e0 fs/f2fs/file.c:2099
>>         __f2fs_ioctl+0x1b5c/0xb770
>>         vfs_ioctl fs/ioctl.c:51 [inline]
>>         __do_sys_ioctl fs/ioctl.c:870 [inline]
>>         __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856
>>         do_syscall_x64 arch/x86/entry/common.c:50 [inline]
>>         do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
>>         entry_SYSCALL_64_after_hwframe+0x63/0xcd
>>
>> The root cause is as below reversed lock order:
>> - f2fs_create
>>   - f2fs_add_dentry
>>    - f2fs_down_write(&F2FS_I(dir)->i_xattr_sem)
>> 						- f2fs_ioc_start_atomic_write
>> 						 - __f2fs_tmpfile
>> 						  - f2fs_do_tmpfile
>> 						   - f2fs_down_write(&F2FS_I(inode)->i_sem)
>> 						   - f2fs_init_inode_metadata
>> 						    - f2fs_init_acl
>> 						     - __f2fs_get_acl
>> 						      - f2fs_getxattr
>> 						       - f2fs_down_read(&F2FS_I(dir)->i_xattr_sem)
>>    - f2fs_add_inline_entry
>>     - f2fs_down_write(&F2FS_I(inode)->i_sem)
> 
> How is it possible to get two inode are identical?

Oh, so, it looks like a false positive?

BTW, except commit message, what do you think of the change itself?
We can using i_sem to instead i_xattr_sem, so that 1) it can shrink
inode's size, 2) it's more clear that use common i_sem lock to protect
inode's {,x}attr access.

Thanks,

> 
>>
>> We can break the dependency of deadlock by below change:
>> - use i_sem to keep order of {get,set}xattr instead of i_xattr_sem
>> - keep below lock order in inode operation to avoid deadlock:
>>   dir->i_sem -> inode->i_sem
>>   dir->i_sem -> dpage_lock
>>
>> Fixes: 5eda1ad1aaff ("f2fs: fix deadlock in i_xattr_sem and inode page lock")
>> Reported-by: syzbot+e5600587fa9cbf8e3826@syzkaller.appspotmail.com
>> Closes: https://lore.kernel.org/linux-f2fs-devel/00000000000096797d06001a359d@google.com
>> Signed-off-by: Chao Yu <chao@kernel.org>
>> ---
>>   fs/f2fs/acl.c    | 36 +++++++++++++++++++-----------------
>>   fs/f2fs/acl.h    |  4 ++--
>>   fs/f2fs/dir.c    | 22 ++++++++++++++++------
>>   fs/f2fs/f2fs.h   |  1 -
>>   fs/f2fs/super.c  |  5 ++---
>>   fs/f2fs/verity.c |  5 +++--
>>   fs/f2fs/xattr.c  | 37 +++++++++++++++++++++----------------
>>   fs/f2fs/xattr.h  | 19 +++++++++++++------
>>   8 files changed, 76 insertions(+), 53 deletions(-)
>>
>> diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
>> index ec2aeccb69a3..af58b38e953c 100644
>> --- a/fs/f2fs/acl.c
>> +++ b/fs/f2fs/acl.c
>> @@ -166,7 +166,7 @@ static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
>>   }
>>   
>>   static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
>> -						struct page *dpage)
>> +						struct page *dpage, bool locked)
>>   {
>>   	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
>>   	void *value = NULL;
>> @@ -176,13 +176,13 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
>>   	if (type == ACL_TYPE_ACCESS)
>>   		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
>>   
>> -	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
>> +	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage, locked);
>>   	if (retval > 0) {
>>   		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
>>   		if (!value)
>>   			return ERR_PTR(-ENOMEM);
>>   		retval = f2fs_getxattr(inode, name_index, "", value,
>> -							retval, dpage);
>> +							retval, dpage, locked);
>>   	}
>>   
>>   	if (retval > 0)
>> @@ -201,7 +201,7 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
>>   	if (rcu)
>>   		return ERR_PTR(-ECHILD);
>>   
>> -	return __f2fs_get_acl(inode, type, NULL);
>> +	return __f2fs_get_acl(inode, type, NULL, false);
>>   }
>>   
>>   static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
>> @@ -226,9 +226,9 @@ static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
>>   	return 0;
>>   }
>>   
>> -static int __f2fs_set_acl(struct mnt_idmap *idmap,
>> -			struct inode *inode, int type,
>> -			struct posix_acl *acl, struct page *ipage)
>> +static int __f2fs_set_acl(struct mnt_idmap *idmap, struct inode *inode,
>> +					int type, struct posix_acl *acl,
>> +					struct page *ipage, bool locked)
>>   {
>>   	int name_index;
>>   	void *value = NULL;
>> @@ -266,7 +266,8 @@ static int __f2fs_set_acl(struct mnt_idmap *idmap,
>>   		}
>>   	}
>>   
>> -	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
>> +	error = f2fs_setxattr(inode, name_index, "", value, size,
>> +						ipage, 0, locked);
>>   
>>   	kfree(value);
>>   	if (!error)
>> @@ -284,7 +285,7 @@ int f2fs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
>>   	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
>>   		return -EIO;
>>   
>> -	return __f2fs_set_acl(idmap, inode, type, acl, NULL);
>> +	return __f2fs_set_acl(idmap, inode, type, acl, NULL, false);
>>   }
>>   
>>   /*
>> @@ -362,7 +363,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
>>   
>>   static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>>   		struct posix_acl **default_acl, struct posix_acl **acl,
>> -		struct page *dpage)
>> +		struct page *dpage, bool locked)
>>   {
>>   	struct posix_acl *p;
>>   	struct posix_acl *clone;
>> @@ -374,7 +375,7 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>>   	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
>>   		return 0;
>>   
>> -	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
>> +	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage, locked);
>>   	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
>>   		*mode &= ~current_umask();
>>   		return 0;
>> @@ -412,28 +413,29 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
>>   }
>>   
>>   int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
>> -							struct page *dpage)
>> +						struct page *dpage, bool locked)
>>   {
>>   	struct posix_acl *default_acl = NULL, *acl = NULL;
>>   	int error;
>>   
>> -	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
>> +	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl,
>> +							dpage, locked);
>>   	if (error)
>>   		return error;
>>   
>>   	f2fs_mark_inode_dirty_sync(inode, true);
>>   
>>   	if (default_acl) {
>> -		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,
>> -				       ipage);
>> +		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT,
>> +					default_acl, ipage, locked);
>>   		posix_acl_release(default_acl);
>>   	} else {
>>   		inode->i_default_acl = NULL;
>>   	}
>>   	if (acl) {
>>   		if (!error)
>> -			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,
>> -					       ipage);
>> +			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS,
>> +							acl, ipage, locked);
>>   		posix_acl_release(acl);
>>   	} else {
>>   		inode->i_acl = NULL;
>> diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h
>> index 94ebfbfbdc6f..9c14b6f549c6 100644
>> --- a/fs/f2fs/acl.h
>> +++ b/fs/f2fs/acl.h
>> @@ -37,13 +37,13 @@ extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
>>   extern int f2fs_set_acl(struct mnt_idmap *, struct dentry *,
>>   			struct posix_acl *, int);
>>   extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
>> -							struct page *);
>> +							struct page *, bool);
>>   #else
>>   #define f2fs_get_acl	NULL
>>   #define f2fs_set_acl	NULL
>>   
>>   static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
>> -				struct page *ipage, struct page *dpage)
>> +			struct page *ipage, struct page *dpage, bool locked)
>>   {
>>   	return 0;
>>   }
>> diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
>> index d635c58cf5a3..4b5c62e18d67 100644
>> --- a/fs/f2fs/dir.c
>> +++ b/fs/f2fs/dir.c
>> @@ -540,6 +540,8 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
>>   	int err;
>>   
>>   	if (is_inode_flag_set(inode, FI_NEW_INODE)) {
>> +		struct f2fs_xattr_arg xarg;
>> +
>>   		page = f2fs_new_inode_page(inode);
>>   		if (IS_ERR(page))
>>   			return page;
>> @@ -555,12 +557,16 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
>>   			put_page(page);
>>   		}
>>   
>> -		err = f2fs_init_acl(inode, dir, page, dpage);
>> +		err = f2fs_init_acl(inode, dir, page, dpage, true);
>>   		if (err)
>>   			goto put_error;
>>   
>> +		xarg.page = page;
>> +		xarg.locked = true;
>> +
>>   		err = f2fs_init_security(inode, dir,
>> -					 fname ? fname->usr_fname : NULL, page);
>> +					 fname ? fname->usr_fname : NULL,
>> +					 &xarg);
>>   		if (err)
>>   			goto put_error;
>>   
>> @@ -775,18 +781,20 @@ int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
>>   {
>>   	int err = -EAGAIN;
>>   
>> +	f2fs_down_read(&F2FS_I(dir)->i_sem);
>> +
>>   	if (f2fs_has_inline_dentry(dir)) {
>>   		/*
>> -		 * Should get i_xattr_sem to keep the lock order:
>> -		 * i_xattr_sem -> inode_page lock used by f2fs_setxattr.
>> +		 * Should get i_sem to keep the lock order:
>> +		 * i_sem -> inode_page lock used by f2fs_setxattr.
>>   		 */
>> -		f2fs_down_read(&F2FS_I(dir)->i_xattr_sem);
>>   		err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
>> -		f2fs_up_read(&F2FS_I(dir)->i_xattr_sem);
>>   	}
>>   	if (err == -EAGAIN)
>>   		err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
>>   
>> +	f2fs_up_read(&F2FS_I(dir)->i_sem);
>> +
>>   	f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
>>   	return err;
>>   }
>> @@ -835,6 +843,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
>>   	struct page *page;
>>   	int err = 0;
>>   
>> +	f2fs_down_write(&F2FS_I(dir)->i_sem);
>>   	f2fs_down_write(&F2FS_I(inode)->i_sem);
>>   	page = f2fs_init_inode_metadata(inode, dir, NULL, NULL);
>>   	if (IS_ERR(page)) {
>> @@ -847,6 +856,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
>>   	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
>>   fail:
>>   	f2fs_up_write(&F2FS_I(inode)->i_sem);
>> +	f2fs_up_write(&F2FS_I(dir)->i_sem);
>>   	return err;
>>   }
>>   
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index c7cb2177b252..60ec032be48d 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -838,7 +838,6 @@ struct f2fs_inode_info {
>>   
>>   	/* avoid racing between foreground op and gc */
>>   	struct f2fs_rwsem i_gc_rwsem[2];
>> -	struct f2fs_rwsem i_xattr_sem; /* avoid racing between reading and changing EAs */
>>   
>>   	int i_extra_isize;		/* size of extra space located in i_addr */
>>   	kprojid_t i_projid;		/* id for project quota */
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index ca31163da00a..c72fda24cffd 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -1418,7 +1418,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
>>   	INIT_LIST_HEAD(&fi->gdirty_list);
>>   	init_f2fs_rwsem(&fi->i_gc_rwsem[READ]);
>>   	init_f2fs_rwsem(&fi->i_gc_rwsem[WRITE]);
>> -	init_f2fs_rwsem(&fi->i_xattr_sem);
>>   
>>   	/* Will be used by directory only */
>>   	fi->i_dir_level = F2FS_SB(sb)->dir_level;
>> @@ -3163,7 +3162,7 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
>>   {
>>   	return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
>>   				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
>> -				ctx, len, NULL);
>> +				ctx, len, NULL, false);
>>   }
>>   
>>   static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
>> @@ -3183,7 +3182,7 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
>>   
>>   	return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
>>   				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
>> -				ctx, len, fs_data, XATTR_CREATE);
>> +				ctx, len, fs_data, XATTR_CREATE, true);
>>   }
>>   
>>   static const union fscrypt_policy *f2fs_get_dummy_policy(struct super_block *sb)
>> diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
>> index 4fc95f353a7a..e181528b1f26 100644
>> --- a/fs/f2fs/verity.c
>> +++ b/fs/f2fs/verity.c
>> @@ -181,7 +181,7 @@ static int f2fs_end_enable_verity(struct file *filp, const void *desc,
>>   	/* Set the verity xattr. */
>>   	err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
>>   			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
>> -			    NULL, XATTR_CREATE);
>> +			    NULL, XATTR_CREATE, false);
>>   	if (err)
>>   		goto cleanup;
>>   
>> @@ -226,7 +226,8 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
>>   
>>   	/* Get the descriptor location */
>>   	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY,
>> -			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL);
>> +			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
>> +			    NULL, false);
>>   	if (res < 0 && res != -ERANGE)
>>   		return res;
>>   	if (res != sizeof(dloc) || dloc.version != cpu_to_le32(F2FS_VERIFY_VER)) {
>> diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
>> index 476b186b90a6..a6a611f8a771 100644
>> --- a/fs/f2fs/xattr.c
>> +++ b/fs/f2fs/xattr.c
>> @@ -61,7 +61,7 @@ static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
>>   		return -EINVAL;
>>   	}
>>   	return f2fs_getxattr(inode, handler->flags, name,
>> -			     buffer, size, NULL);
>> +			     buffer, size, NULL, false);
>>   }
>>   
>>   static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
>> @@ -84,7 +84,7 @@ static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
>>   		return -EINVAL;
>>   	}
>>   	return f2fs_setxattr(inode, handler->flags, name,
>> -					value, size, NULL, flags);
>> +					value, size, NULL, flags, false);
>>   }
>>   
>>   static bool f2fs_xattr_user_list(struct dentry *dentry)
>> @@ -136,15 +136,16 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
>>   
>>   #ifdef CONFIG_F2FS_FS_SECURITY
>>   static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
>> -		void *page)
>> +								void *fs_data)
>>   {
>> +	struct f2fs_xattr_arg *xarg = (struct f2fs_xattr_arg *)fs_data;
>>   	const struct xattr *xattr;
>>   	int err = 0;
>>   
>>   	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
>>   		err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
>>   				xattr->name, xattr->value,
>> -				xattr->value_len, (struct page *)page, 0);
>> +				xattr->value_len, xarg->page, 0, xarg->locked);
>>   		if (err < 0)
>>   			break;
>>   	}
>> @@ -152,10 +153,11 @@ static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
>>   }
>>   
>>   int f2fs_init_security(struct inode *inode, struct inode *dir,
>> -				const struct qstr *qstr, struct page *ipage)
>> +				const struct qstr *qstr,
>> +				struct f2fs_xattr_arg *xarg)
>>   {
>>   	return security_inode_init_security(inode, dir, qstr,
>> -				&f2fs_initxattrs, ipage);
>> +				&f2fs_initxattrs, xarg);
>>   }
>>   #endif
>>   
>> @@ -512,7 +514,8 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
>>   }
>>   
>>   int f2fs_getxattr(struct inode *inode, int index, const char *name,
>> -		void *buffer, size_t buffer_size, struct page *ipage)
>> +				void *buffer, size_t buffer_size,
>> +				struct page *ipage, bool locked)
>>   {
>>   	struct f2fs_xattr_entry *entry = NULL;
>>   	int error;
>> @@ -528,12 +531,12 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
>>   	if (len > F2FS_NAME_LEN)
>>   		return -ERANGE;
>>   
>> -	if (!ipage)
>> -		f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
>> +	if (!locked)
>> +		f2fs_down_read(&F2FS_I(inode)->i_sem);
>>   	error = lookup_all_xattrs(inode, ipage, index, len, name,
>>   				&entry, &base_addr, &base_size, &is_inline);
>> -	if (!ipage)
>> -		f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
>> +	if (!locked)
>> +		f2fs_up_read(&F2FS_I(inode)->i_sem);
>>   	if (error)
>>   		return error;
>>   
>> @@ -567,9 +570,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
>>   	int error;
>>   	size_t rest = buffer_size;
>>   
>> -	f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
>> +	f2fs_down_read(&F2FS_I(inode)->i_sem);
>>   	error = read_all_xattrs(inode, NULL, &base_addr);
>> -	f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
>> +	f2fs_up_read(&F2FS_I(inode)->i_sem);
>>   	if (error)
>>   		return error;
>>   
>> @@ -775,7 +778,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
>>   
>>   int f2fs_setxattr(struct inode *inode, int index, const char *name,
>>   				const void *value, size_t size,
>> -				struct page *ipage, int flags)
>> +				struct page *ipage, int flags, bool locked)
>>   {
>>   	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
>>   	int err;
>> @@ -796,9 +799,11 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
>>   	f2fs_balance_fs(sbi, true);
>>   
>>   	f2fs_lock_op(sbi);
>> -	f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
>> +	if (!locked)
>> +		f2fs_down_write(&F2FS_I(inode)->i_sem);
>>   	err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
>> -	f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
>> +	if (!locked)
>> +		f2fs_up_write(&F2FS_I(inode)->i_sem);
>>   	f2fs_unlock_op(sbi);
>>   
>>   	f2fs_update_time(sbi, REQ_TIME);
>> diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
>> index b1811c392e6f..170e4a49af31 100644
>> --- a/fs/f2fs/xattr.h
>> +++ b/fs/f2fs/xattr.h
>> @@ -52,6 +52,11 @@ struct f2fs_xattr_entry {
>>   	char    e_name[];      /* attribute name */
>>   };
>>   
>> +struct f2fs_xattr_arg {
>> +	struct page *page;	/* inode page */
>> +	bool locked;		/* indicate i_sem is locked */
>> +};
>> +
>>   #define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
>>   #define XATTR_ENTRY(ptr)	((struct f2fs_xattr_entry *)(ptr))
>>   #define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
>> @@ -128,9 +133,9 @@ extern const struct xattr_handler f2fs_xattr_security_handler;
>>   extern const struct xattr_handler *f2fs_xattr_handlers[];
>>   
>>   extern int f2fs_setxattr(struct inode *, int, const char *,
>> -				const void *, size_t, struct page *, int);
>> +				const void *, size_t, struct page *, int, bool);
>>   extern int f2fs_getxattr(struct inode *, int, const char *, void *,
>> -						size_t, struct page *);
>> +					size_t, struct page *, bool locked);
>>   extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
>>   extern int f2fs_init_xattr_caches(struct f2fs_sb_info *);
>>   extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
>> @@ -140,13 +145,13 @@ extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
>>   #define f2fs_listxattr		NULL
>>   static inline int f2fs_setxattr(struct inode *inode, int index,
>>   		const char *name, const void *value, size_t size,
>> -		struct page *page, int flags)
>> +		struct page *page, int flags, bool locked)
>>   {
>>   	return -EOPNOTSUPP;
>>   }
>>   static inline int f2fs_getxattr(struct inode *inode, int index,
>>   			const char *name, void *buffer,
>> -			size_t buffer_size, struct page *dpage)
>> +			size_t buffer_size, struct page *dpage, bool locked)
>>   {
>>   	return -EOPNOTSUPP;
>>   }
>> @@ -156,10 +161,12 @@ static inline void f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi) { }
>>   
>>   #ifdef CONFIG_F2FS_FS_SECURITY
>>   extern int f2fs_init_security(struct inode *, struct inode *,
>> -				const struct qstr *, struct page *);
>> +					const struct qstr *,
>> +					struct f2fs_xattr_arg *xarg);
>>   #else
>>   static inline int f2fs_init_security(struct inode *inode, struct inode *dir,
>> -				const struct qstr *qstr, struct page *ipage)
>> +					const struct qstr *qstr,
>> +					struct f2fs_xattr_arg *xarg)
>>   {
>>   	return 0;
>>   }
>> -- 
>> 2.40.1
Jaegeuk Kim July 11, 2023, 4:16 p.m. UTC | #3
On 07/11, Chao Yu wrote:
> On 2023/7/11 1:18, Jaegeuk Kim wrote:
> > On 07/10, Chao Yu wrote:
> > > syzbot reports deadlock bug as below:
> > > 
> > > -> #1 (&fi->i_sem){+.+.}-{3:3}:
> > >         down_write+0x3a/0x50 kernel/locking/rwsem.c:1573
> > >         f2fs_down_write fs/f2fs/f2fs.h:2133 [inline]
> > >         f2fs_add_inline_entry+0x3a8/0x760 fs/f2fs/inline.c:644
> > >         f2fs_add_dentry+0xba/0x1e0 fs/f2fs/dir.c:784
> > >         f2fs_do_add_link+0x21e/0x340 fs/f2fs/dir.c:827
> > >         f2fs_add_link fs/f2fs/f2fs.h:3554 [inline]
> > >         f2fs_create+0x32c/0x530 fs/f2fs/namei.c:377
> > >         lookup_open fs/namei.c:3492 [inline]
> > >         open_last_lookups fs/namei.c:3560 [inline]
> > >         path_openat+0x13e7/0x3180 fs/namei.c:3790
> > >         do_filp_open+0x234/0x490 fs/namei.c:3820
> > >         do_sys_openat2+0x13e/0x1d0 fs/open.c:1407
> > >         do_sys_open fs/open.c:1422 [inline]
> > >         __do_sys_open fs/open.c:1430 [inline]
> > >         __se_sys_open fs/open.c:1426 [inline]
> > >         __x64_sys_open+0x225/0x270 fs/open.c:1426
> > >         do_syscall_x64 arch/x86/entry/common.c:50 [inline]
> > >         do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
> > >         entry_SYSCALL_64_after_hwframe+0x63/0xcd
> > > 
> > > -> #0 (&fi->i_xattr_sem){.+.+}-{3:3}:
> > >         check_prev_add kernel/locking/lockdep.c:3142 [inline]
> > >         check_prevs_add kernel/locking/lockdep.c:3261 [inline]
> > >         validate_chain kernel/locking/lockdep.c:3876 [inline]
> > >         __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144
> > >         lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761
> > >         down_read+0x47/0x2f0 kernel/locking/rwsem.c:1520
> > >         f2fs_down_read fs/f2fs/f2fs.h:2108 [inline]
> > >         f2fs_getxattr+0xb8/0x1460 fs/f2fs/xattr.c:532
> > >         __f2fs_get_acl+0x52/0x8e0 fs/f2fs/acl.c:179
> > >         f2fs_acl_create fs/f2fs/acl.c:377 [inline]
> > >         f2fs_init_acl+0xd7/0x9a0 fs/f2fs/acl.c:420
> > >         f2fs_init_inode_metadata+0x824/0x1190 fs/f2fs/dir.c:558
> > >         f2fs_do_tmpfile+0x34/0x170 fs/f2fs/dir.c:839
> > >         __f2fs_tmpfile+0x1f9/0x380 fs/f2fs/namei.c:884
> > >         f2fs_ioc_start_atomic_write+0x4a3/0x9e0 fs/f2fs/file.c:2099
> > >         __f2fs_ioctl+0x1b5c/0xb770
> > >         vfs_ioctl fs/ioctl.c:51 [inline]
> > >         __do_sys_ioctl fs/ioctl.c:870 [inline]
> > >         __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856
> > >         do_syscall_x64 arch/x86/entry/common.c:50 [inline]
> > >         do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
> > >         entry_SYSCALL_64_after_hwframe+0x63/0xcd
> > > 
> > > The root cause is as below reversed lock order:
> > > - f2fs_create
> > >   - f2fs_add_dentry
> > >    - f2fs_down_write(&F2FS_I(dir)->i_xattr_sem)
> > > 						- f2fs_ioc_start_atomic_write
> > > 						 - __f2fs_tmpfile
> > > 						  - f2fs_do_tmpfile
> > > 						   - f2fs_down_write(&F2FS_I(inode)->i_sem)
> > > 						   - f2fs_init_inode_metadata
> > > 						    - f2fs_init_acl
> > > 						     - __f2fs_get_acl
> > > 						      - f2fs_getxattr
> > > 						       - f2fs_down_read(&F2FS_I(dir)->i_xattr_sem)
> > >    - f2fs_add_inline_entry
> > >     - f2fs_down_write(&F2FS_I(inode)->i_sem)
> > 
> > How is it possible to get two inode are identical?
> 
> Oh, so, it looks like a false positive?
> 
> BTW, except commit message, what do you think of the change itself?
> We can using i_sem to instead i_xattr_sem, so that 1) it can shrink
> inode's size, 2) it's more clear that use common i_sem lock to protect
> inode's {,x}attr access.

If then, xattr can be blocked by unnecessary i_sem lock?

> 
> Thanks,
> 
> > 
> > > 
> > > We can break the dependency of deadlock by below change:
> > > - use i_sem to keep order of {get,set}xattr instead of i_xattr_sem
> > > - keep below lock order in inode operation to avoid deadlock:
> > >   dir->i_sem -> inode->i_sem
> > >   dir->i_sem -> dpage_lock
> > > 
> > > Fixes: 5eda1ad1aaff ("f2fs: fix deadlock in i_xattr_sem and inode page lock")
> > > Reported-by: syzbot+e5600587fa9cbf8e3826@syzkaller.appspotmail.com
> > > Closes: https://lore.kernel.org/linux-f2fs-devel/00000000000096797d06001a359d@google.com
> > > Signed-off-by: Chao Yu <chao@kernel.org>
> > > ---
> > >   fs/f2fs/acl.c    | 36 +++++++++++++++++++-----------------
> > >   fs/f2fs/acl.h    |  4 ++--
> > >   fs/f2fs/dir.c    | 22 ++++++++++++++++------
> > >   fs/f2fs/f2fs.h   |  1 -
> > >   fs/f2fs/super.c  |  5 ++---
> > >   fs/f2fs/verity.c |  5 +++--
> > >   fs/f2fs/xattr.c  | 37 +++++++++++++++++++++----------------
> > >   fs/f2fs/xattr.h  | 19 +++++++++++++------
> > >   8 files changed, 76 insertions(+), 53 deletions(-)
> > > 
> > > diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
> > > index ec2aeccb69a3..af58b38e953c 100644
> > > --- a/fs/f2fs/acl.c
> > > +++ b/fs/f2fs/acl.c
> > > @@ -166,7 +166,7 @@ static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
> > >   }
> > >   static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
> > > -						struct page *dpage)
> > > +						struct page *dpage, bool locked)
> > >   {
> > >   	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
> > >   	void *value = NULL;
> > > @@ -176,13 +176,13 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
> > >   	if (type == ACL_TYPE_ACCESS)
> > >   		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
> > > -	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
> > > +	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage, locked);
> > >   	if (retval > 0) {
> > >   		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
> > >   		if (!value)
> > >   			return ERR_PTR(-ENOMEM);
> > >   		retval = f2fs_getxattr(inode, name_index, "", value,
> > > -							retval, dpage);
> > > +							retval, dpage, locked);
> > >   	}
> > >   	if (retval > 0)
> > > @@ -201,7 +201,7 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
> > >   	if (rcu)
> > >   		return ERR_PTR(-ECHILD);
> > > -	return __f2fs_get_acl(inode, type, NULL);
> > > +	return __f2fs_get_acl(inode, type, NULL, false);
> > >   }
> > >   static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
> > > @@ -226,9 +226,9 @@ static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
> > >   	return 0;
> > >   }
> > > -static int __f2fs_set_acl(struct mnt_idmap *idmap,
> > > -			struct inode *inode, int type,
> > > -			struct posix_acl *acl, struct page *ipage)
> > > +static int __f2fs_set_acl(struct mnt_idmap *idmap, struct inode *inode,
> > > +					int type, struct posix_acl *acl,
> > > +					struct page *ipage, bool locked)
> > >   {
> > >   	int name_index;
> > >   	void *value = NULL;
> > > @@ -266,7 +266,8 @@ static int __f2fs_set_acl(struct mnt_idmap *idmap,
> > >   		}
> > >   	}
> > > -	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
> > > +	error = f2fs_setxattr(inode, name_index, "", value, size,
> > > +						ipage, 0, locked);
> > >   	kfree(value);
> > >   	if (!error)
> > > @@ -284,7 +285,7 @@ int f2fs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
> > >   	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
> > >   		return -EIO;
> > > -	return __f2fs_set_acl(idmap, inode, type, acl, NULL);
> > > +	return __f2fs_set_acl(idmap, inode, type, acl, NULL, false);
> > >   }
> > >   /*
> > > @@ -362,7 +363,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
> > >   static int f2fs_acl_create(struct inode *dir, umode_t *mode,
> > >   		struct posix_acl **default_acl, struct posix_acl **acl,
> > > -		struct page *dpage)
> > > +		struct page *dpage, bool locked)
> > >   {
> > >   	struct posix_acl *p;
> > >   	struct posix_acl *clone;
> > > @@ -374,7 +375,7 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
> > >   	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
> > >   		return 0;
> > > -	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
> > > +	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage, locked);
> > >   	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
> > >   		*mode &= ~current_umask();
> > >   		return 0;
> > > @@ -412,28 +413,29 @@ static int f2fs_acl_create(struct inode *dir, umode_t *mode,
> > >   }
> > >   int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
> > > -							struct page *dpage)
> > > +						struct page *dpage, bool locked)
> > >   {
> > >   	struct posix_acl *default_acl = NULL, *acl = NULL;
> > >   	int error;
> > > -	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
> > > +	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl,
> > > +							dpage, locked);
> > >   	if (error)
> > >   		return error;
> > >   	f2fs_mark_inode_dirty_sync(inode, true);
> > >   	if (default_acl) {
> > > -		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,
> > > -				       ipage);
> > > +		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT,
> > > +					default_acl, ipage, locked);
> > >   		posix_acl_release(default_acl);
> > >   	} else {
> > >   		inode->i_default_acl = NULL;
> > >   	}
> > >   	if (acl) {
> > >   		if (!error)
> > > -			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,
> > > -					       ipage);
> > > +			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS,
> > > +							acl, ipage, locked);
> > >   		posix_acl_release(acl);
> > >   	} else {
> > >   		inode->i_acl = NULL;
> > > diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h
> > > index 94ebfbfbdc6f..9c14b6f549c6 100644
> > > --- a/fs/f2fs/acl.h
> > > +++ b/fs/f2fs/acl.h
> > > @@ -37,13 +37,13 @@ extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
> > >   extern int f2fs_set_acl(struct mnt_idmap *, struct dentry *,
> > >   			struct posix_acl *, int);
> > >   extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
> > > -							struct page *);
> > > +							struct page *, bool);
> > >   #else
> > >   #define f2fs_get_acl	NULL
> > >   #define f2fs_set_acl	NULL
> > >   static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
> > > -				struct page *ipage, struct page *dpage)
> > > +			struct page *ipage, struct page *dpage, bool locked)
> > >   {
> > >   	return 0;
> > >   }
> > > diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
> > > index d635c58cf5a3..4b5c62e18d67 100644
> > > --- a/fs/f2fs/dir.c
> > > +++ b/fs/f2fs/dir.c
> > > @@ -540,6 +540,8 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
> > >   	int err;
> > >   	if (is_inode_flag_set(inode, FI_NEW_INODE)) {
> > > +		struct f2fs_xattr_arg xarg;
> > > +
> > >   		page = f2fs_new_inode_page(inode);
> > >   		if (IS_ERR(page))
> > >   			return page;
> > > @@ -555,12 +557,16 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
> > >   			put_page(page);
> > >   		}
> > > -		err = f2fs_init_acl(inode, dir, page, dpage);
> > > +		err = f2fs_init_acl(inode, dir, page, dpage, true);
> > >   		if (err)
> > >   			goto put_error;
> > > +		xarg.page = page;
> > > +		xarg.locked = true;
> > > +
> > >   		err = f2fs_init_security(inode, dir,
> > > -					 fname ? fname->usr_fname : NULL, page);
> > > +					 fname ? fname->usr_fname : NULL,
> > > +					 &xarg);
> > >   		if (err)
> > >   			goto put_error;
> > > @@ -775,18 +781,20 @@ int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
> > >   {
> > >   	int err = -EAGAIN;
> > > +	f2fs_down_read(&F2FS_I(dir)->i_sem);
> > > +
> > >   	if (f2fs_has_inline_dentry(dir)) {
> > >   		/*
> > > -		 * Should get i_xattr_sem to keep the lock order:
> > > -		 * i_xattr_sem -> inode_page lock used by f2fs_setxattr.
> > > +		 * Should get i_sem to keep the lock order:
> > > +		 * i_sem -> inode_page lock used by f2fs_setxattr.
> > >   		 */
> > > -		f2fs_down_read(&F2FS_I(dir)->i_xattr_sem);
> > >   		err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
> > > -		f2fs_up_read(&F2FS_I(dir)->i_xattr_sem);
> > >   	}
> > >   	if (err == -EAGAIN)
> > >   		err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
> > > +	f2fs_up_read(&F2FS_I(dir)->i_sem);
> > > +
> > >   	f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
> > >   	return err;
> > >   }
> > > @@ -835,6 +843,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
> > >   	struct page *page;
> > >   	int err = 0;
> > > +	f2fs_down_write(&F2FS_I(dir)->i_sem);
> > >   	f2fs_down_write(&F2FS_I(inode)->i_sem);
> > >   	page = f2fs_init_inode_metadata(inode, dir, NULL, NULL);
> > >   	if (IS_ERR(page)) {
> > > @@ -847,6 +856,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
> > >   	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
> > >   fail:
> > >   	f2fs_up_write(&F2FS_I(inode)->i_sem);
> > > +	f2fs_up_write(&F2FS_I(dir)->i_sem);
> > >   	return err;
> > >   }
> > > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > > index c7cb2177b252..60ec032be48d 100644
> > > --- a/fs/f2fs/f2fs.h
> > > +++ b/fs/f2fs/f2fs.h
> > > @@ -838,7 +838,6 @@ struct f2fs_inode_info {
> > >   	/* avoid racing between foreground op and gc */
> > >   	struct f2fs_rwsem i_gc_rwsem[2];
> > > -	struct f2fs_rwsem i_xattr_sem; /* avoid racing between reading and changing EAs */
> > >   	int i_extra_isize;		/* size of extra space located in i_addr */
> > >   	kprojid_t i_projid;		/* id for project quota */
> > > diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> > > index ca31163da00a..c72fda24cffd 100644
> > > --- a/fs/f2fs/super.c
> > > +++ b/fs/f2fs/super.c
> > > @@ -1418,7 +1418,6 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
> > >   	INIT_LIST_HEAD(&fi->gdirty_list);
> > >   	init_f2fs_rwsem(&fi->i_gc_rwsem[READ]);
> > >   	init_f2fs_rwsem(&fi->i_gc_rwsem[WRITE]);
> > > -	init_f2fs_rwsem(&fi->i_xattr_sem);
> > >   	/* Will be used by directory only */
> > >   	fi->i_dir_level = F2FS_SB(sb)->dir_level;
> > > @@ -3163,7 +3162,7 @@ static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
> > >   {
> > >   	return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
> > >   				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
> > > -				ctx, len, NULL);
> > > +				ctx, len, NULL, false);
> > >   }
> > >   static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
> > > @@ -3183,7 +3182,7 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
> > >   	return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
> > >   				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
> > > -				ctx, len, fs_data, XATTR_CREATE);
> > > +				ctx, len, fs_data, XATTR_CREATE, true);
> > >   }
> > >   static const union fscrypt_policy *f2fs_get_dummy_policy(struct super_block *sb)
> > > diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
> > > index 4fc95f353a7a..e181528b1f26 100644
> > > --- a/fs/f2fs/verity.c
> > > +++ b/fs/f2fs/verity.c
> > > @@ -181,7 +181,7 @@ static int f2fs_end_enable_verity(struct file *filp, const void *desc,
> > >   	/* Set the verity xattr. */
> > >   	err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
> > >   			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
> > > -			    NULL, XATTR_CREATE);
> > > +			    NULL, XATTR_CREATE, false);
> > >   	if (err)
> > >   		goto cleanup;
> > > @@ -226,7 +226,8 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
> > >   	/* Get the descriptor location */
> > >   	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY,
> > > -			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL);
> > > +			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
> > > +			    NULL, false);
> > >   	if (res < 0 && res != -ERANGE)
> > >   		return res;
> > >   	if (res != sizeof(dloc) || dloc.version != cpu_to_le32(F2FS_VERIFY_VER)) {
> > > diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
> > > index 476b186b90a6..a6a611f8a771 100644
> > > --- a/fs/f2fs/xattr.c
> > > +++ b/fs/f2fs/xattr.c
> > > @@ -61,7 +61,7 @@ static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
> > >   		return -EINVAL;
> > >   	}
> > >   	return f2fs_getxattr(inode, handler->flags, name,
> > > -			     buffer, size, NULL);
> > > +			     buffer, size, NULL, false);
> > >   }
> > >   static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
> > > @@ -84,7 +84,7 @@ static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
> > >   		return -EINVAL;
> > >   	}
> > >   	return f2fs_setxattr(inode, handler->flags, name,
> > > -					value, size, NULL, flags);
> > > +					value, size, NULL, flags, false);
> > >   }
> > >   static bool f2fs_xattr_user_list(struct dentry *dentry)
> > > @@ -136,15 +136,16 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
> > >   #ifdef CONFIG_F2FS_FS_SECURITY
> > >   static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
> > > -		void *page)
> > > +								void *fs_data)
> > >   {
> > > +	struct f2fs_xattr_arg *xarg = (struct f2fs_xattr_arg *)fs_data;
> > >   	const struct xattr *xattr;
> > >   	int err = 0;
> > >   	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
> > >   		err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
> > >   				xattr->name, xattr->value,
> > > -				xattr->value_len, (struct page *)page, 0);
> > > +				xattr->value_len, xarg->page, 0, xarg->locked);
> > >   		if (err < 0)
> > >   			break;
> > >   	}
> > > @@ -152,10 +153,11 @@ static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
> > >   }
> > >   int f2fs_init_security(struct inode *inode, struct inode *dir,
> > > -				const struct qstr *qstr, struct page *ipage)
> > > +				const struct qstr *qstr,
> > > +				struct f2fs_xattr_arg *xarg)
> > >   {
> > >   	return security_inode_init_security(inode, dir, qstr,
> > > -				&f2fs_initxattrs, ipage);
> > > +				&f2fs_initxattrs, xarg);
> > >   }
> > >   #endif
> > > @@ -512,7 +514,8 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
> > >   }
> > >   int f2fs_getxattr(struct inode *inode, int index, const char *name,
> > > -		void *buffer, size_t buffer_size, struct page *ipage)
> > > +				void *buffer, size_t buffer_size,
> > > +				struct page *ipage, bool locked)
> > >   {
> > >   	struct f2fs_xattr_entry *entry = NULL;
> > >   	int error;
> > > @@ -528,12 +531,12 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
> > >   	if (len > F2FS_NAME_LEN)
> > >   		return -ERANGE;
> > > -	if (!ipage)
> > > -		f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
> > > +	if (!locked)
> > > +		f2fs_down_read(&F2FS_I(inode)->i_sem);
> > >   	error = lookup_all_xattrs(inode, ipage, index, len, name,
> > >   				&entry, &base_addr, &base_size, &is_inline);
> > > -	if (!ipage)
> > > -		f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
> > > +	if (!locked)
> > > +		f2fs_up_read(&F2FS_I(inode)->i_sem);
> > >   	if (error)
> > >   		return error;
> > > @@ -567,9 +570,9 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
> > >   	int error;
> > >   	size_t rest = buffer_size;
> > > -	f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
> > > +	f2fs_down_read(&F2FS_I(inode)->i_sem);
> > >   	error = read_all_xattrs(inode, NULL, &base_addr);
> > > -	f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
> > > +	f2fs_up_read(&F2FS_I(inode)->i_sem);
> > >   	if (error)
> > >   		return error;
> > > @@ -775,7 +778,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
> > >   int f2fs_setxattr(struct inode *inode, int index, const char *name,
> > >   				const void *value, size_t size,
> > > -				struct page *ipage, int flags)
> > > +				struct page *ipage, int flags, bool locked)
> > >   {
> > >   	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> > >   	int err;
> > > @@ -796,9 +799,11 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
> > >   	f2fs_balance_fs(sbi, true);
> > >   	f2fs_lock_op(sbi);
> > > -	f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
> > > +	if (!locked)
> > > +		f2fs_down_write(&F2FS_I(inode)->i_sem);
> > >   	err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
> > > -	f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
> > > +	if (!locked)
> > > +		f2fs_up_write(&F2FS_I(inode)->i_sem);
> > >   	f2fs_unlock_op(sbi);
> > >   	f2fs_update_time(sbi, REQ_TIME);
> > > diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
> > > index b1811c392e6f..170e4a49af31 100644
> > > --- a/fs/f2fs/xattr.h
> > > +++ b/fs/f2fs/xattr.h
> > > @@ -52,6 +52,11 @@ struct f2fs_xattr_entry {
> > >   	char    e_name[];      /* attribute name */
> > >   };
> > > +struct f2fs_xattr_arg {
> > > +	struct page *page;	/* inode page */
> > > +	bool locked;		/* indicate i_sem is locked */
> > > +};
> > > +
> > >   #define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
> > >   #define XATTR_ENTRY(ptr)	((struct f2fs_xattr_entry *)(ptr))
> > >   #define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
> > > @@ -128,9 +133,9 @@ extern const struct xattr_handler f2fs_xattr_security_handler;
> > >   extern const struct xattr_handler *f2fs_xattr_handlers[];
> > >   extern int f2fs_setxattr(struct inode *, int, const char *,
> > > -				const void *, size_t, struct page *, int);
> > > +				const void *, size_t, struct page *, int, bool);
> > >   extern int f2fs_getxattr(struct inode *, int, const char *, void *,
> > > -						size_t, struct page *);
> > > +					size_t, struct page *, bool locked);
> > >   extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
> > >   extern int f2fs_init_xattr_caches(struct f2fs_sb_info *);
> > >   extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
> > > @@ -140,13 +145,13 @@ extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
> > >   #define f2fs_listxattr		NULL
> > >   static inline int f2fs_setxattr(struct inode *inode, int index,
> > >   		const char *name, const void *value, size_t size,
> > > -		struct page *page, int flags)
> > > +		struct page *page, int flags, bool locked)
> > >   {
> > >   	return -EOPNOTSUPP;
> > >   }
> > >   static inline int f2fs_getxattr(struct inode *inode, int index,
> > >   			const char *name, void *buffer,
> > > -			size_t buffer_size, struct page *dpage)
> > > +			size_t buffer_size, struct page *dpage, bool locked)
> > >   {
> > >   	return -EOPNOTSUPP;
> > >   }
> > > @@ -156,10 +161,12 @@ static inline void f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi) { }
> > >   #ifdef CONFIG_F2FS_FS_SECURITY
> > >   extern int f2fs_init_security(struct inode *, struct inode *,
> > > -				const struct qstr *, struct page *);
> > > +					const struct qstr *,
> > > +					struct f2fs_xattr_arg *xarg);
> > >   #else
> > >   static inline int f2fs_init_security(struct inode *inode, struct inode *dir,
> > > -				const struct qstr *qstr, struct page *ipage)
> > > +					const struct qstr *qstr,
> > > +					struct f2fs_xattr_arg *xarg)
> > >   {
> > >   	return 0;
> > >   }
> > > -- 
> > > 2.40.1
diff mbox series

Patch

diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index ec2aeccb69a3..af58b38e953c 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -166,7 +166,7 @@  static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
 }
 
 static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
-						struct page *dpage)
+						struct page *dpage, bool locked)
 {
 	int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 	void *value = NULL;
@@ -176,13 +176,13 @@  static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
 	if (type == ACL_TYPE_ACCESS)
 		name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 
-	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
+	retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage, locked);
 	if (retval > 0) {
 		value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
 		if (!value)
 			return ERR_PTR(-ENOMEM);
 		retval = f2fs_getxattr(inode, name_index, "", value,
-							retval, dpage);
+							retval, dpage, locked);
 	}
 
 	if (retval > 0)
@@ -201,7 +201,7 @@  struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
 	if (rcu)
 		return ERR_PTR(-ECHILD);
 
-	return __f2fs_get_acl(inode, type, NULL);
+	return __f2fs_get_acl(inode, type, NULL, false);
 }
 
 static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
@@ -226,9 +226,9 @@  static int f2fs_acl_update_mode(struct mnt_idmap *idmap,
 	return 0;
 }
 
-static int __f2fs_set_acl(struct mnt_idmap *idmap,
-			struct inode *inode, int type,
-			struct posix_acl *acl, struct page *ipage)
+static int __f2fs_set_acl(struct mnt_idmap *idmap, struct inode *inode,
+					int type, struct posix_acl *acl,
+					struct page *ipage, bool locked)
 {
 	int name_index;
 	void *value = NULL;
@@ -266,7 +266,8 @@  static int __f2fs_set_acl(struct mnt_idmap *idmap,
 		}
 	}
 
-	error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
+	error = f2fs_setxattr(inode, name_index, "", value, size,
+						ipage, 0, locked);
 
 	kfree(value);
 	if (!error)
@@ -284,7 +285,7 @@  int f2fs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
 	if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
 		return -EIO;
 
-	return __f2fs_set_acl(idmap, inode, type, acl, NULL);
+	return __f2fs_set_acl(idmap, inode, type, acl, NULL, false);
 }
 
 /*
@@ -362,7 +363,7 @@  static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 
 static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 		struct posix_acl **default_acl, struct posix_acl **acl,
-		struct page *dpage)
+		struct page *dpage, bool locked)
 {
 	struct posix_acl *p;
 	struct posix_acl *clone;
@@ -374,7 +375,7 @@  static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 	if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
 		return 0;
 
-	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
+	p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage, locked);
 	if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
 		*mode &= ~current_umask();
 		return 0;
@@ -412,28 +413,29 @@  static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 }
 
 int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
-							struct page *dpage)
+						struct page *dpage, bool locked)
 {
 	struct posix_acl *default_acl = NULL, *acl = NULL;
 	int error;
 
-	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
+	error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl,
+							dpage, locked);
 	if (error)
 		return error;
 
 	f2fs_mark_inode_dirty_sync(inode, true);
 
 	if (default_acl) {
-		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT, default_acl,
-				       ipage);
+		error = __f2fs_set_acl(NULL, inode, ACL_TYPE_DEFAULT,
+					default_acl, ipage, locked);
 		posix_acl_release(default_acl);
 	} else {
 		inode->i_default_acl = NULL;
 	}
 	if (acl) {
 		if (!error)
-			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS, acl,
-					       ipage);
+			error = __f2fs_set_acl(NULL, inode, ACL_TYPE_ACCESS,
+							acl, ipage, locked);
 		posix_acl_release(acl);
 	} else {
 		inode->i_acl = NULL;
diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h
index 94ebfbfbdc6f..9c14b6f549c6 100644
--- a/fs/f2fs/acl.h
+++ b/fs/f2fs/acl.h
@@ -37,13 +37,13 @@  extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
 extern int f2fs_set_acl(struct mnt_idmap *, struct dentry *,
 			struct posix_acl *, int);
 extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
-							struct page *);
+							struct page *, bool);
 #else
 #define f2fs_get_acl	NULL
 #define f2fs_set_acl	NULL
 
 static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
-				struct page *ipage, struct page *dpage)
+			struct page *ipage, struct page *dpage, bool locked)
 {
 	return 0;
 }
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index d635c58cf5a3..4b5c62e18d67 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -540,6 +540,8 @@  struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
 	int err;
 
 	if (is_inode_flag_set(inode, FI_NEW_INODE)) {
+		struct f2fs_xattr_arg xarg;
+
 		page = f2fs_new_inode_page(inode);
 		if (IS_ERR(page))
 			return page;
@@ -555,12 +557,16 @@  struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir,
 			put_page(page);
 		}
 
-		err = f2fs_init_acl(inode, dir, page, dpage);
+		err = f2fs_init_acl(inode, dir, page, dpage, true);
 		if (err)
 			goto put_error;
 
+		xarg.page = page;
+		xarg.locked = true;
+
 		err = f2fs_init_security(inode, dir,
-					 fname ? fname->usr_fname : NULL, page);
+					 fname ? fname->usr_fname : NULL,
+					 &xarg);
 		if (err)
 			goto put_error;
 
@@ -775,18 +781,20 @@  int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
 {
 	int err = -EAGAIN;
 
+	f2fs_down_read(&F2FS_I(dir)->i_sem);
+
 	if (f2fs_has_inline_dentry(dir)) {
 		/*
-		 * Should get i_xattr_sem to keep the lock order:
-		 * i_xattr_sem -> inode_page lock used by f2fs_setxattr.
+		 * Should get i_sem to keep the lock order:
+		 * i_sem -> inode_page lock used by f2fs_setxattr.
 		 */
-		f2fs_down_read(&F2FS_I(dir)->i_xattr_sem);
 		err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
-		f2fs_up_read(&F2FS_I(dir)->i_xattr_sem);
 	}
 	if (err == -EAGAIN)
 		err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
 
+	f2fs_up_read(&F2FS_I(dir)->i_sem);
+
 	f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
 	return err;
 }
@@ -835,6 +843,7 @@  int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
 	struct page *page;
 	int err = 0;
 
+	f2fs_down_write(&F2FS_I(dir)->i_sem);
 	f2fs_down_write(&F2FS_I(inode)->i_sem);
 	page = f2fs_init_inode_metadata(inode, dir, NULL, NULL);
 	if (IS_ERR(page)) {
@@ -847,6 +856,7 @@  int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
 	f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
 fail:
 	f2fs_up_write(&F2FS_I(inode)->i_sem);
+	f2fs_up_write(&F2FS_I(dir)->i_sem);
 	return err;
 }
 
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index c7cb2177b252..60ec032be48d 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -838,7 +838,6 @@  struct f2fs_inode_info {
 
 	/* avoid racing between foreground op and gc */
 	struct f2fs_rwsem i_gc_rwsem[2];
-	struct f2fs_rwsem i_xattr_sem; /* avoid racing between reading and changing EAs */
 
 	int i_extra_isize;		/* size of extra space located in i_addr */
 	kprojid_t i_projid;		/* id for project quota */
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index ca31163da00a..c72fda24cffd 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1418,7 +1418,6 @@  static struct inode *f2fs_alloc_inode(struct super_block *sb)
 	INIT_LIST_HEAD(&fi->gdirty_list);
 	init_f2fs_rwsem(&fi->i_gc_rwsem[READ]);
 	init_f2fs_rwsem(&fi->i_gc_rwsem[WRITE]);
-	init_f2fs_rwsem(&fi->i_xattr_sem);
 
 	/* Will be used by directory only */
 	fi->i_dir_level = F2FS_SB(sb)->dir_level;
@@ -3163,7 +3162,7 @@  static int f2fs_get_context(struct inode *inode, void *ctx, size_t len)
 {
 	return f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
 				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
-				ctx, len, NULL);
+				ctx, len, NULL, false);
 }
 
 static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
@@ -3183,7 +3182,7 @@  static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
 
 	return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
 				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
-				ctx, len, fs_data, XATTR_CREATE);
+				ctx, len, fs_data, XATTR_CREATE, true);
 }
 
 static const union fscrypt_policy *f2fs_get_dummy_policy(struct super_block *sb)
diff --git a/fs/f2fs/verity.c b/fs/f2fs/verity.c
index 4fc95f353a7a..e181528b1f26 100644
--- a/fs/f2fs/verity.c
+++ b/fs/f2fs/verity.c
@@ -181,7 +181,7 @@  static int f2fs_end_enable_verity(struct file *filp, const void *desc,
 	/* Set the verity xattr. */
 	err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
 			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
-			    NULL, XATTR_CREATE);
+			    NULL, XATTR_CREATE, false);
 	if (err)
 		goto cleanup;
 
@@ -226,7 +226,8 @@  static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
 
 	/* Get the descriptor location */
 	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY,
-			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL);
+			    F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
+			    NULL, false);
 	if (res < 0 && res != -ERANGE)
 		return res;
 	if (res != sizeof(dloc) || dloc.version != cpu_to_le32(F2FS_VERIFY_VER)) {
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 476b186b90a6..a6a611f8a771 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -61,7 +61,7 @@  static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
 		return -EINVAL;
 	}
 	return f2fs_getxattr(inode, handler->flags, name,
-			     buffer, size, NULL);
+			     buffer, size, NULL, false);
 }
 
 static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
@@ -84,7 +84,7 @@  static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
 		return -EINVAL;
 	}
 	return f2fs_setxattr(inode, handler->flags, name,
-					value, size, NULL, flags);
+					value, size, NULL, flags, false);
 }
 
 static bool f2fs_xattr_user_list(struct dentry *dentry)
@@ -136,15 +136,16 @@  static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
 
 #ifdef CONFIG_F2FS_FS_SECURITY
 static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
-		void *page)
+								void *fs_data)
 {
+	struct f2fs_xattr_arg *xarg = (struct f2fs_xattr_arg *)fs_data;
 	const struct xattr *xattr;
 	int err = 0;
 
 	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
 		err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
 				xattr->name, xattr->value,
-				xattr->value_len, (struct page *)page, 0);
+				xattr->value_len, xarg->page, 0, xarg->locked);
 		if (err < 0)
 			break;
 	}
@@ -152,10 +153,11 @@  static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
 }
 
 int f2fs_init_security(struct inode *inode, struct inode *dir,
-				const struct qstr *qstr, struct page *ipage)
+				const struct qstr *qstr,
+				struct f2fs_xattr_arg *xarg)
 {
 	return security_inode_init_security(inode, dir, qstr,
-				&f2fs_initxattrs, ipage);
+				&f2fs_initxattrs, xarg);
 }
 #endif
 
@@ -512,7 +514,8 @@  static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
 }
 
 int f2fs_getxattr(struct inode *inode, int index, const char *name,
-		void *buffer, size_t buffer_size, struct page *ipage)
+				void *buffer, size_t buffer_size,
+				struct page *ipage, bool locked)
 {
 	struct f2fs_xattr_entry *entry = NULL;
 	int error;
@@ -528,12 +531,12 @@  int f2fs_getxattr(struct inode *inode, int index, const char *name,
 	if (len > F2FS_NAME_LEN)
 		return -ERANGE;
 
-	if (!ipage)
-		f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
+	if (!locked)
+		f2fs_down_read(&F2FS_I(inode)->i_sem);
 	error = lookup_all_xattrs(inode, ipage, index, len, name,
 				&entry, &base_addr, &base_size, &is_inline);
-	if (!ipage)
-		f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
+	if (!locked)
+		f2fs_up_read(&F2FS_I(inode)->i_sem);
 	if (error)
 		return error;
 
@@ -567,9 +570,9 @@  ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
 	int error;
 	size_t rest = buffer_size;
 
-	f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
+	f2fs_down_read(&F2FS_I(inode)->i_sem);
 	error = read_all_xattrs(inode, NULL, &base_addr);
-	f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
+	f2fs_up_read(&F2FS_I(inode)->i_sem);
 	if (error)
 		return error;
 
@@ -775,7 +778,7 @@  static int __f2fs_setxattr(struct inode *inode, int index,
 
 int f2fs_setxattr(struct inode *inode, int index, const char *name,
 				const void *value, size_t size,
-				struct page *ipage, int flags)
+				struct page *ipage, int flags, bool locked)
 {
 	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
 	int err;
@@ -796,9 +799,11 @@  int f2fs_setxattr(struct inode *inode, int index, const char *name,
 	f2fs_balance_fs(sbi, true);
 
 	f2fs_lock_op(sbi);
-	f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
+	if (!locked)
+		f2fs_down_write(&F2FS_I(inode)->i_sem);
 	err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
-	f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
+	if (!locked)
+		f2fs_up_write(&F2FS_I(inode)->i_sem);
 	f2fs_unlock_op(sbi);
 
 	f2fs_update_time(sbi, REQ_TIME);
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index b1811c392e6f..170e4a49af31 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -52,6 +52,11 @@  struct f2fs_xattr_entry {
 	char    e_name[];      /* attribute name */
 };
 
+struct f2fs_xattr_arg {
+	struct page *page;	/* inode page */
+	bool locked;		/* indicate i_sem is locked */
+};
+
 #define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
 #define XATTR_ENTRY(ptr)	((struct f2fs_xattr_entry *)(ptr))
 #define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
@@ -128,9 +133,9 @@  extern const struct xattr_handler f2fs_xattr_security_handler;
 extern const struct xattr_handler *f2fs_xattr_handlers[];
 
 extern int f2fs_setxattr(struct inode *, int, const char *,
-				const void *, size_t, struct page *, int);
+				const void *, size_t, struct page *, int, bool);
 extern int f2fs_getxattr(struct inode *, int, const char *, void *,
-						size_t, struct page *);
+					size_t, struct page *, bool locked);
 extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
 extern int f2fs_init_xattr_caches(struct f2fs_sb_info *);
 extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
@@ -140,13 +145,13 @@  extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
 #define f2fs_listxattr		NULL
 static inline int f2fs_setxattr(struct inode *inode, int index,
 		const char *name, const void *value, size_t size,
-		struct page *page, int flags)
+		struct page *page, int flags, bool locked)
 {
 	return -EOPNOTSUPP;
 }
 static inline int f2fs_getxattr(struct inode *inode, int index,
 			const char *name, void *buffer,
-			size_t buffer_size, struct page *dpage)
+			size_t buffer_size, struct page *dpage, bool locked)
 {
 	return -EOPNOTSUPP;
 }
@@ -156,10 +161,12 @@  static inline void f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi) { }
 
 #ifdef CONFIG_F2FS_FS_SECURITY
 extern int f2fs_init_security(struct inode *, struct inode *,
-				const struct qstr *, struct page *);
+					const struct qstr *,
+					struct f2fs_xattr_arg *xarg);
 #else
 static inline int f2fs_init_security(struct inode *inode, struct inode *dir,
-				const struct qstr *qstr, struct page *ipage)
+					const struct qstr *qstr,
+					struct f2fs_xattr_arg *xarg)
 {
 	return 0;
 }