mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
kernfs: adapt to rhashtable-based simple_xattrs with lazy allocation
Adapt kernfs to use the rhashtable-based xattr path and switch from an embedded struct to pointer-based lazy allocation. Change kernfs_iattrs.xattrs from embedded 'struct simple_xattrs' to a pointer 'struct simple_xattrs *', initialized to NULL (zeroed by kmem_cache_zalloc). Since kernfs_iattrs is already lazily allocated itself, this adds a second level of lazy allocation specifically for the xattr store. The xattr store is allocated on first setxattr. Read paths check for NULL and return -ENODATA or empty list. Replaced xattr entries are freed via simple_xattr_free_rcu() to allow concurrent RCU readers to finish. The cleanup paths in kernfs_free_rcu() and __kernfs_new_node() error handling conditionally free the xattr store only when allocated. As Jan noted in [1]: > This is a slight change in the lifetime rules because previously kernfs > xattrs could be safely accessed only under RCU but after this change you > have to hold inode reference *and* RCU to safely access them. I don't think > anybody would be accessing xattrs without holding inode reference so this > should be safe [...]. Link: https://patch.msgid.link/20260216-work-xattr-socket-v1-4-c2efa4f74cb7@kernel.org Link: https://lore.kernel.org/3cnmtqmakpbb2uwhenrj7kdqu3uefykiykjllgfbtpkiwhaa4s@sghkevv7jned [1] Acked-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
52b364fed6
commit
5bd97f5c5f
|
|
@ -547,10 +547,8 @@ static void kernfs_free_rcu(struct rcu_head *rcu)
|
||||||
/* If the whole node goes away, then name can't be used outside */
|
/* If the whole node goes away, then name can't be used outside */
|
||||||
kfree_const(rcu_access_pointer(kn->name));
|
kfree_const(rcu_access_pointer(kn->name));
|
||||||
|
|
||||||
if (kn->iattr) {
|
if (kn->iattr)
|
||||||
simple_xattrs_free(&kn->iattr->xattrs, NULL);
|
|
||||||
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
||||||
}
|
|
||||||
|
|
||||||
kmem_cache_free(kernfs_node_cache, kn);
|
kmem_cache_free(kernfs_node_cache, kn);
|
||||||
}
|
}
|
||||||
|
|
@ -584,6 +582,12 @@ void kernfs_put(struct kernfs_node *kn)
|
||||||
if (kernfs_type(kn) == KERNFS_LINK)
|
if (kernfs_type(kn) == KERNFS_LINK)
|
||||||
kernfs_put(kn->symlink.target_kn);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock(&root->kernfs_idr_lock);
|
spin_lock(&root->kernfs_idr_lock);
|
||||||
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
|
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
|
||||||
spin_unlock(&root->kernfs_idr_lock);
|
spin_unlock(&root->kernfs_idr_lock);
|
||||||
|
|
@ -682,7 +686,10 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
|
||||||
|
|
||||||
err_out4:
|
err_out4:
|
||||||
if (kn->iattr) {
|
if (kn->iattr) {
|
||||||
simple_xattrs_free(&kn->iattr->xattrs, NULL);
|
if (kn->iattr->xattrs) {
|
||||||
|
simple_xattrs_free(kn->iattr->xattrs, NULL);
|
||||||
|
kfree(kn->iattr->xattrs);
|
||||||
|
}
|
||||||
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
|
||||||
}
|
}
|
||||||
err_out3:
|
err_out3:
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, bool alloc)
|
||||||
ret->ia_mtime = ret->ia_atime;
|
ret->ia_mtime = ret->ia_atime;
|
||||||
ret->ia_ctime = ret->ia_atime;
|
ret->ia_ctime = ret->ia_atime;
|
||||||
|
|
||||||
simple_xattrs_init(&ret->xattrs);
|
|
||||||
atomic_set(&ret->nr_user_xattrs, 0);
|
atomic_set(&ret->nr_user_xattrs, 0);
|
||||||
atomic_set(&ret->user_xattr_size, 0);
|
atomic_set(&ret->user_xattr_size, 0);
|
||||||
|
|
||||||
|
|
@ -146,7 +145,8 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
|
||||||
if (!attrs)
|
if (!attrs)
|
||||||
return -ENOMEM;
|
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)
|
static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
|
||||||
|
|
@ -298,27 +298,38 @@ int kernfs_xattr_get(struct kernfs_node *kn, const char *name,
|
||||||
void *value, size_t size)
|
void *value, size_t size)
|
||||||
{
|
{
|
||||||
struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn);
|
struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn);
|
||||||
|
struct simple_xattrs *xattrs;
|
||||||
|
|
||||||
if (!attrs)
|
if (!attrs)
|
||||||
return -ENODATA;
|
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,
|
int kernfs_xattr_set(struct kernfs_node *kn, const char *name,
|
||||||
const void *value, size_t size, int flags)
|
const void *value, size_t size, int flags)
|
||||||
{
|
{
|
||||||
struct simple_xattr *old_xattr;
|
struct simple_xattr *old_xattr;
|
||||||
|
struct simple_xattrs *xattrs;
|
||||||
struct kernfs_iattrs *attrs;
|
struct kernfs_iattrs *attrs;
|
||||||
|
|
||||||
attrs = kernfs_iattrs(kn);
|
attrs = kernfs_iattrs(kn);
|
||||||
if (!attrs)
|
if (!attrs)
|
||||||
return -ENOMEM;
|
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))
|
if (IS_ERR(old_xattr))
|
||||||
return PTR_ERR(old_xattr);
|
return PTR_ERR(old_xattr);
|
||||||
|
|
||||||
simple_xattr_free(old_xattr);
|
simple_xattr_free_rcu(old_xattr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +387,7 @@ static int kernfs_vfs_user_xattr_add(struct kernfs_node *kn,
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
size = old_xattr->size;
|
size = old_xattr->size;
|
||||||
simple_xattr_free(old_xattr);
|
simple_xattr_free_rcu(old_xattr);
|
||||||
dec_size_out:
|
dec_size_out:
|
||||||
atomic_sub(size, sz);
|
atomic_sub(size, sz);
|
||||||
dec_count_out:
|
dec_count_out:
|
||||||
|
|
@ -403,7 +414,7 @@ static int kernfs_vfs_user_xattr_rm(struct kernfs_node *kn,
|
||||||
|
|
||||||
atomic_sub(old_xattr->size, sz);
|
atomic_sub(old_xattr->size, sz);
|
||||||
atomic_dec(nr);
|
atomic_dec(nr);
|
||||||
simple_xattr_free(old_xattr);
|
simple_xattr_free_rcu(old_xattr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,6 +426,7 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler,
|
||||||
{
|
{
|
||||||
const char *full_name = xattr_full_name(handler, suffix);
|
const char *full_name = xattr_full_name(handler, suffix);
|
||||||
struct kernfs_node *kn = inode->i_private;
|
struct kernfs_node *kn = inode->i_private;
|
||||||
|
struct simple_xattrs *xattrs;
|
||||||
struct kernfs_iattrs *attrs;
|
struct kernfs_iattrs *attrs;
|
||||||
|
|
||||||
if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR))
|
if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR))
|
||||||
|
|
@ -424,11 +436,15 @@ static int kernfs_vfs_user_xattr_set(const struct xattr_handler *handler,
|
||||||
if (!attrs)
|
if (!attrs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags);
|
||||||
|
if (IS_ERR_OR_NULL(xattrs))
|
||||||
|
return PTR_ERR(xattrs);
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
return kernfs_vfs_user_xattr_add(kn, full_name, &attrs->xattrs,
|
return kernfs_vfs_user_xattr_add(kn, full_name, xattrs,
|
||||||
value, size, flags);
|
value, size, flags);
|
||||||
else
|
else
|
||||||
return kernfs_vfs_user_xattr_rm(kn, full_name, &attrs->xattrs,
|
return kernfs_vfs_user_xattr_rm(kn, full_name, xattrs,
|
||||||
value, size, flags);
|
value, size, flags);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ struct kernfs_iattrs {
|
||||||
struct timespec64 ia_mtime;
|
struct timespec64 ia_mtime;
|
||||||
struct timespec64 ia_ctime;
|
struct timespec64 ia_ctime;
|
||||||
|
|
||||||
struct simple_xattrs xattrs;
|
struct simple_xattrs *xattrs;
|
||||||
atomic_t nr_user_xattrs;
|
atomic_t nr_user_xattrs;
|
||||||
atomic_t user_xattr_size;
|
atomic_t user_xattr_size;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user