mm/damon/sysfs-schemes: implement scheme filters
authorSeongJae Park <sj@kernel.org>
Mon, 5 Dec 2022 23:08:27 +0000 (23:08 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 19 Jan 2023 01:12:44 +0000 (17:12 -0800)
Implement scheme filters functionality of DAMON sysfs interface by making
the code reads the values of files under the filter directories and pass
that to DAMON using DAMON kernel API.

[sj@kernel.org: fix leaking a filter for wrong cgroup path]
Link: https://lkml.kernel.org/r/20221219171807.55708-2-sj@kernel.org
[sj@kernel.org: return an error for filter memcg path id lookup failure]
Link: https://lkml.kernel.org/r/20221219171807.55708-3-sj@kernel.org
Link: https://lkml.kernel.org/r/20221205230830.144349-9-sj@kernel.org
Signed-off-by: SeongJae Park <sj@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/damon/sysfs-schemes.c

index e79c678..f0dabe3 100644 (file)
@@ -1403,6 +1403,79 @@ struct kobj_type damon_sysfs_schemes_ktype = {
        .default_groups = damon_sysfs_schemes_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;
+}
+
+static int damon_sysfs_memcg_path_to_id(char *memcg_path, unsigned short *id)
+{
+       struct mem_cgroup *memcg;
+       char *path;
+       bool found = false;
+
+       if (!memcg_path)
+               return -EINVAL;
+
+       path = kmalloc(sizeof(*path) * PATH_MAX, GFP_KERNEL);
+       if (!path)
+               return -ENOMEM;
+
+       for (memcg = mem_cgroup_iter(NULL, NULL, NULL); memcg;
+                       memcg = mem_cgroup_iter(NULL, memcg, NULL)) {
+               /* skip removed memcg */
+               if (!mem_cgroup_id(memcg))
+                       continue;
+               if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) {
+                       *id = mem_cgroup_id(memcg);
+                       found = true;
+                       break;
+               }
+       }
+
+       kfree(path);
+       return found ? 0 : -EINVAL;
+}
+
+static int damon_sysfs_set_scheme_filters(struct damos *scheme,
+               struct damon_sysfs_scheme_filters *sysfs_filters)
+{
+       int i;
+       struct damos_filter *filter, *next;
+
+       damos_for_each_filter_safe(filter, next, scheme)
+               damos_destroy_filter(filter);
+
+       for (i = 0; i < sysfs_filters->nr; i++) {
+               struct damon_sysfs_scheme_filter *sysfs_filter =
+                       sysfs_filters->filters_arr[i];
+               struct damos_filter *filter =
+                       damos_new_filter(sysfs_filter->type,
+                                       sysfs_filter->matching);
+               int err;
+
+               if (!filter)
+                       return -ENOMEM;
+               if (filter->type == DAMOS_FILTER_TYPE_MEMCG) {
+                       err = damon_sysfs_memcg_path_to_id(
+                                       sysfs_filter->memcg_path,
+                                       &filter->memcg_id);
+                       if (err) {
+                               damos_destroy_filter(filter);
+                               return err;
+                       }
+               }
+               damos_add_filter(scheme, filter);
+       }
+       return 0;
+}
+
 static struct damos *damon_sysfs_mk_scheme(
                struct damon_sysfs_scheme *sysfs_scheme)
 {
@@ -1411,6 +1484,10 @@ static struct damos *damon_sysfs_mk_scheme(
        struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
        struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights;
        struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks;
+       struct damon_sysfs_scheme_filters *sysfs_filters =
+               sysfs_scheme->filters;
+       struct damos *scheme;
+       int err;
 
        struct damos_access_pattern pattern = {
                .min_sz_region = access_pattern->sz->min,
@@ -1436,8 +1513,17 @@ static struct damos *damon_sysfs_mk_scheme(
                .low = sysfs_wmarks->low,
        };
 
-       return damon_new_scheme(&pattern, sysfs_scheme->action, &quota,
+       scheme = damon_new_scheme(&pattern, sysfs_scheme->action, &quota,
                        &wmarks);
+       if (!scheme)
+               return NULL;
+
+       err = damon_sysfs_set_scheme_filters(scheme, sysfs_filters);
+       if (err) {
+               damon_destroy_scheme(scheme);
+               return NULL;
+       }
+       return scheme;
 }
 
 static void damon_sysfs_update_scheme(struct damos *scheme,
@@ -1448,6 +1534,7 @@ static void damon_sysfs_update_scheme(struct damos *scheme,
        struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
        struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights;
        struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks;
+       int err;
 
        scheme->pattern.min_sz_region = access_pattern->sz->min;
        scheme->pattern.max_sz_region = access_pattern->sz->max;
@@ -1470,6 +1557,10 @@ static void damon_sysfs_update_scheme(struct damos *scheme,
        scheme->wmarks.high = sysfs_wmarks->high;
        scheme->wmarks.mid = sysfs_wmarks->mid;
        scheme->wmarks.low = sysfs_wmarks->low;
+
+       err = damon_sysfs_set_scheme_filters(scheme, sysfs_scheme->filters);
+       if (err)
+               damon_destroy_scheme(scheme);
 }
 
 int damon_sysfs_set_schemes(struct damon_ctx *ctx,