[RFC PATCH v3 09/37] mm/damon/sysfs: implement sample/primitives/ dir

SeongJae Park
2025-12-08T06:30:02Z

Add a sysfs directory that will be used to let DAMON ABI users control
what access sampling primitives to use, among page table Accessed bit,
and page fault events.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
mm/damon/sysfs.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 132 insertions(+)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 851e8c0dc989..14478fd79953 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -749,12 +749,111 @@ static const struct kobj_type damon_sysfs_intervals_ktype = {
.default_groups = damon_sysfs_intervals_groups,
};

+/*
+ * access check primitives directory
+ */
+
+struct damon_sysfs_primitives {
+ struct kobject kobj;
+ bool page_table;
+ bool page_fault;
+};
+
+static struct damon_sysfs_primitives *damon_sysfs_primitives_alloc(
+ bool page_table, bool page_fault)
+{
+ struct damon_sysfs_primitives *primitives = kmalloc(
+ sizeof(*primitives), GFP_KERNEL);
+
+ if (!primitives)
+ return NULL;
+
+ primitives->kobj = (struct kobject){};
+ primitives->page_table = page_table;
+ primitives->page_fault = page_fault;
+ return primitives;
+}
+
+static ssize_t page_table_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct damon_sysfs_primitives *primitives = container_of(kobj,
+ struct damon_sysfs_primitives, kobj);
+
+ return sysfs_emit(buf, "%c\n", primitives->page_table ? 'Y' : 'N');
+}
+
+static ssize_t page_table_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct damon_sysfs_primitives *primitives = container_of(kobj,
+ struct damon_sysfs_primitives, kobj);
+ bool enable;
+ int err = kstrtobool(buf, &enable);
+
+ if (err)
+ return err;
+ primitives->page_table = enable;
+ return count;
+}
+
+static ssize_t page_fault_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct damon_sysfs_primitives *primitives = container_of(kobj,
+ struct damon_sysfs_primitives, kobj);
+
+ return sysfs_emit(buf, "%c\n", primitives->page_fault ? 'Y' : 'N');
+}
+
+static ssize_t page_fault_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct damon_sysfs_primitives *primitives = container_of(kobj,
+ struct damon_sysfs_primitives, kobj);
+ bool enable;
+ int err = kstrtobool(buf, &enable);
+
+ if (err)
+ return err;
+ primitives->page_fault = enable;
+ return count;
+}
+
+static void damon_sysfs_primitives_release(struct kobject *kobj)
+{
+ struct damon_sysfs_primitives *primitives = container_of(kobj,
+ struct damon_sysfs_primitives, kobj);
+
+ kfree(primitives);
+}
+
+static struct kobj_attribute damon_sysfs_primitives_page_table_attr =
+ __ATTR_RW_MODE(page_table, 0600);
+
+static struct kobj_attribute damon_sysfs_primitives_page_fault_attr =
+ __ATTR_RW_MODE(page_fault, 0600);
+
+static struct attribute *damon_sysfs_primitives_attrs[] = {
+ &damon_sysfs_primitives_page_table_attr.attr,
+ &damon_sysfs_primitives_page_fault_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_primitives);
+
+static const struct kobj_type damon_sysfs_primitives_ktype = {
+ .release = damon_sysfs_primitives_release,
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_groups = damon_sysfs_primitives_groups,
+};
+
/*
* sample directory
*/

struct damon_sysfs_sample {
struct kobject kobj;
+ struct damon_sysfs_primitives *primitives;
};

static struct damon_sysfs_sample *damon_sysfs_sample_alloc(void)
@@ -768,6 +867,35 @@ static struct damon_sysfs_sample *damon_sysfs_sample_alloc(void)
return sample;
}

+static int damon_sysfs_sample_add_dirs(
+ struct damon_sysfs_sample *sample)
+{
+ struct damon_sysfs_primitives *primitives;
+ int err;
+
+ primitives = damon_sysfs_primitives_alloc(true, false);
+ if (!primitives)
+ return -ENOMEM;
+ err = kobject_init_and_add(&primitives->kobj,
+ &damon_sysfs_primitives_ktype, &sample->kobj,
+ "primitives");
+ if (err)
+ goto put_primitives_out;
+ sample->primitives = primitives;
+
+put_primitives_out:
+ kobject_put(&primitives->kobj);
+ sample->primitives = NULL;
+ return err;
+}
+
+static void damon_sysfs_sample_rm_dirs(
+ struct damon_sysfs_sample *sample)
+{
+ if (sample->primitives)
+ kobject_put(&sample->primitives->kobj);
+}
+
static void damon_sysfs_sample_release(struct kobject *kobj)
{
kfree(container_of(kobj, struct damon_sysfs_sample, kobj));
@@ -847,6 +975,9 @@ static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
err = kobject_init_and_add(&sample->kobj,
&damon_sysfs_sample_ktype, &attrs->kobj,
"sample");
+ if (err)
+ goto put_sample_out;
+ err = damon_sysfs_sample_add_dirs(sample);
if (err)
goto put_sample_out;
attrs->sample = sample;
@@ -869,6 +1000,7 @@ static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
kobject_put(&attrs->nr_regions_range->kobj);
damon_sysfs_intervals_rm_dirs(attrs->intervals);
kobject_put(&attrs->intervals->kobj);
+ damon_sysfs_sample_rm_dirs(attrs->sample);
kobject_put(&attrs->sample->kobj);
}

--
2.47.3