diff options
Diffstat (limited to 'fs/kernfs/inode.c')
| -rw-r--r-- | fs/kernfs/inode.c | 155 |
1 files changed, 54 insertions, 101 deletions
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index b83054da68b3..38b28aa7cd02 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -24,45 +24,44 @@ static const struct inode_operations kernfs_iops = { .listxattr = kernfs_iop_listxattr, }; -static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, int alloc) +static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, bool alloc) { - static DEFINE_MUTEX(iattr_mutex); - struct kernfs_iattrs *ret; + struct kernfs_iattrs *ret __free(kfree) = NULL; + struct kernfs_iattrs *attr; - mutex_lock(&iattr_mutex); + attr = READ_ONCE(kn->iattr); + if (attr || !alloc) + return attr; - if (kn->iattr || !alloc) - goto out_unlock; - - kn->iattr = kmem_cache_zalloc(kernfs_iattrs_cache, GFP_KERNEL); - if (!kn->iattr) - goto out_unlock; + ret = kmem_cache_zalloc(kernfs_iattrs_cache, GFP_KERNEL); + if (!ret) + return NULL; /* assign default attributes */ - kn->iattr->ia_uid = GLOBAL_ROOT_UID; - kn->iattr->ia_gid = GLOBAL_ROOT_GID; - - ktime_get_real_ts64(&kn->iattr->ia_atime); - kn->iattr->ia_mtime = kn->iattr->ia_atime; - kn->iattr->ia_ctime = kn->iattr->ia_atime; - - simple_xattrs_init(&kn->iattr->xattrs); - atomic_set(&kn->iattr->nr_user_xattrs, 0); - atomic_set(&kn->iattr->user_xattr_size, 0); -out_unlock: - ret = kn->iattr; - mutex_unlock(&iattr_mutex); - return ret; + ret->ia_uid = GLOBAL_ROOT_UID; + ret->ia_gid = GLOBAL_ROOT_GID; + + ktime_get_real_ts64(&ret->ia_atime); + ret->ia_mtime = ret->ia_atime; + ret->ia_ctime = ret->ia_atime; + + simple_xattr_limits_init(&ret->xattr_limits); + + /* If someone raced us, recognize it. */ + if (!try_cmpxchg(&kn->iattr, &attr, ret)) + return READ_ONCE(kn->iattr); + + return no_free_ptr(ret); } static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) { - return __kernfs_iattrs(kn, 1); + return __kernfs_iattrs(kn, true); } static struct kernfs_iattrs *kernfs_iattrs_noalloc(struct kernfs_node *kn) { - return __kernfs_iattrs(kn, 0); + return __kernfs_iattrs(kn, false); } int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) @@ -145,7 +144,8 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size) if (!attrs) return -ENOMEM; - return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); + return simple_xattr_list(d_inode(dentry), READ_ONCE(attrs->xattrs), + buf, size); } static inline void set_default_inode_attr(struct inode *inode, umode_t mode) @@ -166,9 +166,10 @@ static inline void set_inode_attr(struct inode *inode, static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) { - struct kernfs_iattrs *attrs = kn->iattr; + struct kernfs_iattrs *attrs; inode->i_mode = kn->mode; + attrs = kernfs_iattrs_noalloc(kn); if (attrs) /* * kernfs_node has non-default attributes get them from @@ -176,7 +177,7 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) */ set_inode_attr(inode, attrs); - if (kernfs_type(kn) == KERNFS_DIR) + if (kernfs_type(kn) == KERNFS_DIR && !(kn->flags & KERNFS_REMOVING)) set_nlink(inode, kn->dir.subdirs + 2); } @@ -249,7 +250,7 @@ struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn) struct inode *inode; inode = iget_locked(sb, kernfs_ino(kn)); - if (inode && (inode->i_state & I_NEW)) + if (inode && (inode_state_read_once(inode) & I_NEW)) kernfs_init_inode(kn, inode); return inode; @@ -296,25 +297,38 @@ int kernfs_xattr_get(struct kernfs_node *kn, const char *name, void *value, size_t size) { struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn); + struct simple_xattrs *xattrs; + if (!attrs) return -ENODATA; - return simple_xattr_get(&attrs->xattrs, name, value, size); + xattrs = READ_ONCE(attrs->xattrs); + if (!xattrs) + return -ENODATA; + + return simple_xattr_get(xattrs, name, value, size); } int kernfs_xattr_set(struct kernfs_node *kn, const char *name, const void *value, size_t size, int flags) { struct simple_xattr *old_xattr; - struct kernfs_iattrs *attrs = kernfs_iattrs(kn); + struct simple_xattrs *xattrs; + struct kernfs_iattrs *attrs; + + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; - old_xattr = simple_xattr_set(&attrs->xattrs, name, value, size, flags); + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); + if (IS_ERR_OR_NULL(xattrs)) + return PTR_ERR(xattrs); + + old_xattr = simple_xattr_set(xattrs, name, value, size, flags); if (IS_ERR(old_xattr)) return PTR_ERR(old_xattr); - simple_xattr_free(old_xattr); + simple_xattr_free_rcu(old_xattr); return 0; } @@ -340,67 +354,6 @@ static int kernfs_vfs_xattr_set(const struct xattr_handler *handler, return kernfs_xattr_set(kn, name, value, size, flags); } -static int kernfs_vfs_user_xattr_add(struct kernfs_node *kn, - const char *full_name, - struct simple_xattrs *xattrs, - const void *value, size_t size, int flags) -{ - atomic_t *sz = &kn->iattr->user_xattr_size; - atomic_t *nr = &kn->iattr->nr_user_xattrs; - struct simple_xattr *old_xattr; - int ret; - - if (atomic_inc_return(nr) > KERNFS_MAX_USER_XATTRS) { - ret = -ENOSPC; - goto dec_count_out; - } - - if (atomic_add_return(size, sz) > KERNFS_USER_XATTR_SIZE_LIMIT) { - ret = -ENOSPC; - goto dec_size_out; - } - - old_xattr = simple_xattr_set(xattrs, full_name, value, size, flags); - if (!old_xattr) - return 0; - - if (IS_ERR(old_xattr)) { - ret = PTR_ERR(old_xattr); - goto dec_size_out; - } - - ret = 0; - size = old_xattr->size; - simple_xattr_free(old_xattr); -dec_size_out: - atomic_sub(size, sz); -dec_count_out: - atomic_dec(nr); - return ret; -} - -static int kernfs_vfs_user_xattr_rm(struct kernfs_node *kn, - const char *full_name, - struct simple_xattrs *xattrs, - const void *value, size_t size, int flags) -{ - atomic_t *sz = &kn->iattr->user_xattr_size; - atomic_t *nr = &kn->iattr->nr_user_xattrs; - struct simple_xattr *old_xattr; - - old_xattr = simple_xattr_set(xattrs, full_name, value, size, flags); - if (!old_xattr) - return 0; - - if (IS_ERR(old_xattr)) - return PTR_ERR(old_xattr); - - atomic_sub(old_xattr->size, sz); - atomic_dec(nr); - simple_xattr_free(old_xattr); - return 0; -} - static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, struct mnt_idmap *idmap, struct dentry *unused, struct inode *inode, @@ -409,6 +362,7 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, { const char *full_name = xattr_full_name(handler, suffix); struct kernfs_node *kn = inode->i_private; + struct simple_xattrs *xattrs; struct kernfs_iattrs *attrs; if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR)) @@ -418,13 +372,12 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler, if (!attrs) return -ENOMEM; - if (value) - return kernfs_vfs_user_xattr_add(kn, full_name, &attrs->xattrs, - value, size, flags); - else - return kernfs_vfs_user_xattr_rm(kn, full_name, &attrs->xattrs, - value, size, flags); + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); + if (IS_ERR_OR_NULL(xattrs)) + return PTR_ERR(xattrs); + return simple_xattr_set_limited(xattrs, &attrs->xattr_limits, + full_name, value, size, flags); } static const struct xattr_handler kernfs_trusted_xattr_handler = { |
