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.

mm/mempolicy: fix memory leaks in weighted interleave sysfs

Patch series "Enhance sysfs handling for memory hotplug in weighted
interleave", v9.

The following patch series enhances the weighted interleave policy in the
memory management subsystem by improving sysfs handling, fixing memory
leaks, and introducing dynamic sysfs updates for memory hotplug support.


This patch (of 3):

Memory leaks occurred when removing sysfs attributes for weighted
interleave. Improper kobject deallocation led to unreleased memory when
initialization failed or when nodes were removed.

The risk of leak is low because it only appears to trigger if setup
fails. Setup only fails due to -ENOMEM which is unlikely to happen
from a late_initcall() when memory pressure is low.

This patch resolves the issue by replacing unnecessary `kfree()` calls
with proper `kobject_del()` and `kobject_put()` sequences, ensuring
correct teardown and preventing memory leaks.

By explicitly calling `kobject_del()` before `kobject_put()`, the release
function is now invoked safely, and internal sysfs state is correctly
cleaned up. This guarantees that the memory associated with the kobject
is fully released and avoids resource leaks, thereby improving system
stability.

Additionally, sysfs_remove_file() is no longer called from the release
function to avoid accessing invalid sysfs state after kobject_del(). All
attribute removals are now done before kobject_del(), preventing WARN_ON()
in kernfs and ensuring safe and consistent cleanup of sysfs entries.

Link: https://lkml.kernel.org/r/20250417072839.711-1-rakie.kim@sk.com
Link: https://lkml.kernel.org/r/20250417072839.711-2-rakie.kim@sk.com
Fixes: dce41f5ae253 ("mm/mempolicy: implement the sysfs-based weighted_interleave interface")
Signed-off-by: Rakie Kim <rakie.kim@sk.com>
Reviewed-by: Gregory Price <gourry@gourry.net>
Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Honggyu Kim <honggyu.kim@sk.com>
Cc: "Huang, Ying" <ying.huang@linux.alibaba.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Yunjeong Mun <yunjeong.mun@sk.com>
Cc: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Rakie Kim and committed by
Andrew Morton
bb52e89d 6e14fd33

+62 -65
+62 -65
mm/mempolicy.c
··· 3471 3471 3472 3472 static struct iw_node_attr **node_attrs; 3473 3473 3474 - static void sysfs_wi_node_release(struct iw_node_attr *node_attr, 3475 - struct kobject *parent) 3474 + static void sysfs_wi_node_delete(struct iw_node_attr *node_attr, 3475 + struct kobject *parent) 3476 3476 { 3477 3477 if (!node_attr) 3478 3478 return; ··· 3481 3481 kfree(node_attr); 3482 3482 } 3483 3483 3484 - static void sysfs_wi_release(struct kobject *wi_kobj) 3484 + static void sysfs_wi_node_delete_all(struct kobject *wi_kobj) 3485 3485 { 3486 - int i; 3486 + int nid; 3487 3487 3488 - for (i = 0; i < nr_node_ids; i++) 3489 - sysfs_wi_node_release(node_attrs[i], wi_kobj); 3490 - kobject_put(wi_kobj); 3488 + for (nid = 0; nid < nr_node_ids; nid++) 3489 + sysfs_wi_node_delete(node_attrs[nid], wi_kobj); 3490 + } 3491 + 3492 + static void iw_table_free(void) 3493 + { 3494 + u8 *old; 3495 + 3496 + mutex_lock(&iw_table_lock); 3497 + old = rcu_dereference_protected(iw_table, 3498 + lockdep_is_held(&iw_table_lock)); 3499 + rcu_assign_pointer(iw_table, NULL); 3500 + mutex_unlock(&iw_table_lock); 3501 + 3502 + synchronize_rcu(); 3503 + kfree(old); 3504 + } 3505 + 3506 + static void wi_cleanup(struct kobject *wi_kobj) { 3507 + sysfs_wi_node_delete_all(wi_kobj); 3508 + iw_table_free(); 3509 + kfree(node_attrs); 3510 + } 3511 + 3512 + static void wi_kobj_release(struct kobject *wi_kobj) 3513 + { 3514 + kfree(wi_kobj); 3491 3515 } 3492 3516 3493 3517 static const struct kobj_type wi_ktype = { 3494 3518 .sysfs_ops = &kobj_sysfs_ops, 3495 - .release = sysfs_wi_release, 3519 + .release = wi_kobj_release, 3496 3520 }; 3497 3521 3498 3522 static int add_weight_node(int nid, struct kobject *wi_kobj) ··· 3557 3533 struct kobject *wi_kobj; 3558 3534 int nid, err; 3559 3535 3560 - wi_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); 3561 - if (!wi_kobj) 3536 + node_attrs = kcalloc(nr_node_ids, sizeof(struct iw_node_attr *), 3537 + GFP_KERNEL); 3538 + if (!node_attrs) 3562 3539 return -ENOMEM; 3540 + 3541 + wi_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); 3542 + if (!wi_kobj) { 3543 + kfree(node_attrs); 3544 + return -ENOMEM; 3545 + } 3563 3546 3564 3547 err = kobject_init_and_add(wi_kobj, &wi_ktype, root_kobj, 3565 3548 "weighted_interleave"); 3566 - if (err) { 3567 - kfree(wi_kobj); 3568 - return err; 3569 - } 3549 + if (err) 3550 + goto err_put_kobj; 3570 3551 3571 3552 for_each_node_state(nid, N_POSSIBLE) { 3572 3553 err = add_weight_node(nid, wi_kobj); 3573 3554 if (err) { 3574 3555 pr_err("failed to add sysfs [node%d]\n", nid); 3575 - break; 3556 + goto err_cleanup_kobj; 3576 3557 } 3577 3558 } 3578 - if (err) 3579 - kobject_put(wi_kobj); 3559 + 3580 3560 return 0; 3561 + 3562 + err_cleanup_kobj: 3563 + wi_cleanup(wi_kobj); 3564 + kobject_del(wi_kobj); 3565 + err_put_kobj: 3566 + kobject_put(wi_kobj); 3567 + return err; 3581 3568 } 3582 - 3583 - static void mempolicy_kobj_release(struct kobject *kobj) 3584 - { 3585 - u8 *old; 3586 - 3587 - mutex_lock(&iw_table_lock); 3588 - old = rcu_dereference_protected(iw_table, 3589 - lockdep_is_held(&iw_table_lock)); 3590 - rcu_assign_pointer(iw_table, NULL); 3591 - mutex_unlock(&iw_table_lock); 3592 - synchronize_rcu(); 3593 - kfree(old); 3594 - kfree(node_attrs); 3595 - kfree(kobj); 3596 - } 3597 - 3598 - static const struct kobj_type mempolicy_ktype = { 3599 - .release = mempolicy_kobj_release 3600 - }; 3601 3569 3602 3570 static int __init mempolicy_sysfs_init(void) 3603 3571 { 3604 3572 int err; 3605 3573 static struct kobject *mempolicy_kobj; 3606 3574 3607 - mempolicy_kobj = kzalloc(sizeof(*mempolicy_kobj), GFP_KERNEL); 3608 - if (!mempolicy_kobj) { 3609 - err = -ENOMEM; 3610 - goto err_out; 3611 - } 3612 - 3613 - node_attrs = kcalloc(nr_node_ids, sizeof(struct iw_node_attr *), 3614 - GFP_KERNEL); 3615 - if (!node_attrs) { 3616 - err = -ENOMEM; 3617 - goto mempol_out; 3618 - } 3619 - 3620 - err = kobject_init_and_add(mempolicy_kobj, &mempolicy_ktype, mm_kobj, 3621 - "mempolicy"); 3622 - if (err) 3623 - goto node_out; 3575 + mempolicy_kobj = kobject_create_and_add("mempolicy", mm_kobj); 3576 + if (!mempolicy_kobj) 3577 + return -ENOMEM; 3624 3578 3625 3579 err = add_weighted_interleave_group(mempolicy_kobj); 3626 - if (err) { 3627 - pr_err("mempolicy sysfs structure failed to initialize\n"); 3628 - kobject_put(mempolicy_kobj); 3629 - return err; 3630 - } 3580 + if (err) 3581 + goto err_kobj; 3631 3582 3632 - return err; 3633 - node_out: 3634 - kfree(node_attrs); 3635 - mempol_out: 3636 - kfree(mempolicy_kobj); 3637 - err_out: 3638 - pr_err("failed to add mempolicy kobject to the system\n"); 3583 + return 0; 3584 + 3585 + err_kobj: 3586 + kobject_del(mempolicy_kobj); 3587 + kobject_put(mempolicy_kobj); 3639 3588 return err; 3640 3589 } 3641 3590