Cache richacls in struct inode so that this doesn't have to be done individually in each filesystem. This is similar to POSIX ACLs. Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx> --- fs/inode.c | 11 ++++++-- fs/posix_acl.c | 2 +- fs/richacl_base.c | 4 +-- fs/richacl_inode.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 5 +++- include/linux/richacl.h | 15 ++++++---- 6 files changed, 100 insertions(+), 12 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 2a387f4..8462ddb 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -174,8 +174,11 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_private = NULL; inode->i_mapping = mapping; INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */ -#ifdef CONFIG_FS_POSIX_ACL - inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED; +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) + inode->i_acl = ACL_NOT_CACHED; +# if defined(CONFIG_FS_POSIX_ACL) + inode->i_default_acl = ACL_NOT_CACHED; +# endif #endif #ifdef CONFIG_FSNOTIFY @@ -231,11 +234,13 @@ void __destroy_inode(struct inode *inode) atomic_long_dec(&inode->i_sb->s_remove_count); } -#ifdef CONFIG_FS_POSIX_ACL +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED) put_base_acl(inode->i_acl); +# if defined(CONFIG_FS_POSIX_ACL) if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED) put_base_acl(inode->i_default_acl); +# endif #endif this_cpu_dec(nr_inodes); } diff --git a/fs/posix_acl.c b/fs/posix_acl.c index b3b2265..1d766a5 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -38,7 +38,7 @@ struct posix_acl *get_cached_acl(struct inode *inode, int type) { struct posix_acl **p = acl_by_type(inode, type); struct posix_acl *acl = ACCESS_ONCE(*p); - if (acl) { + if (acl && IS_POSIXACL(inode)) { spin_lock(&inode->i_lock); acl = *p; if (acl != ACL_NOT_CACHED) diff --git a/fs/richacl_base.c b/fs/richacl_base.c index 4a10174..78e81df 100644 --- a/fs/richacl_base.c +++ b/fs/richacl_base.c @@ -33,7 +33,7 @@ richacl_alloc(int count, gfp_t gfp) struct richacl *acl = kzalloc(size, gfp); if (acl) { - atomic_set(&acl->a_refcount, 1); + atomic_set(&acl->a_base.ba_refcount, 1); acl->a_count = count; } return acl; @@ -52,7 +52,7 @@ richacl_clone(const struct richacl *acl, gfp_t gfp) if (dup) { memcpy(dup, acl, size); - atomic_set(&dup->a_refcount, 1); + atomic_set(&dup->a_base.ba_refcount, 1); } return dup; } diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c index 5098717..c0458db 100644 --- a/fs/richacl_inode.c +++ b/fs/richacl_inode.c @@ -20,6 +20,81 @@ #include <linux/slab.h> #include <linux/richacl.h> +struct richacl *get_cached_richacl(struct inode *inode) +{ + struct richacl *acl; + + acl = (struct richacl *)ACCESS_ONCE(inode->i_acl); + if (acl && IS_RICHACL(inode)) { + spin_lock(&inode->i_lock); + acl = (struct richacl *)inode->i_acl; + if (acl != ACL_NOT_CACHED) + acl = richacl_get(acl); + spin_unlock(&inode->i_lock); + } + return acl; +} +EXPORT_SYMBOL_GPL(get_cached_richacl); + +struct richacl *get_cached_richacl_rcu(struct inode *inode) +{ + return (struct richacl *)rcu_dereference(inode->i_acl); +} +EXPORT_SYMBOL_GPL(get_cached_richacl_rcu); + +void set_cached_richacl(struct inode *inode, struct richacl *acl) +{ + struct base_acl *old = NULL; + + spin_lock(&inode->i_lock); + old = inode->i_acl; + rcu_assign_pointer(inode->i_acl, &richacl_get(acl)->a_base); + spin_unlock(&inode->i_lock); + if (old != ACL_NOT_CACHED) + put_base_acl(old); +} +EXPORT_SYMBOL_GPL(set_cached_richacl); + +void forget_cached_richacl(struct inode *inode) +{ + struct base_acl *old = NULL; + + spin_lock(&inode->i_lock); + old = inode->i_acl; + inode->i_acl = ACL_NOT_CACHED; + spin_unlock(&inode->i_lock); + if (old != ACL_NOT_CACHED) + put_base_acl(old); +} +EXPORT_SYMBOL_GPL(forget_cached_richacl); + +struct richacl *get_richacl(struct inode *inode) +{ + struct richacl *acl; + + acl = get_cached_richacl(inode); + if (acl != ACL_NOT_CACHED) + return acl; + + if (!IS_RICHACL(inode)) + return NULL; + + /* + * A filesystem can force a ACL callback by just never filling the + * ACL cache. But normally you'd fill the cache either at inode + * instantiation time, or on the first ->get_richacl call. + * + * If the filesystem doesn't have a get_richacl() function at all, + * we'll just create the negative cache entry. + */ + if (!inode->i_op->get_richacl) { + set_cached_richacl(inode, NULL); + return NULL; + } + return inode->i_op->get_richacl(inode); +} +EXPORT_SYMBOL_GPL(get_richacl); + /** * richacl_permission - richacl permission check algorithm * @inode: inode to check diff --git a/include/linux/fs.h b/include/linux/fs.h index 08fde42..d91deef 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -583,6 +583,7 @@ struct base_acl { }; }; struct posix_acl; +struct richacl; #define ACL_NOT_CACHED ((void *)(-1)) #define IOP_FASTPERM 0x0001 @@ -601,9 +602,11 @@ struct inode { kgid_t i_gid; unsigned int i_flags; -#if defined(CONFIG_FS_POSIX_ACL) +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) struct base_acl *i_acl; +# if defined(CONFIG_FS_POSIX_ACL) struct base_acl *i_default_acl; +# endif #endif const struct inode_operations *i_op; diff --git a/include/linux/richacl.h b/include/linux/richacl.h index 575ec32..7562ab4 100644 --- a/include/linux/richacl.h +++ b/include/linux/richacl.h @@ -31,7 +31,7 @@ struct richace { }; struct richacl { - atomic_t a_refcount; + struct base_acl a_base; unsigned int a_owner_mask; unsigned int a_group_mask; unsigned int a_other_mask; @@ -93,8 +93,7 @@ struct richacl { static inline struct richacl * richacl_get(struct richacl *acl) { - if (acl) - atomic_inc(&acl->a_base.ba_refcount); + get_base_acl(&acl->a_base); return acl; } @@ -104,10 +103,16 @@ richacl_get(struct richacl *acl) static inline void richacl_put(struct richacl *acl) { - if (acl && atomic_dec_and_test(&acl->a_refcount)) - kfree(acl); + BUILD_BUG_ON(offsetof(struct richacl, a_base) != 0); + put_base_acl(&acl->a_base); } +extern struct richacl *get_cached_richacl(struct inode *); +extern struct richacl *get_cached_richacl_rcu(struct inode *); +extern void set_cached_richacl(struct inode *, struct richacl *); +extern void forget_cached_richacl(struct inode *); +extern struct richacl *get_richacl(struct inode *); + /** * richace_is_owner - check if @ace is an OWNER@ entry */ -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html