162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CMA SysFS Interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2021 Minchan Kim <minchan@kernel.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/cma.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "cma.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define CMA_ATTR_RO(_name) \
1562306a36Sopenharmony_ci	static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_civoid cma_sysfs_account_success_pages(struct cma *cma, unsigned long nr_pages)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	atomic64_add(nr_pages, &cma->nr_pages_succeeded);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_civoid cma_sysfs_account_fail_pages(struct cma *cma, unsigned long nr_pages)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	atomic64_add(nr_pages, &cma->nr_pages_failed);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic inline struct cma *cma_from_kobj(struct kobject *kobj)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	return container_of(kobj, struct cma_kobject, kobj)->cma;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic ssize_t alloc_pages_success_show(struct kobject *kobj,
3362306a36Sopenharmony_ci					struct kobj_attribute *attr, char *buf)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct cma *cma = cma_from_kobj(kobj);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return sysfs_emit(buf, "%llu\n",
3862306a36Sopenharmony_ci			  atomic64_read(&cma->nr_pages_succeeded));
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ciCMA_ATTR_RO(alloc_pages_success);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic ssize_t alloc_pages_fail_show(struct kobject *kobj,
4362306a36Sopenharmony_ci				     struct kobj_attribute *attr, char *buf)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct cma *cma = cma_from_kobj(kobj);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return sysfs_emit(buf, "%llu\n", atomic64_read(&cma->nr_pages_failed));
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ciCMA_ATTR_RO(alloc_pages_fail);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void cma_kobj_release(struct kobject *kobj)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct cma *cma = cma_from_kobj(kobj);
5462306a36Sopenharmony_ci	struct cma_kobject *cma_kobj = cma->cma_kobj;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	kfree(cma_kobj);
5762306a36Sopenharmony_ci	cma->cma_kobj = NULL;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct attribute *cma_attrs[] = {
6162306a36Sopenharmony_ci	&alloc_pages_success_attr.attr,
6262306a36Sopenharmony_ci	&alloc_pages_fail_attr.attr,
6362306a36Sopenharmony_ci	NULL,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ciATTRIBUTE_GROUPS(cma);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const struct kobj_type cma_ktype = {
6862306a36Sopenharmony_ci	.release = cma_kobj_release,
6962306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
7062306a36Sopenharmony_ci	.default_groups = cma_groups,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int __init cma_sysfs_init(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct kobject *cma_kobj_root;
7662306a36Sopenharmony_ci	struct cma_kobject *cma_kobj;
7762306a36Sopenharmony_ci	struct cma *cma;
7862306a36Sopenharmony_ci	int i, err;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	cma_kobj_root = kobject_create_and_add("cma", mm_kobj);
8162306a36Sopenharmony_ci	if (!cma_kobj_root)
8262306a36Sopenharmony_ci		return -ENOMEM;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	for (i = 0; i < cma_area_count; i++) {
8562306a36Sopenharmony_ci		cma_kobj = kzalloc(sizeof(*cma_kobj), GFP_KERNEL);
8662306a36Sopenharmony_ci		if (!cma_kobj) {
8762306a36Sopenharmony_ci			err = -ENOMEM;
8862306a36Sopenharmony_ci			goto out;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		cma = &cma_areas[i];
9262306a36Sopenharmony_ci		cma->cma_kobj = cma_kobj;
9362306a36Sopenharmony_ci		cma_kobj->cma = cma;
9462306a36Sopenharmony_ci		err = kobject_init_and_add(&cma_kobj->kobj, &cma_ktype,
9562306a36Sopenharmony_ci					   cma_kobj_root, "%s", cma->name);
9662306a36Sopenharmony_ci		if (err) {
9762306a36Sopenharmony_ci			kobject_put(&cma_kobj->kobj);
9862306a36Sopenharmony_ci			goto out;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ciout:
10462306a36Sopenharmony_ci	while (--i >= 0) {
10562306a36Sopenharmony_ci		cma = &cma_areas[i];
10662306a36Sopenharmony_ci		kobject_put(&cma->cma_kobj->kobj);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci	kobject_put(cma_kobj_root);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return err;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_cisubsys_initcall(cma_sysfs_init);
113