Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+37 -14
+11 -4
fs/kernfs/dir.c
··· 547 547 /* If the whole node goes away, then name can't be used outside */ 548 548 kfree_const(rcu_access_pointer(kn->name)); 549 549 550 - if (kn->iattr) { 551 - simple_xattrs_free(&kn->iattr->xattrs, NULL); 550 + if (kn->iattr) 552 551 kmem_cache_free(kernfs_iattrs_cache, kn->iattr); 553 - } 554 552 555 553 kmem_cache_free(kernfs_node_cache, kn); 556 554 } ··· 581 583 582 584 if (kernfs_type(kn) == KERNFS_LINK) 583 585 kernfs_put(kn->symlink.target_kn); 586 + 587 + if (kn->iattr && kn->iattr->xattrs) { 588 + simple_xattrs_free(kn->iattr->xattrs, NULL); 589 + kfree(kn->iattr->xattrs); 590 + kn->iattr->xattrs = NULL; 591 + } 584 592 585 593 spin_lock(&root->kernfs_idr_lock); 586 594 idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); ··· 686 682 687 683 err_out4: 688 684 if (kn->iattr) { 689 - simple_xattrs_free(&kn->iattr->xattrs, NULL); 685 + if (kn->iattr->xattrs) { 686 + simple_xattrs_free(kn->iattr->xattrs, NULL); 687 + kfree(kn->iattr->xattrs); 688 + } 690 689 kmem_cache_free(kernfs_iattrs_cache, kn->iattr); 691 690 } 692 691 err_out3:
+25 -9
fs/kernfs/inode.c
··· 45 45 ret->ia_mtime = ret->ia_atime; 46 46 ret->ia_ctime = ret->ia_atime; 47 47 48 - simple_xattrs_init(&ret->xattrs); 49 48 atomic_set(&ret->nr_user_xattrs, 0); 50 49 atomic_set(&ret->user_xattr_size, 0); 51 50 ··· 145 146 if (!attrs) 146 147 return -ENOMEM; 147 148 148 - return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); 149 + return simple_xattr_list(d_inode(dentry), READ_ONCE(attrs->xattrs), 150 + buf, size); 149 151 } 150 152 151 153 static inline void set_default_inode_attr(struct inode *inode, umode_t mode) ··· 298 298 void *value, size_t size) 299 299 { 300 300 struct kernfs_iattrs *attrs = kernfs_iattrs_noalloc(kn); 301 + struct simple_xattrs *xattrs; 302 + 301 303 if (!attrs) 302 304 return -ENODATA; 303 305 304 - return simple_xattr_get(&attrs->xattrs, name, value, size); 306 + xattrs = READ_ONCE(attrs->xattrs); 307 + if (!xattrs) 308 + return -ENODATA; 309 + 310 + return simple_xattr_get(xattrs, name, value, size); 305 311 } 306 312 307 313 int kernfs_xattr_set(struct kernfs_node *kn, const char *name, 308 314 const void *value, size_t size, int flags) 309 315 { 310 316 struct simple_xattr *old_xattr; 317 + struct simple_xattrs *xattrs; 311 318 struct kernfs_iattrs *attrs; 312 319 313 320 attrs = kernfs_iattrs(kn); 314 321 if (!attrs) 315 322 return -ENOMEM; 316 323 317 - old_xattr = simple_xattr_set(&attrs->xattrs, name, value, size, flags); 324 + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); 325 + if (IS_ERR_OR_NULL(xattrs)) 326 + return PTR_ERR(xattrs); 327 + 328 + old_xattr = simple_xattr_set(xattrs, name, value, size, flags); 318 329 if (IS_ERR(old_xattr)) 319 330 return PTR_ERR(old_xattr); 320 331 321 - simple_xattr_free(old_xattr); 332 + simple_xattr_free_rcu(old_xattr); 322 333 return 0; 323 334 } 324 335 ··· 387 376 388 377 ret = 0; 389 378 size = old_xattr->size; 390 - simple_xattr_free(old_xattr); 379 + simple_xattr_free_rcu(old_xattr); 391 380 dec_size_out: 392 381 atomic_sub(size, sz); 393 382 dec_count_out: ··· 414 403 415 404 atomic_sub(old_xattr->size, sz); 416 405 atomic_dec(nr); 417 - simple_xattr_free(old_xattr); 406 + simple_xattr_free_rcu(old_xattr); 418 407 return 0; 419 408 } 420 409 ··· 426 415 { 427 416 const char *full_name = xattr_full_name(handler, suffix); 428 417 struct kernfs_node *kn = inode->i_private; 418 + struct simple_xattrs *xattrs; 429 419 struct kernfs_iattrs *attrs; 430 420 431 421 if (!(kernfs_root(kn)->flags & KERNFS_ROOT_SUPPORT_USER_XATTR)) ··· 436 424 if (!attrs) 437 425 return -ENOMEM; 438 426 427 + xattrs = simple_xattrs_lazy_alloc(&attrs->xattrs, value, flags); 428 + if (IS_ERR_OR_NULL(xattrs)) 429 + return PTR_ERR(xattrs); 430 + 439 431 if (value) 440 - return kernfs_vfs_user_xattr_add(kn, full_name, &attrs->xattrs, 432 + return kernfs_vfs_user_xattr_add(kn, full_name, xattrs, 441 433 value, size, flags); 442 434 else 443 - return kernfs_vfs_user_xattr_rm(kn, full_name, &attrs->xattrs, 435 + return kernfs_vfs_user_xattr_rm(kn, full_name, xattrs, 444 436 value, size, flags); 445 437 446 438 }
+1 -1
fs/kernfs/kernfs-internal.h
··· 26 26 struct timespec64 ia_mtime; 27 27 struct timespec64 ia_ctime; 28 28 29 - struct simple_xattrs xattrs; 29 + struct simple_xattrs *xattrs; 30 30 atomic_t nr_user_xattrs; 31 31 atomic_t user_xattr_size; 32 32 };