add proper ACL support to btrfs. Now with acl caching!
PATCH 1/1 add proper ACL support to btrfs. Now with acl caching!
Hello,
This is an updated version of the patch I sent earlier today. This adds acl cacheing to make things a little bit faster. I did all my acl tests with this and dbench -x 10 and here are my numbers
with acl
Throughput 69.8675 MB/sec 10 procs
with noacl
Throughput 70.172 MB/sec 10 procs
without patch
Throughput 69.347 MB/sec 10 procs
Thanks,
acl.c +280 -35
@@ -20,76 +20,321 @@#include <linux/string.h> #include <linux/xattr.h> #include <linux/posix_acl_xattr.h> +#include <linux/posix_acl.h> #include <linux/sched.h> +#include "ctree.h" +#include "btrfs_inode.h" #include "xattr.h" -#ifndef is_owner_or_cap -#define is_owner_or_cap (inode) \ - ((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER)) -#endif + +static void btrfs_update_cached_acl (struct inode *inode, +struct posix_acl **p_acl, +struct posix_acl *acl) +{ +spin_lock (&inode->i_lock); +if (*p_acl && *p_acl !=BTRFS_ACL_NOT_CACHED ) +posix_acl_release (*p_acl); + *p_acl =posix_acl_dup (acl); +spin_unlock (&inode->i_lock); +} + +static struct posix_acl *btrfs_get_acl (struct inode *inode,int type) +{ +int size, name_index; +char *value =NULL ; +struct posix_acl *acl =NULL , **p_acl; + +switch (type) { +case ACL_TYPE_ACCESS : + name_index =BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS ; + p_acl = &BTRFS_I (inode)->i_acl; +break ; +case ACL_TYPE_DEFAULT : + name_index =BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT ; + p_acl = &BTRFS_I (inode)->i_default_acl; +break ; +default : +return ERR_PTR (-EINVAL ); + } + +spin_lock (&inode->i_lock); +if (*p_acl !=BTRFS_ACL_NOT_CACHED ) + acl =posix_acl_dup (*p_acl); +spin_unlock (&inode->i_lock); + +if (acl) +return acl; + + + size =btrfs_xattr_get_locked (inode, name_index,"" ,NULL ,0 ); +if (size >0 ) { + value =kzalloc (size,GFP_NOFS ); +if (!value) +return ERR_PTR (-ENOMEM ); + size =btrfs_xattr_get_locked (inode, name_index,"" , value, + size); +if (size >0 ) { + acl =posix_acl_from_xattr (value, size); +btrfs_update_cached_acl (inode, p_acl, acl); + } +kfree (value); + }else if (size == -ENOENT ) { + acl =NULL ; +btrfs_update_cached_acl (inode, p_acl, acl); + } + +return acl; +} + +static int btrfs_xattr_get_acl (struct inode *inode,int type, +void *value,size_t size) +{ +struct posix_acl *acl; +struct btrfs_root *root =BTRFS_I (inode)->root; +int ret =0 ; + +mutex_lock (&root->fs_info->fs_mutex); + acl =btrfs_get_acl (inode, type); +mutex_unlock (&root->fs_info->fs_mutex); + +if (IS_ERR (acl)) +return PTR_ERR (acl); +if (acl ==NULL ) +return -ENODATA ; + ret =posix_acl_to_xattr (acl, value, size); +posix_acl_release (acl); + +return ret; +} + +/* +* Needs to be called with fs_mutex held +*/ +static int btrfs_set_acl (struct inode *inode,struct posix_acl *acl,int type) +{ +int ret, name_index =0 , size =0 ; +struct posix_acl **p_acl; +char *value =NULL ; +mode_t mode; + +if (acl) { + ret =posix_acl_valid (acl); +if (ret <0 ) +return ret; + ret =0 ; + } + +switch (type) { +case ACL_TYPE_ACCESS : + mode = inode->i_mode; + ret =posix_acl_equiv_mode (acl, &mode); +if (ret <0 ) +return ret; + ret =0 ; + inode->i_mode = mode; + name_index =BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS ; + p_acl = &BTRFS_I (inode)->i_acl; +break ; +case ACL_TYPE_DEFAULT : +if (!S_ISDIR (inode->i_mode)) +return acl ? -EINVAL :0 ; + name_index =BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT ; + p_acl = &BTRFS_I (inode)->i_default_acl; +break ; +default : +return -EINVAL ; + } + +if (acl) { + size =posix_acl_xattr_size (acl->a_count); + value =kmalloc (size,GFP_NOFS ); +if (!value) { + ret = -ENOMEM ; + goto out; + } + + ret =posix_acl_to_xattr (acl, value, size); +if (ret <0 ) + goto out; + } + + ret =btrfs_xattr_set_locked (inode, name_index,"" , value, size,0 ); + +out: +if (value) +kfree (value); + +if (!ret) +btrfs_update_cached_acl (inode, p_acl, acl); + +return ret; +}static int btrfs_xattr_set_acl (struct inode *inode,int type,const void *value,size_t size) {int ret =0 ; -struct posix_acl *acl; +struct posix_acl *acl =NULL ; +struct btrfs_root *root =BTRFS_I (inode)->root; -if (!is_owner_or_cap (inode)) -return -EPERM ;if (value) { acl =posix_acl_from_xattr (value, size);if (acl ==NULL ) { value =NULL ; size =0 ; }else if (IS_ERR (acl)) { - ret =PTR_ERR (acl); - }else { - ret =posix_acl_valid (acl); -posix_acl_release (acl); +return PTR_ERR (acl); } -if (ret) -return ret; } -return btrfs_xattr_set (inode, type,"" , value, size,0 ); + +mutex_lock (&root->fs_info->fs_mutex); + + ret =btrfs_set_acl (inode, acl, type); + +mutex_unlock (&root->fs_info->fs_mutex); + +posix_acl_release (acl); + +return ret; } -static int btrfs_xattr_get_acl (struct inode *inode,int type, -void *value,size_t size) -{ -return btrfs_xattr_get (inode, type,"" , value, size); -} +static int btrfs_xattr_acl_access_get (struct inode *inode,const char *name,void *value,size_t size) { -if (*name !='\0' ) -return -EINVAL ; -return btrfs_xattr_get_acl (inode,BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS , - value, size); +return btrfs_xattr_get_acl (inode,ACL_TYPE_ACCESS , value, size); } +static int btrfs_xattr_acl_access_set (struct inode *inode,const char *name,const void *value,size_t size,int flags) { -if (*name !='\0' ) -return -EINVAL ; -return btrfs_xattr_set_acl (inode,BTRFS_XATTR_INDEX_POSIX_ACL_ACCESS , - value, size); +return btrfs_xattr_set_acl (inode,ACL_TYPE_ACCESS , value, size); } +static int btrfs_xattr_acl_default_get (struct inode *inode,const char *name,void *value,size_t size) { -if (*name !='\0' ) -return -EINVAL ; -return btrfs_xattr_get_acl (inode,BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT , - value, size); +return btrfs_xattr_get_acl (inode,ACL_TYPE_DEFAULT , value, size); } +static int btrfs_xattr_acl_default_set (struct inode *inode,const char *name,const void *value,size_t size,int flags) { -if (*name !='\0' ) -return -EINVAL ; -return btrfs_xattr_set_acl (inode,BTRFS_XATTR_INDEX_POSIX_ACL_DEFAULT , - value, size); +return btrfs_xattr_set_acl (inode,ACL_TYPE_DEFAULT , value, size); } + +int btrfs_check_acl (struct inode *inode,int mask) +{ +struct posix_acl *acl; +struct btrfs_root *root =BTRFS_I (inode)->root; +int error = -EAGAIN ; + +mutex_lock (&root->fs_info->fs_mutex); + acl =btrfs_get_acl (inode,ACL_TYPE_ACCESS ); +mutex_unlock (&root->fs_info->fs_mutex); + +if (IS_ERR (acl)) +return PTR_ERR (acl); +if (acl) { + error =posix_acl_permission (inode, acl, mask); +posix_acl_release (acl); + } + +return error; +} + +/* +* btrfs_init_acl is already generally called under fs_mutex, so the locking +* stuff has been fixed to work with that. If the locking stuff changes, we +* need to re-evaluate the acl locking stuff. +*/ +int btrfs_init_acl (struct inode *inode,struct inode *dir) +{ +struct posix_acl *acl =NULL ; +int ret =0 ; + +/* this happens with subvols */ +if (!dir) +return 0 ; + +if (!S_ISLNK (inode->i_mode)) { +if (IS_POSIXACL (dir)) { + acl =btrfs_get_acl (dir,ACL_TYPE_DEFAULT ); +if (IS_ERR (acl)) +return PTR_ERR (acl); + } + +if (!acl) + inode->i_mode &= ~current->fs->umask; + } + +if (IS_POSIXACL (dir) && acl) { +struct posix_acl *clone; +mode_t mode; + +if (S_ISDIR (inode->i_mode)) { + ret =btrfs_set_acl (inode, acl,ACL_TYPE_DEFAULT ); +if (ret) + goto failed; + } + clone =posix_acl_clone (acl,GFP_NOFS ); + ret = -ENOMEM ; +if (!clone) + goto failed; + + mode = inode->i_mode; + ret =posix_acl_create_masq (clone, &mode); +if (ret >=0 ) { + inode->i_mode = mode; +if (ret >0 ) { +/* we need an acl */ + ret =btrfs_set_acl (inode, clone, +ACL_TYPE_ACCESS ); + } + } + } +failed: +posix_acl_release (acl); + +return ret; +} + +int btrfs_acl_chmod (struct inode *inode) +{ +struct posix_acl *acl, *clone; +struct btrfs_root *root =BTRFS_I (inode)->root; +int ret =0 ; + +if (S_ISLNK (inode->i_mode)) +return -EOPNOTSUPP ; + +if (!IS_POSIXACL (inode)) +return 0 ; + +mutex_lock (&root->fs_info->fs_mutex); + acl =btrfs_get_acl (inode,ACL_TYPE_ACCESS ); +if (IS_ERR (acl) || !acl) { +mutex_unlock (&root->fs_info->fs_mutex); +return PTR_ERR (acl); + } + + clone =posix_acl_clone (acl,GFP_KERNEL ); +posix_acl_release (acl); +if (!clone) { +mutex_unlock (&root->fs_info->fs_mutex); +return -ENOMEM ; + } + + ret =posix_acl_chmod_masq (clone, inode->i_mode); +if (!ret) + ret =btrfs_set_acl (inode, clone,ACL_TYPE_ACCESS ); + +posix_acl_release (clone); +mutex_unlock (&root->fs_info->fs_mutex); + +return ret; +} +struct xattr_handler btrfs_xattr_acl_default_handler = { .prefix =POSIX_ACL_XATTR_DEFAULT , .list = btrfs_xattr_generic_list,
btrfs_inode.h +3 -0
@@ -33,6 +33,9 @@ struct btrfs_inode {struct inode vfs_inode;atomic_t ordered_writeback; +struct posix_acl *i_acl; +struct posix_acl *i_default_acl; +u64 ordered_trans;/* * transid of the trans_handle that last modified this inode
ctree.h +7 -0
@@ -40,6 +40,8 @@ extern struct kmem_cache *btrfs_path_cacextern struct kmem_cache *btrfs_path_cachep;#define BTRFS_MAGIC "_B5RfS_M" + +#define BTRFS_ACL_NOT_CACHED ((void *)-1)#define BTRFS_MAX_LEVEL 8
@@ -1638,4 +1640,9 @@ u64 btrfs_parse_size(char *str);u64 btrfs_parse_size (char *str);int btrfs_parse_options (struct btrfs_root *root,char *options);int btrfs_sync_fs (struct super_block *sb,int wait); + +/* acl.c */ +int btrfs_check_acl (struct inode *inode,int mask); +int btrfs_init_acl (struct inode *inode,struct inode *dir); +int btrfs_acl_chmod (struct inode *inode);#endif
inode.c +42 -1
103 lines not on this page · raw message
super.c +7 -2
@@ -67,7 +67,7 @@ enum {enum { Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow, Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier, - Opt_ssd, Opt_thread_pool, Opt_err, + Opt_ssd, Opt_thread_pool, Opt_noacl, Opt_err, };static match_table_t tokens = {
@@ -82,7 +82,8 @@ static match_table_t tokens = { {Opt_alloc_start, "alloc_start=%s"}, {Opt_thread_pool, "thread_pool=%d"}, {Opt_ssd, "ssd"}, - {Opt_err, NULL} + {Opt_noacl, "noacl"}, + {Opt_err, NULL}, };u64 btrfs_parse_size (char *str)
@@ -213,6 +214,9 @@ int btrfs_parse_options(struct btrfs_roo"btrfs: allocations start at %llu\n" , info->alloc_start); } +break ; +case Opt_noacl: + root->fs_info->sb->s_flags &= ~MS_POSIXACL ;break ;default :break ;
@@ -300,6 +304,7 @@ static int btrfs_fill_super(struct super sb->s_op = &btrfs_super_ops; sb->s_xattr = btrfs_xattr_handlers; sb->s_time_gran =1 ; + sb->s_flags |=MS_POSIXACL ; tree_root =open_ctree (sb, fs_devices, (char *)data);
xattr.c +53 -18
136 lines not on this page · raw message
xattr.h +5 -1
@@ -42,11 +42,15 @@ extern struct xattr_handler btrfs_xattr_extern struct xattr_handler *btrfs_xattr_handlers[]; +ssize_t btrfs_xattr_get_locked (struct inode *inode,int name_index, +const char *name,void *buffer,size_t size);ssize_t btrfs_xattr_get (struct inode *inode,int name_index,const char *name,void *buffer,size_t size); +int btrfs_xattr_set_locked (struct inode *inode,int name_index, +const char *name,const void *value, +size_t size,int flags);int btrfs_xattr_set (struct inode *inode,int name_index,const char *name,const void *value,size_t size,int flags); -/* * the only reason this is public is for acl.c. There may be a point where * acl.c doesn't need it, and if thats the case we need to remove it and make
Quoted 6 lines · Josef Bacik
Hello,
This is an updated version of the patch I sent earlier today. This adds acl cacheing to make things a little bit faster. I did all my acl tests with this and dbench -x 10 and here are my numbers
Fantastic. I think you can update this to the locking code I just pushed out by just removing uses of fs_mutex. If you want to give that a shot, I'll try to get it integrated before running off on vacation.
-chris