Skip to content

Commit

Permalink
ovl: enable RCU'd ->get_acl()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Miklos Szeredi committed Aug 18, 2021
1 parent 0cad624 commit 332f606
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 5 deletions.
7 changes: 4 additions & 3 deletions fs/overlayfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/fiemap.h>
#include <linux/fileattr.h>
#include <linux/security.h>
#include <linux/namei.h>
#include "overlayfs.h"


Expand Down Expand Up @@ -452,12 +453,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);
Expand Down
13 changes: 12 additions & 1 deletion fs/posix_acl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,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 *
Expand Down
3 changes: 2 additions & 1 deletion include/linux/posix_acl.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ extern struct posix_acl *get_posix_acl(struct inode *, int);
extern int set_posix_acl(struct user_namespace *, struct inode *, int,
struct posix_acl *);

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

#ifdef CONFIG_FS_POSIX_ACL
int posix_acl_chmod(struct user_namespace *, struct inode *, umode_t);
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
Expand All @@ -84,7 +86,6 @@ extern int simple_set_acl(struct user_namespace *, struct inode *,
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);
Expand Down

0 comments on commit 332f606

Please sign in to comment.