mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-30 22:03:17 +01:00
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/[email protected]
Link: https://lore.kernel.org/[email protected]
Fixes: 43ca106fa8 ("mm: cma: support sysfs")
Signed-off-by: Kaitao Cheng <[email protected]>
Reported-by: David Hildenbrand (Arm) <[email protected]>
Suggested-by: David Hildenbrand (Arm) <[email protected]>
Suggested-by: Muchun Song <[email protected]>
Reported-by: Muchun Song <[email protected]>
Closes: https://lore.kernel.org/linux-mm/[email protected]/
Acked-by: Muchun Song <[email protected]>
Cc: David Hildenbrand <[email protected]>
Cc: Dmitry Osipenko <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: John Hubbard <[email protected]>
Cc: Liam R. Howlett <[email protected]>
Cc: Lorenzo Stoakes <[email protected]>
Cc: Michal Hocko <[email protected]>
Cc: Mike Rapoport <[email protected]>
Cc: Minchan Kim <[email protected]>
Cc: Suren Baghdasaryan <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
152 lines
3.4 KiB
C
152 lines
3.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* CMA SysFS Interface
|
|
*
|
|
* Copyright (c) 2021 Minchan Kim <[email protected]>
|
|
*/
|
|
|
|
#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);
|