Commit 488d4279 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Jialin Zhang
Browse files

ovl: enable RCU'd ->get_acl()

mainline inclusion
from mainline-v5.15-rc1
commit 332f606b
category: perf
bugzilla: https://gitee.com/openeuler/kernel/issues/I6ZCW0
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=332f606b32b6291a944c8cf23b91f53a6e676525



--------------------------------

Overlayfs does not cache ACL's (to avoid double caching).  Instead it just
calls the underlying filesystem's i_op->get_acl(), which will return the
cached value, if possible.

In rcu path walk, however, get_cached_acl_rcu() is employed to get the
value from the cache, which will fail on overlayfs resulting in dropping
out of rcu walk mode.  This can result in a big performance hit in certain
situations.

Fix by calling ->get_acl() with rcu=true in case of ACL_DONT_CACHE (which
indicates pass-through)

Reported-by: default avatargaryhuang <zjh.20052005@163.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Conflicts:
	fs/posix_acl.c
Signed-off-by: default avatarZhihao Cheng <chengzhihao1@huawei.com>
Reviewed-by: default avatarZhang Yi <yi.zhang@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parent 67cd4976
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/posix_acl.h>
#include <linux/ratelimit.h>
#include <linux/fiemap.h>
#include <linux/namei.h>
#include "overlayfs.h"


@@ -447,12 +448,12 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
	const struct cred *old_cred;
	struct posix_acl *acl;

	if (rcu)
		return ERR_PTR(-ECHILD);

	if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
		return NULL;

	if (rcu)
		return get_cached_acl_rcu(realinode, type);

	old_cred = ovl_override_creds(inode->i_sb);
	acl = get_acl(realinode, type);
	revert_creds(old_cred);
+12 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/xattr.h>
#include <linux/export.h>
#include <linux/user_namespace.h>
#include <linux/namei.h>

static struct posix_acl **acl_by_type(struct inode *inode, int type)
{
@@ -56,7 +57,17 @@ EXPORT_SYMBOL(get_cached_acl);

struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
{
	return rcu_dereference(*acl_by_type(inode, type));
	struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type));

	if (acl == ACL_DONT_CACHE && inode->i_op->get_acl2) {
		struct posix_acl *ret;

		ret = inode->i_op->get_acl2(inode, type, LOOKUP_RCU);
		if (!IS_ERR(ret))
			acl = ret;
	}

	return acl;
}
EXPORT_SYMBOL(get_cached_acl_rcu);

+5 −0
Original line number Diff line number Diff line
@@ -600,6 +600,11 @@ static inline void mapping_allow_writable(struct address_space *mapping)

struct posix_acl;
#define ACL_NOT_CACHED ((void *)(-1))
/*
 * ACL_DONT_CACHE is for stacked filesystems, that rely on underlying fs to
 * cache the ACL.  This also means that ->get_acl2() can be called in RCU mode
 * with the LOOKUP_RCU flag.
 */
#define ACL_DONT_CACHE ((void *)(-3))

static inline struct posix_acl *
+2 −1
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@ extern int __posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
extern struct posix_acl *get_posix_acl(struct inode *, int);
extern int set_posix_acl(struct inode *, int, struct posix_acl *);

struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);

#ifdef CONFIG_FS_POSIX_ACL
extern int posix_acl_chmod(struct inode *, umode_t);
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
@@ -81,7 +83,6 @@ extern int simple_set_acl(struct inode *, struct posix_acl *, int);
extern int simple_acl_create(struct inode *, struct inode *);

struct posix_acl *get_cached_acl(struct inode *inode, int type);
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
void forget_cached_acl(struct inode *inode, int type);
void forget_all_cached_acls(struct inode *inode);