cma_activate_area() can fail after a CMA area has already been added to
cma_areas[]. In that case the area is left in the global array, but it
does not reach the point where CMA_ACTIVATED is set.
cma_sysfs_init() currently walks all cma_area_count entries and creates
sysfs files for every area, including ones that failed activation. These
areas are not usable CMA areas and should not be exposed to userspace as
valid CMA regions.
If such an inactive area is exposed, userspace sees a CMA directory whose
read-only accounting files report zeros. total_pages and available_pages
report zero because the failed activation path clears cma->count and
cma->available_count, while the allocation and release counters also stay
at zero because the area cannot service CMA allocations. This makes the
failed area look like a valid but empty CMA region and can mislead tests,
monitoring, and diagnostics.
Skip CMA areas that did not reach CMA_ACTIVATED when creating the sysfs
objects. Since inactive entries can now be skipped, make the error unwind
tolerate entries that never had cma_kobj initialized.
Link: https://lore.kernel.org/20260524140420.61864-1-kaitao.cheng@linux.dev
Link: https://lore.kernel.org/20260522131434.78532-1-kaitao.cheng@linux.dev
Fixes: 43ca106fa8 ("mm: cma: support sysfs")
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
Reported-by: David Hildenbrand (Arm) <david@kernel.org>
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Suggested-by: Muchun Song <songmuchun@bytedance.com>
Reported-by: Muchun Song <songmuchun@bytedance.com>
Closes: https://lore.kernel.org/linux-mm/55481a8b-dcfc-4bef-ba59-aa0b43dca88b@kernel.org/
Acked-by: Muchun Song <muchun.song@linux.dev>
Cc: David Hildenbrand <david@kernel.org>
Cc: Dmitry Osipenko <digetx@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
151 lines
3.4 KiB
C
151 lines
3.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* CMA SysFS Interface
|
|
*
|
|
* Copyright (c) 2021 Minchan Kim <minchan@kernel.org>
|
|
*/
|
|
|
|
#include <linux/cma.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "cma.h"
|
|
|
|
#define CMA_ATTR_RO(_name) \
|
|
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
|
|
|
|
void cma_sysfs_account_success_pages(struct cma *cma, unsigned long nr_pages)
|
|
{
|
|
atomic64_add(nr_pages, &cma->nr_pages_succeeded);
|
|
}
|
|
|
|
void cma_sysfs_account_fail_pages(struct cma *cma, unsigned long nr_pages)
|
|
{
|
|
atomic64_add(nr_pages, &cma->nr_pages_failed);
|
|
}
|
|
|
|
void cma_sysfs_account_release_pages(struct cma *cma, unsigned long nr_pages)
|
|
{
|
|
atomic64_add(nr_pages, &cma->nr_pages_released);
|
|
}
|
|
|
|
static inline struct cma *cma_from_kobj(struct kobject *kobj)
|
|
{
|
|
return container_of(kobj, struct cma_kobject, kobj)->cma;
|
|
}
|
|
|
|
static ssize_t alloc_pages_success_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
atomic64_read(&cma->nr_pages_succeeded));
|
|
}
|
|
CMA_ATTR_RO(alloc_pages_success);
|
|
|
|
static ssize_t alloc_pages_fail_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
|
|
return sysfs_emit(buf, "%llu\n", atomic64_read(&cma->nr_pages_failed));
|
|
}
|
|
CMA_ATTR_RO(alloc_pages_fail);
|
|
|
|
static ssize_t release_pages_success_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
|
|
return sysfs_emit(buf, "%llu\n", atomic64_read(&cma->nr_pages_released));
|
|
}
|
|
CMA_ATTR_RO(release_pages_success);
|
|
|
|
static ssize_t total_pages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
|
|
return sysfs_emit(buf, "%lu\n", cma->count);
|
|
}
|
|
CMA_ATTR_RO(total_pages);
|
|
|
|
static ssize_t available_pages_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
|
|
return sysfs_emit(buf, "%lu\n", cma->available_count);
|
|
}
|
|
CMA_ATTR_RO(available_pages);
|
|
|
|
static void cma_kobj_release(struct kobject *kobj)
|
|
{
|
|
struct cma *cma = cma_from_kobj(kobj);
|
|
struct cma_kobject *cma_kobj = cma->cma_kobj;
|
|
|
|
kfree(cma_kobj);
|
|
cma->cma_kobj = NULL;
|
|
}
|
|
|
|
static struct attribute *cma_attrs[] = {
|
|
&alloc_pages_success_attr.attr,
|
|
&alloc_pages_fail_attr.attr,
|
|
&release_pages_success_attr.attr,
|
|
&total_pages_attr.attr,
|
|
&available_pages_attr.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(cma);
|
|
|
|
static const struct kobj_type cma_ktype = {
|
|
.release = cma_kobj_release,
|
|
.sysfs_ops = &kobj_sysfs_ops,
|
|
.default_groups = cma_groups,
|
|
};
|
|
|
|
static int __init cma_sysfs_init(void)
|
|
{
|
|
struct kobject *cma_kobj_root;
|
|
struct cma_kobject *cma_kobj;
|
|
struct cma *cma;
|
|
int i, err;
|
|
|
|
cma_kobj_root = kobject_create_and_add("cma", mm_kobj);
|
|
if (!cma_kobj_root)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < cma_area_count; i++) {
|
|
cma = &cma_areas[i];
|
|
if (!test_bit(CMA_ACTIVATED, &cma->flags))
|
|
continue;
|
|
|
|
cma_kobj = kzalloc_obj(*cma_kobj);
|
|
if (!cma_kobj) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
cma->cma_kobj = cma_kobj;
|
|
cma_kobj->cma = cma;
|
|
err = kobject_init_and_add(&cma_kobj->kobj, &cma_ktype,
|
|
cma_kobj_root, "%s", cma->name);
|
|
if (err) {
|
|
kobject_put(&cma_kobj->kobj);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
out:
|
|
while (--i >= 0) {
|
|
cma = &cma_areas[i];
|
|
if (cma->cma_kobj)
|
|
kobject_put(&cma->cma_kobj->kobj);
|
|
}
|
|
kobject_put(cma_kobj_root);
|
|
|
|
return err;
|
|
}
|
|
subsys_initcall(cma_sysfs_init);
|