ovl: enable RCU'd ->get_acl()
ANBZ: #5669
commit 332f606b32
upstream.
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: garyhuang <zjh.20052005@163.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Jingbo Xu <jefflexu@linux.alibaba.com>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Link: https://gitee.com/anolis/cloud-kernel/pulls/1801
This commit is contained in:
parent
4d36616698
commit
d5c57539a0
|
@ -11,6 +11,7 @@
|
|||
#include <linux/posix_acl.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/namei.h>
|
||||
#include "overlayfs.h"
|
||||
|
||||
|
||||
|
@ -453,12 +454,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);
|
||||
|
|
|
@ -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) {
|
||||
struct posix_acl *ret;
|
||||
|
||||
ret = inode->i_op->get_acl(inode, type, LOOKUP_RCU);
|
||||
if (!IS_ERR(ret))
|
||||
acl = ret;
|
||||
}
|
||||
|
||||
return acl;
|
||||
}
|
||||
EXPORT_SYMBOL(get_cached_acl_rcu);
|
||||
|
||||
|
|
|
@ -603,6 +603,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_acl() can be called in RCU mode
|
||||
* with the LOOKUP_RCU flag.
|
||||
*/
|
||||
#define ACL_DONT_CACHE ((void *)(-3))
|
||||
|
||||
static inline struct posix_acl *
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue