mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
Merge patch series "Rework simple xattrs"
Christian Brauner (Amutable) <brauner@kernel.org> says: Rework the simple xattrs api to make it more efficient and easier to use for all consumers. * patches from https://patch.msgid.link/20260605135322.2632068-1-mszeredi@redhat.com: simpe_xattr: use per-sb cache simple_xattr: change interface to pass struct simple_xattrs ** tmpfs: simplify constructing "security.foo" xattr names kernfs: fix xattr race condition with multiple superblocks Link: https://patch.msgid.link/20260605135322.2632068-1-mszeredi@redhat.com Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
commit
3936c49a03
|
|
@ -605,11 +605,8 @@ void kernfs_put(struct kernfs_node *kn)
|
|||
if (kernfs_type(kn) == KERNFS_LINK)
|
||||
kernfs_put(kn->symlink.target_kn);
|
||||
|
||||
if (kn->iattr && kn->iattr->xattrs) {
|
||||
simple_xattrs_free(kn->iattr->xattrs, NULL);
|
||||
kfree(kn->iattr->xattrs);
|
||||
kn->iattr->xattrs = NULL;
|
||||
}
|
||||
if (kn->iattr)
|
||||
simple_xattrs_free(&root->xa_cache, &kn->iattr->xattrs, NULL);
|
||||
|
||||
spin_lock(&root->kernfs_idr_lock);
|
||||
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
|
||||
|
|
@ -624,6 +621,7 @@ void kernfs_put(struct kernfs_node *kn)
|
|||
} else {
|
||||
/* just released the root kn, free @root too */
|
||||
idr_destroy(&root->ino_idr);
|
||||
simple_xattr_cache_cleanup(&root->xa_cache);
|
||||
kfree_rcu(root, rcu);
|
||||
}
|
||||
}
|
||||
|
|
@ -709,10 +707,7 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
|
|||
|
||||
err_out4:
|
||||
if (kn->iattr) {
|
||||
if (kn->iattr->xattrs) {
|
||||
simple_xattrs_free(kn->iattr->xattrs, NULL);
|
||||
kfree(kn->iattr->xattrs);
|
||||
}
|
||||
simple_xattrs_free(&root->xa_cache, &kn->iattr->xattrs, NULL);
|
||||
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
||||
}
|
||||
err_out3:
|
||||
|
|
|
|||
|
|
@ -40,22 +40,15 @@ struct kernfs_open_node {
|
|||
static DEFINE_SPINLOCK(kernfs_notify_lock);
|
||||
static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL;
|
||||
|
||||
/* Compatibility wrappers - use the common hashed node lock */
|
||||
static inline struct mutex *kernfs_open_file_mutex_ptr(struct kernfs_node *kn)
|
||||
{
|
||||
int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS);
|
||||
|
||||
return &kernfs_locks->open_file_mutex[idx];
|
||||
return kernfs_node_lock_ptr(kn);
|
||||
}
|
||||
|
||||
static inline struct mutex *kernfs_open_file_mutex_lock(struct kernfs_node *kn)
|
||||
{
|
||||
struct mutex *lock;
|
||||
|
||||
lock = kernfs_open_file_mutex_ptr(kn);
|
||||
|
||||
mutex_lock(lock);
|
||||
|
||||
return lock;
|
||||
return kernfs_node_lock(kn);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, bool alloc)
|
|||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD_RCU(&ret->xattrs);
|
||||
/* assign default attributes */
|
||||
ret->ia_uid = GLOBAL_ROOT_UID;
|
||||
ret->ia_gid = GLOBAL_ROOT_GID;
|
||||
|
|
@ -144,8 +145,7 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
|
|||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
return simple_xattr_list(d_inode(dentry), READ_ONCE(attrs->xattrs),
|
||||
buf, size);
|
||||
return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size);
|
||||
}
|
||||
|
||||
static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
|
||||
|
|
@ -297,34 +297,35 @@ 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;
|
||||
struct simple_xattr_cache *cache = &kernfs_root(kn)->xa_cache;
|
||||
|
||||
if (!attrs)
|
||||
return -ENODATA;
|
||||
|
||||
xattrs = READ_ONCE(attrs->xattrs);
|
||||
if (!xattrs)
|
||||
return -ENODATA;
|
||||
|
||||
return simple_xattr_get(xattrs, name, value, size);
|
||||
return simple_xattr_get(cache, &attrs->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 simple_xattrs *xattrs;
|
||||
struct kernfs_iattrs *attrs;
|
||||
struct simple_xattr_cache *cache = &kernfs_root(kn)->xa_cache;
|
||||
|
||||
attrs = kernfs_iattrs(kn);
|
||||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags);
|
||||
if (IS_ERR_OR_NULL(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
/*
|
||||
* Protect xattr modifications with the hashed per-node mutex.
|
||||
* Multiple superblocks (with different namespaces) can share the same
|
||||
* kernfs_node, so inode locking alone is insufficient. The hashed mutex
|
||||
* ensures serialization of concurrent xattr operations on the same node,
|
||||
* including the lazy allocation of the xattrs structure itself.
|
||||
*/
|
||||
CLASS(kernfs_node_lock, lock)(kn);
|
||||
|
||||
old_xattr = simple_xattr_set(xattrs, name, value, size, flags);
|
||||
old_xattr = simple_xattr_set(cache, &attrs->xattrs, name, value, size, flags);
|
||||
if (IS_ERR(old_xattr))
|
||||
return PTR_ERR(old_xattr);
|
||||
|
||||
|
|
@ -362,7 +363,6 @@ 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))
|
||||
|
|
@ -372,11 +372,11 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler,
|
|||
if (!attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags);
|
||||
if (IS_ERR_OR_NULL(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
/* See comment in kernfs_xattr_set() about locking. */
|
||||
CLASS(kernfs_node_lock, lock)(kn);
|
||||
|
||||
return simple_xattr_set_limited(xattrs, &attrs->xattr_limits,
|
||||
return simple_xattr_set_limited(&kernfs_root(kn)->xa_cache,
|
||||
&attrs->xattrs, &attrs->xattr_limits,
|
||||
full_name, value, size, flags);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ struct kernfs_iattrs {
|
|||
struct timespec64 ia_mtime;
|
||||
struct timespec64 ia_ctime;
|
||||
|
||||
struct simple_xattrs *xattrs;
|
||||
struct list_head xattrs;
|
||||
struct simple_xattr_limits xattr_limits;
|
||||
};
|
||||
|
||||
|
|
@ -54,6 +54,8 @@ struct kernfs_root {
|
|||
rwlock_t kernfs_rename_lock;
|
||||
|
||||
struct rcu_head rcu;
|
||||
|
||||
struct simple_xattr_cache xa_cache;
|
||||
};
|
||||
|
||||
/* +1 to avoid triggering overflow warning when negating it */
|
||||
|
|
@ -211,4 +213,24 @@ extern const struct inode_operations kernfs_symlink_iops;
|
|||
* kernfs locks
|
||||
*/
|
||||
extern struct kernfs_global_locks *kernfs_locks;
|
||||
|
||||
/* Hashed mutex helpers - protect per-node data structures */
|
||||
static inline struct mutex *kernfs_node_lock_ptr(struct kernfs_node *kn)
|
||||
{
|
||||
int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS);
|
||||
|
||||
return &kernfs_locks->node_mutex[idx];
|
||||
}
|
||||
|
||||
static inline struct mutex *kernfs_node_lock(struct kernfs_node *kn)
|
||||
{
|
||||
struct mutex *lock = kernfs_node_lock_ptr(kn);
|
||||
|
||||
mutex_lock(lock);
|
||||
return lock;
|
||||
}
|
||||
|
||||
DEFINE_CLASS(kernfs_node_lock, struct mutex *,
|
||||
mutex_unlock(_T), kernfs_node_lock(kn), struct kernfs_node *kn)
|
||||
|
||||
#endif /* __KERNFS_INTERNAL_H */
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ static void __init kernfs_mutex_init(void)
|
|||
int count;
|
||||
|
||||
for (count = 0; count < NR_KERNFS_LOCKS; count++)
|
||||
mutex_init(&kernfs_locks->open_file_mutex[count]);
|
||||
mutex_init(&kernfs_locks->node_mutex[count]);
|
||||
}
|
||||
|
||||
static void __init kernfs_lock_init(void)
|
||||
|
|
|
|||
45
fs/pidfs.c
45
fs/pidfs.c
|
|
@ -37,6 +37,8 @@ static struct kmem_cache *pidfs_attr_cachep __ro_after_init;
|
|||
|
||||
static struct path pidfs_root_path = {};
|
||||
|
||||
static struct simple_xattr_cache pidfs_xa_cache;
|
||||
|
||||
void pidfs_get_root(struct path *path)
|
||||
{
|
||||
*path = pidfs_root_path;
|
||||
|
|
@ -96,7 +98,7 @@ static const struct rhashtable_params pidfs_ino_ht_params = {
|
|||
* use file handles.
|
||||
*/
|
||||
struct pidfs_attr {
|
||||
struct simple_xattrs *xattrs;
|
||||
struct list_head xattrs;
|
||||
union {
|
||||
struct pidfs_anon_attr;
|
||||
struct llist_node pidfs_llist;
|
||||
|
|
@ -196,12 +198,7 @@ static void pidfs_free_attr_work(struct work_struct *work)
|
|||
|
||||
head = llist_del_all(&pidfs_free_list);
|
||||
llist_for_each_entry_safe(attr, next, head, pidfs_llist) {
|
||||
struct simple_xattrs *xattrs = attr->xattrs;
|
||||
|
||||
if (xattrs) {
|
||||
simple_xattrs_free(xattrs, NULL);
|
||||
kfree(xattrs);
|
||||
}
|
||||
simple_xattrs_free(&pidfs_xa_cache, &attr->xattrs, NULL);
|
||||
kfree(attr);
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +226,7 @@ void pidfs_free_pid(struct pid *pid)
|
|||
if (IS_ERR(attr))
|
||||
return;
|
||||
|
||||
if (likely(!attr->xattrs))
|
||||
if (likely(list_empty(&attr->xattrs)))
|
||||
kfree(attr);
|
||||
else if (llist_add(&attr->pidfs_llist, &pidfs_free_list))
|
||||
schedule_work(&pidfs_free_work);
|
||||
|
|
@ -815,14 +812,8 @@ static ssize_t pidfs_listxattr(struct dentry *dentry, char *buf, size_t size)
|
|||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct pid *pid = inode->i_private;
|
||||
struct pidfs_attr *attr = pid->attr;
|
||||
struct simple_xattrs *xattrs;
|
||||
|
||||
xattrs = READ_ONCE(attr->xattrs);
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
return simple_xattr_list(inode, xattrs, buf, size);
|
||||
return simple_xattr_list(inode, &pid->attr->xattrs, buf, size);
|
||||
}
|
||||
|
||||
static const struct inode_operations pidfs_inode_operations = {
|
||||
|
|
@ -1018,6 +1009,8 @@ int pidfs_register_pid(struct pid *pid)
|
|||
if (!new_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD_RCU(&new_attr->xattrs);
|
||||
|
||||
/* Synchronize with pidfs_exit(). */
|
||||
guard(spinlock_irq)(&pid->wait_pidfd.lock);
|
||||
|
||||
|
|
@ -1057,16 +1050,9 @@ static int pidfs_xattr_get(const struct xattr_handler *handler,
|
|||
const char *suffix, void *value, size_t size)
|
||||
{
|
||||
struct pid *pid = inode->i_private;
|
||||
struct pidfs_attr *attr = pid->attr;
|
||||
const char *name;
|
||||
struct simple_xattrs *xattrs;
|
||||
const char *name = xattr_full_name(handler, suffix);
|
||||
|
||||
xattrs = READ_ONCE(attr->xattrs);
|
||||
if (!xattrs)
|
||||
return -ENODATA;
|
||||
|
||||
name = xattr_full_name(handler, suffix);
|
||||
return simple_xattr_get(xattrs, name, value, size);
|
||||
return simple_xattr_get(&pidfs_xa_cache, &pid->attr->xattrs, name, value, size);
|
||||
}
|
||||
|
||||
static int pidfs_xattr_set(const struct xattr_handler *handler,
|
||||
|
|
@ -1075,20 +1061,13 @@ static int pidfs_xattr_set(const struct xattr_handler *handler,
|
|||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct pid *pid = inode->i_private;
|
||||
struct pidfs_attr *attr = pid->attr;
|
||||
const char *name;
|
||||
struct simple_xattrs *xattrs;
|
||||
const char *name = xattr_full_name(handler, suffix);
|
||||
struct simple_xattr *old_xattr;
|
||||
|
||||
/* Ensure we're the only one to set @attr->xattrs. */
|
||||
WARN_ON_ONCE(!inode_is_locked(inode));
|
||||
|
||||
xattrs = simple_xattrs_lazy_alloc(&attr->xattrs, value, flags);
|
||||
if (IS_ERR_OR_NULL(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
|
||||
name = xattr_full_name(handler, suffix);
|
||||
old_xattr = simple_xattr_set(xattrs, name, value, size, flags);
|
||||
old_xattr = simple_xattr_set(&pidfs_xa_cache, &pid->attr->xattrs, name, value, size, flags);
|
||||
if (IS_ERR(old_xattr))
|
||||
return PTR_ERR(old_xattr);
|
||||
|
||||
|
|
|
|||
265
fs/xattr.c
265
fs/xattr.c
|
|
@ -28,6 +28,11 @@
|
|||
|
||||
#include "internal.h"
|
||||
|
||||
struct sx_key {
|
||||
const struct list_head *parent;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const char *
|
||||
strcmp_prefix(const char *a, const char *a_prefix)
|
||||
{
|
||||
|
|
@ -1269,23 +1274,32 @@ struct simple_xattr *simple_xattr_alloc(const void *value, size_t size)
|
|||
return new_xattr;
|
||||
}
|
||||
|
||||
static u32 sx_hashfn(const char *name, const struct list_head *parent, u32 seed)
|
||||
{
|
||||
return jhash(name, strlen(name), jhash(&parent, sizeof(parent), seed));
|
||||
}
|
||||
|
||||
static u32 simple_xattr_hashfn(const void *data, u32 len, u32 seed)
|
||||
{
|
||||
const char *name = data;
|
||||
return jhash(name, strlen(name), seed);
|
||||
const struct sx_key *key = data;
|
||||
|
||||
return sx_hashfn(key->name, key->parent, seed);
|
||||
}
|
||||
|
||||
static u32 simple_xattr_obj_hashfn(const void *obj, u32 len, u32 seed)
|
||||
{
|
||||
const struct simple_xattr *xattr = obj;
|
||||
return jhash(xattr->name, strlen(xattr->name), seed);
|
||||
|
||||
return sx_hashfn(xattr->name, xattr->parent, seed);
|
||||
}
|
||||
|
||||
static int simple_xattr_obj_cmpfn(struct rhashtable_compare_arg *arg,
|
||||
const void *obj)
|
||||
{
|
||||
const struct simple_xattr *xattr = obj;
|
||||
return strcmp(xattr->name, arg->key);
|
||||
const struct sx_key *key = arg->key;
|
||||
|
||||
return xattr->parent != key->parent || strcmp(xattr->name, key->name);
|
||||
}
|
||||
|
||||
static const struct rhashtable_params simple_xattr_params = {
|
||||
|
|
@ -1298,6 +1312,7 @@ static const struct rhashtable_params simple_xattr_params = {
|
|||
|
||||
/**
|
||||
* simple_xattr_get - get an xattr object
|
||||
* @cache: anchor for the hash table
|
||||
* @xattrs: the header of the xattr object
|
||||
* @name: the name of the xattr to retrieve
|
||||
* @buffer: the buffer to store the value into
|
||||
|
|
@ -1311,14 +1326,19 @@ static const struct rhashtable_params simple_xattr_params = {
|
|||
* Return: On success the length of the xattr value is returned. On error a
|
||||
* negative error code is returned.
|
||||
*/
|
||||
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
||||
void *buffer, size_t size)
|
||||
int simple_xattr_get(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
const char *name, void *buffer, size_t size)
|
||||
{
|
||||
struct simple_xattr *xattr;
|
||||
struct sx_key key = { .parent = xattrs, .name = name };
|
||||
struct rhashtable *ht = READ_ONCE(cache->ht);
|
||||
int ret = -ENODATA;
|
||||
|
||||
if (!ht)
|
||||
return ret;
|
||||
|
||||
guard(rcu)();
|
||||
xattr = rhashtable_lookup(&xattrs->ht, name, simple_xattr_params);
|
||||
xattr = rhashtable_lookup(ht, &key, simple_xattr_params);
|
||||
if (xattr) {
|
||||
ret = xattr->size;
|
||||
if (buffer) {
|
||||
|
|
@ -1331,8 +1351,45 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct rhashtable *simple_xattrs_lazy_alloc(struct simple_xattr_cache *cache,
|
||||
const void *value, int flags)
|
||||
{
|
||||
struct rhashtable *oldht, *ht = READ_ONCE(cache->ht);
|
||||
int err;
|
||||
|
||||
if (unlikely(!ht)) {
|
||||
if (!value)
|
||||
return (flags & XATTR_REPLACE) ? ERR_PTR(-ENODATA) : NULL;
|
||||
|
||||
ht = kzalloc_obj(*ht);
|
||||
if (!ht)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = rhashtable_init(ht, &simple_xattr_params);
|
||||
if (err) {
|
||||
kfree(ht);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Provides release semantics on success, so that use of a
|
||||
* non-NULL READ_ONCE(cache->ht) will be ordered relative to the
|
||||
* above initialization, due to implicit address dependency.
|
||||
*/
|
||||
oldht = cmpxchg_release(&cache->ht, NULL, ht);
|
||||
if (oldht) {
|
||||
/* Race lost */
|
||||
rhashtable_destroy(ht);
|
||||
kfree(ht);
|
||||
ht = oldht;
|
||||
}
|
||||
}
|
||||
return ht;
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_xattr_set - set an xattr object
|
||||
* @cache: anchor for the hash table
|
||||
* @xattrs: the header of the xattr object
|
||||
* @name: the name of the xattr to retrieve
|
||||
* @value: the value to store along the xattr
|
||||
|
|
@ -1362,45 +1419,58 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
|||
* Return: On success, the removed or replaced xattr is returned, to be freed
|
||||
* by the caller; or NULL if none. On failure a negative error code is returned.
|
||||
*/
|
||||
struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs,
|
||||
struct simple_xattr *simple_xattr_set(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct sx_key key = { .parent = xattrs, .name = name };
|
||||
struct simple_xattr *old_xattr = NULL;
|
||||
struct rhashtable *ht;
|
||||
int err;
|
||||
|
||||
ht = simple_xattrs_lazy_alloc(cache, value, flags);
|
||||
if (IS_ERR_OR_NULL(ht))
|
||||
return ERR_CAST(ht);
|
||||
|
||||
CLASS(simple_xattr, new_xattr)(value, size);
|
||||
if (IS_ERR(new_xattr))
|
||||
return new_xattr;
|
||||
|
||||
if (new_xattr) {
|
||||
new_xattr->parent = xattrs;
|
||||
new_xattr->name = kstrdup(name, GFP_KERNEL_ACCOUNT);
|
||||
if (!new_xattr->name)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* Lookup is safe without RCU here since writes are serialized. */
|
||||
old_xattr = rhashtable_lookup_fast(&xattrs->ht, name,
|
||||
simple_xattr_params);
|
||||
|
||||
/*
|
||||
* Hash table lookup/replace/remove will grab RCU read lock themselves.
|
||||
* This makes sure that hash table lookup is safe against concurrent
|
||||
* modification on another inode.
|
||||
*/
|
||||
old_xattr = rhashtable_lookup_fast(ht, &key, simple_xattr_params);
|
||||
if (old_xattr) {
|
||||
/* Fail if XATTR_CREATE is requested and the xattr exists. */
|
||||
if (flags & XATTR_CREATE)
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
if (new_xattr) {
|
||||
err = rhashtable_replace_fast(&xattrs->ht,
|
||||
err = rhashtable_replace_fast(ht,
|
||||
&old_xattr->hash_node,
|
||||
&new_xattr->hash_node,
|
||||
simple_xattr_params);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
list_replace_rcu(&old_xattr->node, &new_xattr->node);
|
||||
} else {
|
||||
err = rhashtable_remove_fast(&xattrs->ht,
|
||||
err = rhashtable_remove_fast(ht,
|
||||
&old_xattr->hash_node,
|
||||
simple_xattr_params);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
list_del_rcu(&old_xattr->node);
|
||||
}
|
||||
} else {
|
||||
/* Fail if XATTR_REPLACE is requested but no xattr is found. */
|
||||
|
|
@ -1412,11 +1482,13 @@ struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs,
|
|||
* new value simply insert it.
|
||||
*/
|
||||
if (new_xattr) {
|
||||
err = rhashtable_insert_fast(&xattrs->ht,
|
||||
err = rhashtable_insert_fast(ht,
|
||||
&new_xattr->hash_node,
|
||||
simple_xattr_params);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
list_add_tail_rcu(&new_xattr->node, xattrs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1453,6 +1525,7 @@ static inline int simple_xattr_limits_inc(struct simple_xattr_limits *limits,
|
|||
|
||||
/**
|
||||
* simple_xattr_set_limited - set an xattr with per-inode user.* limits
|
||||
* @cache: anchor for the hash table
|
||||
* @xattrs: the header of the xattr object
|
||||
* @limits: per-inode limit counters for user.* xattrs
|
||||
* @name: the name of the xattr to set or remove
|
||||
|
|
@ -1467,7 +1540,7 @@ static inline int simple_xattr_limits_inc(struct simple_xattr_limits *limits,
|
|||
* Return: On success zero is returned. On failure a negative error code is
|
||||
* returned.
|
||||
*/
|
||||
int simple_xattr_set_limited(struct simple_xattrs *xattrs,
|
||||
int simple_xattr_set_limited(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
struct simple_xattr_limits *limits,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
|
|
@ -1481,7 +1554,7 @@ int simple_xattr_set_limited(struct simple_xattrs *xattrs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
old_xattr = simple_xattr_set(xattrs, name, value, size, flags);
|
||||
old_xattr = simple_xattr_set(cache, xattrs, name, value, size, flags);
|
||||
if (IS_ERR(old_xattr)) {
|
||||
if (value)
|
||||
simple_xattr_limits_dec(limits, size);
|
||||
|
|
@ -1527,11 +1600,10 @@ static bool xattr_is_maclabel(const char *name)
|
|||
* Return: On success the required size or the size of the copied xattrs is
|
||||
* returned. On error a negative error code is returned.
|
||||
*/
|
||||
ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
|
||||
ssize_t simple_xattr_list(struct inode *inode, struct list_head *xattrs,
|
||||
char *buffer, size_t size)
|
||||
{
|
||||
bool trusted = ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
|
||||
struct rhashtable_iter iter;
|
||||
struct simple_xattr *xattr;
|
||||
ssize_t remaining_size = size;
|
||||
int err = 0;
|
||||
|
|
@ -1555,17 +1627,8 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
|
|||
if (!xattrs)
|
||||
return size - remaining_size;
|
||||
|
||||
rhashtable_walk_enter(&xattrs->ht, &iter);
|
||||
rhashtable_walk_start(&iter);
|
||||
|
||||
while ((xattr = rhashtable_walk_next(&iter)) != NULL) {
|
||||
if (IS_ERR(xattr)) {
|
||||
if (PTR_ERR(xattr) == -EAGAIN)
|
||||
continue;
|
||||
err = PTR_ERR(xattr);
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(xattr, xattrs, node) {
|
||||
/* skip "trusted." attributes for unprivileged callers */
|
||||
if (!trusted && xattr_is_trusted(xattr->name))
|
||||
continue;
|
||||
|
|
@ -1578,15 +1641,14 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
|
|||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
rhashtable_walk_stop(&iter);
|
||||
rhashtable_walk_exit(&iter);
|
||||
rcu_read_unlock();
|
||||
|
||||
return err ? err : size - remaining_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_xattr_add - add xattr objects
|
||||
* @cache: anchor for the hash table
|
||||
* @xattrs: the header of the xattr object
|
||||
* @new_xattr: the xattr object to add
|
||||
*
|
||||
|
|
@ -1597,112 +1659,67 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
|
|||
* Return: On success zero is returned. On failure a negative error code is
|
||||
* returned.
|
||||
*/
|
||||
int simple_xattr_add(struct simple_xattrs *xattrs,
|
||||
int simple_xattr_add(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
struct simple_xattr *new_xattr)
|
||||
{
|
||||
return rhashtable_insert_fast(&xattrs->ht, &new_xattr->hash_node,
|
||||
simple_xattr_params);
|
||||
}
|
||||
struct rhashtable *ht;
|
||||
int err;
|
||||
|
||||
/**
|
||||
* simple_xattrs_init - initialize new xattr header
|
||||
* @xattrs: header to initialize
|
||||
*
|
||||
* Initialize the rhashtable used to store xattr objects.
|
||||
*
|
||||
* Return: On success zero is returned. On failure a negative error code is
|
||||
* returned.
|
||||
*/
|
||||
int simple_xattrs_init(struct simple_xattrs *xattrs)
|
||||
{
|
||||
return rhashtable_init(&xattrs->ht, &simple_xattr_params);
|
||||
}
|
||||
ht = simple_xattrs_lazy_alloc(cache, new_xattr->value, 0);
|
||||
if (IS_ERR(ht))
|
||||
return PTR_ERR(ht);
|
||||
|
||||
/**
|
||||
* simple_xattrs_alloc - allocate and initialize a new xattr header
|
||||
*
|
||||
* Dynamically allocate a simple_xattrs header and initialize the
|
||||
* underlying rhashtable. This is intended for consumers that want
|
||||
* to lazily allocate xattr storage only when the first xattr is set,
|
||||
* avoiding the per-inode rhashtable overhead when no xattrs are used.
|
||||
*
|
||||
* Return: On success a new simple_xattrs is returned. On failure an
|
||||
* ERR_PTR is returned.
|
||||
*/
|
||||
struct simple_xattrs *simple_xattrs_alloc(void)
|
||||
{
|
||||
struct simple_xattrs *xattrs __free(kfree) = NULL;
|
||||
int ret;
|
||||
new_xattr->parent = xattrs;
|
||||
err = rhashtable_insert_fast(ht, &new_xattr->hash_node, simple_xattr_params);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xattrs = kzalloc(sizeof(*xattrs), GFP_KERNEL);
|
||||
if (!xattrs)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = simple_xattrs_init(xattrs);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return no_free_ptr(xattrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_xattrs_lazy_alloc - get or allocate xattrs for a set operation
|
||||
* @xattrsp: pointer to the xattrs pointer (may point to NULL)
|
||||
* @value: value being set (NULL means remove)
|
||||
* @flags: xattr set flags
|
||||
*
|
||||
* For lazily-allocated xattrs on the write path. If no xattrs exist yet
|
||||
* and this is a remove operation, returns the appropriate result without
|
||||
* allocating. Otherwise ensures xattrs is allocated and published with
|
||||
* store-release semantics.
|
||||
*
|
||||
* Return: On success a valid pointer to the xattrs is returned. On
|
||||
* failure or early-exit an ERR_PTR or NULL is returned. Callers should
|
||||
* check with IS_ERR_OR_NULL() and propagate with PTR_ERR() which
|
||||
* correctly returns 0 for the NULL no-op case.
|
||||
*/
|
||||
struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp,
|
||||
const void *value, int flags)
|
||||
{
|
||||
struct simple_xattrs *xattrs;
|
||||
|
||||
xattrs = READ_ONCE(*xattrsp);
|
||||
if (xattrs)
|
||||
return xattrs;
|
||||
|
||||
if (!value)
|
||||
return (flags & XATTR_REPLACE) ? ERR_PTR(-ENODATA) : NULL;
|
||||
|
||||
xattrs = simple_xattrs_alloc();
|
||||
if (!IS_ERR(xattrs))
|
||||
smp_store_release(xattrsp, xattrs);
|
||||
return xattrs;
|
||||
}
|
||||
|
||||
static void simple_xattr_ht_free(void *ptr, void *arg)
|
||||
{
|
||||
struct simple_xattr *xattr = ptr;
|
||||
size_t *freed_space = arg;
|
||||
|
||||
if (freed_space)
|
||||
*freed_space += simple_xattr_space(xattr->name, xattr->size);
|
||||
simple_xattr_free(xattr);
|
||||
list_add_tail_rcu(&new_xattr->node, xattrs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_xattrs_free - free xattrs
|
||||
* @cache: anchor for the hash table
|
||||
* @xattrs: xattr header whose xattrs to destroy
|
||||
* @freed_space: approximate number of bytes of memory freed from @xattrs
|
||||
*
|
||||
* Destroy all xattrs in @xattr. When this is called no one can hold a
|
||||
* Destroy all xattrs in @xattrs. When this is called no one can hold a
|
||||
* reference to any of the xattrs anymore.
|
||||
*/
|
||||
void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space)
|
||||
void simple_xattrs_free(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
size_t *freed_space)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
if (freed_space)
|
||||
*freed_space = 0;
|
||||
rhashtable_free_and_destroy(&xattrs->ht, simple_xattr_ht_free,
|
||||
freed_space);
|
||||
|
||||
while (!list_empty(xattrs)) {
|
||||
struct simple_xattr *xattr = list_first_entry(xattrs, typeof(*xattr), node);
|
||||
|
||||
rhashtable_remove_fast(cache->ht, &xattr->hash_node, simple_xattr_params);
|
||||
list_del(&xattr->node);
|
||||
if (freed_space)
|
||||
*freed_space += simple_xattr_space(xattr->name, xattr->size);
|
||||
/*
|
||||
* Free with RCU, since the xattr might still get accessed by
|
||||
* the hash compare function
|
||||
*/
|
||||
simple_xattr_free_rcu(xattr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_xattr_cache_cleanup - free the cache
|
||||
* @cache: anchor for the hash table
|
||||
*
|
||||
* Destroy the cache table, which was lazily allocated on adding the first xattr.
|
||||
*/
|
||||
void simple_xattr_cache_cleanup(struct simple_xattr_cache *cache)
|
||||
{
|
||||
if (cache->ht) {
|
||||
WARN_ON(atomic_read(&cache->ht->nelems));
|
||||
rhashtable_destroy(cache->ht);
|
||||
kfree(cache->ht);
|
||||
cache->ht = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,20 +76,25 @@ struct kernfs_iattrs;
|
|||
* kernfs_open_file.
|
||||
*
|
||||
* kernfs_open_files are chained at kernfs_open_node->files, which is
|
||||
* protected by kernfs_global_locks.open_file_mutex[i].
|
||||
* protected by kernfs_global_locks.node_mutex[i].
|
||||
*
|
||||
* To reduce possible contention in sysfs access, arising due to single
|
||||
* locks, use an array of locks (e.g. open_file_mutex) and use kernfs_node
|
||||
* locks, use an array of locks (e.g. node_mutex) and use kernfs_node
|
||||
* object address as hash keys to get the index of these locks.
|
||||
*
|
||||
* Hashed mutexes are safe to use here because operations using these don't
|
||||
* rely on global exclusion.
|
||||
*
|
||||
* The hashed mutex array protects per-node data: the kernfs_open_node for
|
||||
* open file management, and kernfs_node xattr operations (necessary because
|
||||
* multiple superblocks with different namespaces can share the same
|
||||
* kernfs_node, making per-inode locking insufficient).
|
||||
*
|
||||
* In future we intend to replace other global locks with hashed ones as well.
|
||||
* kernfs_global_locks acts as a holder for all such hash tables.
|
||||
*/
|
||||
struct kernfs_global_locks {
|
||||
struct mutex open_file_mutex[NR_KERNFS_LOCKS];
|
||||
struct mutex node_mutex[NR_KERNFS_LOCKS];
|
||||
};
|
||||
|
||||
enum kernfs_node_type {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ struct shmem_inode_info {
|
|||
};
|
||||
struct timespec64 i_crtime; /* file creation time */
|
||||
struct shared_policy policy; /* NUMA memory alloc policy */
|
||||
struct simple_xattrs *xattrs; /* list of xattrs */
|
||||
struct list_head xattrs; /* list of xattrs */
|
||||
pgoff_t fallocend; /* highest fallocate endindex */
|
||||
unsigned int fsflags; /* for FS_IOC_[SG]ETFLAGS */
|
||||
atomic_t stop_eviction; /* hold when working on inode */
|
||||
|
|
@ -89,6 +89,7 @@ struct shmem_sb_info {
|
|||
struct list_head shrinklist; /* List of shinkable inodes */
|
||||
unsigned long shrinklist_len; /* Length of shrinklist */
|
||||
struct shmem_quota_limits qlimits; /* Default quota limits */
|
||||
struct simple_xattr_cache xa_cache;
|
||||
};
|
||||
|
||||
static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
|
||||
|
|
|
|||
|
|
@ -106,12 +106,14 @@ static inline const char *xattr_prefix(const struct xattr_handler *handler)
|
|||
return handler->prefix ?: handler->name;
|
||||
}
|
||||
|
||||
struct simple_xattrs {
|
||||
struct rhashtable ht;
|
||||
struct simple_xattr_cache {
|
||||
struct rhashtable *ht;
|
||||
};
|
||||
|
||||
struct simple_xattr {
|
||||
struct rhash_head hash_node;
|
||||
struct list_head *parent;
|
||||
struct list_head node;
|
||||
struct rcu_head rcu;
|
||||
char *name;
|
||||
size_t size;
|
||||
|
|
@ -132,40 +134,35 @@ static inline void simple_xattr_limits_init(struct simple_xattr_limits *limits)
|
|||
atomic_set(&limits->xattr_size, 0);
|
||||
}
|
||||
|
||||
int simple_xattrs_init(struct simple_xattrs *xattrs);
|
||||
struct simple_xattrs *simple_xattrs_alloc(void);
|
||||
struct simple_xattrs *simple_xattrs_lazy_alloc(struct simple_xattrs **xattrsp,
|
||||
const void *value, int flags);
|
||||
void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space);
|
||||
void simple_xattrs_free(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
size_t *freed_space);
|
||||
size_t simple_xattr_space(const char *name, size_t size);
|
||||
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size);
|
||||
void simple_xattr_free(struct simple_xattr *xattr);
|
||||
void simple_xattr_free_rcu(struct simple_xattr *xattr);
|
||||
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
|
||||
void *buffer, size_t size);
|
||||
struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs,
|
||||
int simple_xattr_get(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
const char *name, void *buffer, size_t size);
|
||||
struct simple_xattr *simple_xattr_set(struct simple_xattr_cache *cache,
|
||||
struct list_head *xattrs,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
int simple_xattr_set_limited(struct simple_xattrs *xattrs,
|
||||
int simple_xattr_set_limited(struct simple_xattr_cache *cache,
|
||||
struct list_head *xattrs,
|
||||
struct simple_xattr_limits *limits,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
|
||||
ssize_t simple_xattr_list(struct inode *inode, struct list_head *xattrs,
|
||||
char *buffer, size_t size);
|
||||
int simple_xattr_add(struct simple_xattrs *xattrs,
|
||||
int simple_xattr_add(struct simple_xattr_cache *cache, struct list_head *xattrs,
|
||||
struct simple_xattr *new_xattr);
|
||||
int xattr_list_one(char **buffer, ssize_t *remaining_size, const char *name);
|
||||
|
||||
void simple_xattr_cache_cleanup(struct simple_xattr_cache *cache);
|
||||
|
||||
DEFINE_CLASS(simple_xattr,
|
||||
struct simple_xattr *,
|
||||
if (!IS_ERR_OR_NULL(_T)) simple_xattr_free(_T),
|
||||
simple_xattr_alloc(value, size),
|
||||
const void *value, size_t size)
|
||||
|
||||
DEFINE_CLASS(simple_xattrs,
|
||||
struct simple_xattrs *,
|
||||
if (!IS_ERR_OR_NULL(_T)) { simple_xattrs_free(_T, NULL); kfree(_T); },
|
||||
simple_xattrs_alloc(),
|
||||
void)
|
||||
|
||||
#endif /* _LINUX_XATTR_H */
|
||||
|
|
|
|||
50
mm/shmem.c
50
mm/shmem.c
|
|
@ -1425,10 +1425,8 @@ static void shmem_evict_inode(struct inode *inode)
|
|||
}
|
||||
}
|
||||
|
||||
if (info->xattrs) {
|
||||
simple_xattrs_free(info->xattrs, sbinfo->max_inodes ? &freed : NULL);
|
||||
kfree(info->xattrs);
|
||||
}
|
||||
simple_xattrs_free(&sbinfo->xa_cache, &info->xattrs, sbinfo->max_inodes ? &freed : NULL);
|
||||
|
||||
shmem_free_inode(inode->i_sb, freed);
|
||||
WARN_ON(inode->i_blocks);
|
||||
clear_inode(inode);
|
||||
|
|
@ -3086,6 +3084,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
|
|||
inode->i_generation = get_random_u32();
|
||||
info = SHMEM_I(inode);
|
||||
memset(info, 0, (char *)inode - (char *)info);
|
||||
INIT_LIST_HEAD_RCU(&info->xattrs);
|
||||
spin_lock_init(&info->lock);
|
||||
atomic_set(&info->stop_eviction, 0);
|
||||
info->seals = F_SEAL_SEAL;
|
||||
|
|
@ -4232,11 +4231,6 @@ static int shmem_initxattrs(struct inode *inode,
|
|||
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
|
||||
const struct xattr *xattr;
|
||||
size_t ispace = 0;
|
||||
size_t len;
|
||||
|
||||
CLASS(simple_xattrs, xattrs)();
|
||||
if (IS_ERR(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
|
||||
if (sbinfo->max_inodes) {
|
||||
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
|
||||
|
|
@ -4260,19 +4254,16 @@ static int shmem_initxattrs(struct inode *inode,
|
|||
if (IS_ERR(new_xattr))
|
||||
break;
|
||||
|
||||
len = strlen(xattr->name) + 1;
|
||||
new_xattr->name = kmalloc(XATTR_SECURITY_PREFIX_LEN + len,
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
new_xattr->name = kasprintf(GFP_KERNEL_ACCOUNT,
|
||||
XATTR_SECURITY_PREFIX "%s", xattr->name);
|
||||
if (!new_xattr->name)
|
||||
break;
|
||||
|
||||
memcpy(new_xattr->name, XATTR_SECURITY_PREFIX,
|
||||
XATTR_SECURITY_PREFIX_LEN);
|
||||
memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN,
|
||||
xattr->name, len);
|
||||
|
||||
if (simple_xattr_add(xattrs, new_xattr))
|
||||
if (simple_xattr_add(&sbinfo->xa_cache, &info->xattrs, new_xattr))
|
||||
break;
|
||||
|
||||
if (sbinfo->max_inodes)
|
||||
ispace -= simple_xattr_space(new_xattr->name, new_xattr->size);
|
||||
retain_and_null_ptr(new_xattr);
|
||||
}
|
||||
|
||||
|
|
@ -4284,8 +4275,8 @@ static int shmem_initxattrs(struct inode *inode,
|
|||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
WARN_ON(ispace);
|
||||
|
||||
smp_store_release(&info->xattrs, no_free_ptr(xattrs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -4293,15 +4284,11 @@ static int shmem_xattr_handler_get(const struct xattr_handler *handler,
|
|||
struct dentry *unused, struct inode *inode,
|
||||
const char *name, void *buffer, size_t size)
|
||||
{
|
||||
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
|
||||
struct shmem_inode_info *info = SHMEM_I(inode);
|
||||
struct simple_xattrs *xattrs;
|
||||
|
||||
xattrs = READ_ONCE(info->xattrs);
|
||||
if (!xattrs)
|
||||
return -ENODATA;
|
||||
|
||||
name = xattr_full_name(handler, name);
|
||||
return simple_xattr_get(xattrs, name, buffer, size);
|
||||
return simple_xattr_get(&sbinfo->xa_cache, &info->xattrs, name, buffer, size);
|
||||
}
|
||||
|
||||
static int shmem_xattr_handler_set(const struct xattr_handler *handler,
|
||||
|
|
@ -4312,16 +4299,11 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler,
|
|||
{
|
||||
struct shmem_inode_info *info = SHMEM_I(inode);
|
||||
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
|
||||
struct simple_xattrs *xattrs;
|
||||
struct simple_xattr *old_xattr;
|
||||
size_t ispace = 0;
|
||||
|
||||
name = xattr_full_name(handler, name);
|
||||
|
||||
xattrs = simple_xattrs_lazy_alloc(&info->xattrs, value, flags);
|
||||
if (IS_ERR_OR_NULL(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
|
||||
if (value && sbinfo->max_inodes) {
|
||||
ispace = simple_xattr_space(name, size);
|
||||
raw_spin_lock(&sbinfo->stat_lock);
|
||||
|
|
@ -4334,7 +4316,7 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler,
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
old_xattr = simple_xattr_set(xattrs, name, value, size, flags);
|
||||
old_xattr = simple_xattr_set(&sbinfo->xa_cache, &info->xattrs, name, value, size, flags);
|
||||
if (!IS_ERR(old_xattr)) {
|
||||
ispace = 0;
|
||||
if (old_xattr && sbinfo->max_inodes)
|
||||
|
|
@ -4382,8 +4364,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
|||
{
|
||||
struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
|
||||
|
||||
return simple_xattr_list(d_inode(dentry), READ_ONCE(info->xattrs),
|
||||
buffer, size);
|
||||
return simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, size);
|
||||
}
|
||||
#endif /* CONFIG_TMPFS_XATTR */
|
||||
|
||||
|
|
@ -4984,6 +4965,9 @@ static void shmem_put_super(struct super_block *sb)
|
|||
free_percpu(sbinfo->ino_batch);
|
||||
percpu_counter_destroy(&sbinfo->used_blocks);
|
||||
mpol_put(sbinfo->mpol);
|
||||
#ifdef CONFIG_TMPFS_XATTR
|
||||
simple_xattr_cache_cleanup(&sbinfo->xa_cache);
|
||||
#endif
|
||||
kfree(sbinfo);
|
||||
sb->s_fs_info = NULL;
|
||||
}
|
||||
|
|
|
|||
30
net/socket.c
30
net/socket.c
|
|
@ -310,8 +310,10 @@ static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
|
|||
|
||||
static struct kmem_cache *sock_inode_cachep __ro_after_init;
|
||||
|
||||
static struct simple_xattr_cache sockfs_xa_cache;
|
||||
|
||||
struct sockfs_inode {
|
||||
struct simple_xattrs *xattrs;
|
||||
struct list_head xattrs;
|
||||
struct simple_xattr_limits xattr_limits;
|
||||
struct socket_alloc;
|
||||
};
|
||||
|
|
@ -328,7 +330,7 @@ static struct inode *sock_alloc_inode(struct super_block *sb)
|
|||
si = alloc_inode_sb(sb, sock_inode_cachep, GFP_KERNEL);
|
||||
if (!si)
|
||||
return NULL;
|
||||
si->xattrs = NULL;
|
||||
INIT_LIST_HEAD_RCU(&si->xattrs);
|
||||
simple_xattr_limits_init(&si->xattr_limits);
|
||||
|
||||
init_waitqueue_head(&si->socket.wq.wait);
|
||||
|
|
@ -347,12 +349,8 @@ static struct inode *sock_alloc_inode(struct super_block *sb)
|
|||
static void sock_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct sockfs_inode *si = SOCKFS_I(inode);
|
||||
struct simple_xattrs *xattrs = si->xattrs;
|
||||
|
||||
if (xattrs) {
|
||||
simple_xattrs_free(xattrs, NULL);
|
||||
kfree(xattrs);
|
||||
}
|
||||
simple_xattrs_free(&sockfs_xa_cache, &si->xattrs, NULL);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
|
@ -443,13 +441,9 @@ static int sockfs_user_xattr_get(const struct xattr_handler *handler,
|
|||
const char *suffix, void *value, size_t size)
|
||||
{
|
||||
const char *name = xattr_full_name(handler, suffix);
|
||||
struct simple_xattrs *xattrs;
|
||||
struct sockfs_inode *si = SOCKFS_I(inode);
|
||||
|
||||
xattrs = READ_ONCE(SOCKFS_I(inode)->xattrs);
|
||||
if (!xattrs)
|
||||
return -ENODATA;
|
||||
|
||||
return simple_xattr_get(xattrs, name, value, size);
|
||||
return simple_xattr_get(&sockfs_xa_cache, &si->xattrs, name, value, size);
|
||||
}
|
||||
|
||||
static int sockfs_user_xattr_set(const struct xattr_handler *handler,
|
||||
|
|
@ -460,13 +454,8 @@ static int sockfs_user_xattr_set(const struct xattr_handler *handler,
|
|||
{
|
||||
const char *name = xattr_full_name(handler, suffix);
|
||||
struct sockfs_inode *si = SOCKFS_I(inode);
|
||||
struct simple_xattrs *xattrs;
|
||||
|
||||
xattrs = simple_xattrs_lazy_alloc(&si->xattrs, value, flags);
|
||||
if (IS_ERR_OR_NULL(xattrs))
|
||||
return PTR_ERR(xattrs);
|
||||
|
||||
return simple_xattr_set_limited(xattrs, &si->xattr_limits,
|
||||
return simple_xattr_set_limited(&sockfs_xa_cache, &si->xattrs, &si->xattr_limits,
|
||||
name, value, size, flags);
|
||||
}
|
||||
|
||||
|
|
@ -635,8 +624,7 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer,
|
|||
struct sockfs_inode *si = SOCKFS_I(d_inode(dentry));
|
||||
ssize_t len, used;
|
||||
|
||||
len = simple_xattr_list(d_inode(dentry), READ_ONCE(si->xattrs),
|
||||
buffer, size);
|
||||
len = simple_xattr_list(d_inode(dentry), &si->xattrs, buffer, size);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user