| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Common Code for DAMON Sysfs Interface |
| */ |
| |
| #include <linux/slab.h> |
| |
| #include "sysfs-common.h" |
| |
| DEFINE_MUTEX(damon_sysfs_lock); |
| |
| /* |
| * unsigned long range directory |
| */ |
| |
| struct damon_sysfs_ul_range *damon_sysfs_ul_range_alloc( |
| unsigned long min, |
| unsigned long max) |
| { |
| struct damon_sysfs_ul_range *range = kmalloc_obj(*range); |
| |
| if (!range) |
| return NULL; |
| range->kobj = (struct kobject){}; |
| range->min = min; |
| range->max = max; |
| |
| return range; |
| } |
| |
| static ssize_t min_show(struct kobject *kobj, struct kobj_attribute *attr, |
| char *buf) |
| { |
| struct damon_sysfs_ul_range *range = container_of(kobj, |
| struct damon_sysfs_ul_range, kobj); |
| |
| return sysfs_emit(buf, "%lu\n", range->min); |
| } |
| |
| static ssize_t min_store(struct kobject *kobj, struct kobj_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct damon_sysfs_ul_range *range = container_of(kobj, |
| struct damon_sysfs_ul_range, kobj); |
| unsigned long min; |
| int err; |
| |
| err = kstrtoul(buf, 0, &min); |
| if (err) |
| return err; |
| |
| range->min = min; |
| return count; |
| } |
| |
| static ssize_t max_show(struct kobject *kobj, struct kobj_attribute *attr, |
| char *buf) |
| { |
| struct damon_sysfs_ul_range *range = container_of(kobj, |
| struct damon_sysfs_ul_range, kobj); |
| |
| return sysfs_emit(buf, "%lu\n", range->max); |
| } |
| |
| static ssize_t max_store(struct kobject *kobj, struct kobj_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct damon_sysfs_ul_range *range = container_of(kobj, |
| struct damon_sysfs_ul_range, kobj); |
| unsigned long max; |
| int err; |
| |
| err = kstrtoul(buf, 0, &max); |
| if (err) |
| return err; |
| |
| range->max = max; |
| return count; |
| } |
| |
| void damon_sysfs_ul_range_release(struct kobject *kobj) |
| { |
| kfree(container_of(kobj, struct damon_sysfs_ul_range, kobj)); |
| } |
| |
| static struct kobj_attribute damon_sysfs_ul_range_min_attr = |
| __ATTR_RW_MODE(min, 0600); |
| |
| static struct kobj_attribute damon_sysfs_ul_range_max_attr = |
| __ATTR_RW_MODE(max, 0600); |
| |
| static struct attribute *damon_sysfs_ul_range_attrs[] = { |
| &damon_sysfs_ul_range_min_attr.attr, |
| &damon_sysfs_ul_range_max_attr.attr, |
| NULL, |
| }; |
| ATTRIBUTE_GROUPS(damon_sysfs_ul_range); |
| |
| const struct kobj_type damon_sysfs_ul_range_ktype = { |
| .release = damon_sysfs_ul_range_release, |
| .sysfs_ops = &kobj_sysfs_ops, |
| .default_groups = damon_sysfs_ul_range_groups, |
| }; |
| |
| |
| static bool damon_sysfs_memcg_path_eq(struct mem_cgroup *memcg, |
| char *memcg_path_buf, char *path) |
| { |
| #ifdef CONFIG_MEMCG |
| cgroup_path(memcg->css.cgroup, memcg_path_buf, PATH_MAX); |
| if (sysfs_streq(memcg_path_buf, path)) |
| return true; |
| #endif /* CONFIG_MEMCG */ |
| return false; |
| } |
| |
| int damon_sysfs_memcg_path_to_id(char *memcg_path, u64 *id) |
| { |
| struct mem_cgroup *memcg; |
| char *path; |
| bool found = false; |
| |
| if (!memcg_path) |
| return -EINVAL; |
| |
| path = kmalloc_array(PATH_MAX, sizeof(*path), GFP_KERNEL); |
| if (!path) |
| return -ENOMEM; |
| |
| for (memcg = mem_cgroup_iter(NULL, NULL, NULL); memcg; |
| memcg = mem_cgroup_iter(NULL, memcg, NULL)) { |
| /* skip offlined memcg */ |
| if (!mem_cgroup_online(memcg)) |
| continue; |
| if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) { |
| *id = mem_cgroup_id(memcg); |
| found = true; |
| mem_cgroup_iter_break(NULL, memcg); |
| break; |
| } |
| } |
| |
| kfree(path); |
| return found ? 0 : -EINVAL; |
| } |