Skip to content

Commit 332f606

Browse files
author
Miklos Szeredi
committed
ovl: enable RCU'd ->get_acl()
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 <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 0cad624 commit 332f606

File tree

4 files changed

+23
-5
lines changed

4 files changed

+23
-5
lines changed

fs/overlayfs/inode.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/fiemap.h>
1414
#include <linux/fileattr.h>
1515
#include <linux/security.h>
16+
#include <linux/namei.h>
1617
#include "overlayfs.h"
1718

1819

@@ -452,12 +453,12 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
452453
const struct cred *old_cred;
453454
struct posix_acl *acl;
454455

455-
if (rcu)
456-
return ERR_PTR(-ECHILD);
457-
458456
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
459457
return NULL;
460458

459+
if (rcu)
460+
return get_cached_acl_rcu(realinode, type);
461+
461462
old_cred = ovl_override_creds(inode->i_sb);
462463
acl = get_acl(realinode, type);
463464
revert_creds(old_cred);

fs/posix_acl.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <linux/xattr.h>
2323
#include <linux/export.h>
2424
#include <linux/user_namespace.h>
25+
#include <linux/namei.h>
2526

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

5758
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
5859
{
59-
return rcu_dereference(*acl_by_type(inode, type));
60+
struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type));
61+
62+
if (acl == ACL_DONT_CACHE) {
63+
struct posix_acl *ret;
64+
65+
ret = inode->i_op->get_acl(inode, type, LOOKUP_RCU);
66+
if (!IS_ERR(ret))
67+
acl = ret;
68+
}
69+
70+
return acl;
6071
}
6172
EXPORT_SYMBOL(get_cached_acl_rcu);
6273

include/linux/fs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,11 @@ static inline void mapping_allow_writable(struct address_space *mapping)
581581

582582
struct posix_acl;
583583
#define ACL_NOT_CACHED ((void *)(-1))
584+
/*
585+
* ACL_DONT_CACHE is for stacked filesystems, that rely on underlying fs to
586+
* cache the ACL. This also means that ->get_acl() can be called in RCU mode
587+
* with the LOOKUP_RCU flag.
588+
*/
584589
#define ACL_DONT_CACHE ((void *)(-3))
585590

586591
static inline struct posix_acl *

include/linux/posix_acl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ extern struct posix_acl *get_posix_acl(struct inode *, int);
7272
extern int set_posix_acl(struct user_namespace *, struct inode *, int,
7373
struct posix_acl *);
7474

75+
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
76+
7577
#ifdef CONFIG_FS_POSIX_ACL
7678
int posix_acl_chmod(struct user_namespace *, struct inode *, umode_t);
7779
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
@@ -84,7 +86,6 @@ extern int simple_set_acl(struct user_namespace *, struct inode *,
8486
extern int simple_acl_create(struct inode *, struct inode *);
8587

8688
struct posix_acl *get_cached_acl(struct inode *inode, int type);
87-
struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
8889
void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
8990
void forget_cached_acl(struct inode *inode, int type);
9091
void forget_all_cached_acls(struct inode *inode);

0 commit comments

Comments
 (0)