summaryrefslogtreecommitdiff
path: root/mm/damon/sysfs-schemes.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/damon/sysfs-schemes.c')
-rw-r--r--mm/damon/sysfs-schemes.c146
1 files changed, 113 insertions, 33 deletions
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index ae0f0b314f3a..53a90ac678fb 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -127,17 +127,17 @@ static const struct kobj_type damon_sysfs_scheme_region_ktype = {
*
* Once the tried regions update request is received, the request handling
* start function (damon_sysfs_scheme_update_regions_start()) sets the status
- * of all schemes as 'idle' again, and register ->before_damos_apply() and
- * ->after_sampling() callbacks.
+ * of all schemes as 'idle' again, and register ->before_damos_apply()
+ * callback.
*
* Then, the first followup ->before_damos_apply() callback
* (damon_sysfs_before_damos_apply()) sets the status 'started'. The first
- * ->after_sampling() callback (damon_sysfs_after_sampling()) after the call
- * is called only after the scheme is completely applied
- * to the given snapshot. Hence the callback knows the situation by showing
- * 'started' status, and sets the status as 'finished'. Then,
- * damon_sysfs_before_damos_apply() understands the situation by showing the
- * 'finished' status and do nothing.
+ * ->after_sampling() or ->after_aggregation() callback
+ * (damon_sysfs_cmd_request_callback()) after the call is called only after
+ * the scheme is completely applied to the given snapshot. Hence the callback
+ * knows the situation by showing 'started' status, and sets the status as
+ * 'finished'. Then, damon_sysfs_before_damos_apply() understands the
+ * situation by showing the 'finished' status and do nothing.
*
* If DAMOS is not applied to any region due to any reasons including the
* access pattern, the watermarks, the quotas, and the filters,
@@ -826,15 +826,48 @@ static const struct kobj_type damon_sysfs_watermarks_ktype = {
struct damos_sysfs_quota_goal {
struct kobject kobj;
+ enum damos_quota_goal_metric metric;
unsigned long target_value;
unsigned long current_value;
};
+/* This should match with enum damos_action */
+static const char * const damos_sysfs_quota_goal_metric_strs[] = {
+ "user_input",
+ "some_mem_psi_us",
+};
+
static struct damos_sysfs_quota_goal *damos_sysfs_quota_goal_alloc(void)
{
return kzalloc(sizeof(struct damos_sysfs_quota_goal), GFP_KERNEL);
}
+static ssize_t target_metric_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct damos_sysfs_quota_goal *goal = container_of(kobj,
+ struct damos_sysfs_quota_goal, kobj);
+
+ return sysfs_emit(buf, "%s\n",
+ damos_sysfs_quota_goal_metric_strs[goal->metric]);
+}
+
+static ssize_t target_metric_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct damos_sysfs_quota_goal *goal = container_of(kobj,
+ struct damos_sysfs_quota_goal, kobj);
+ enum damos_quota_goal_metric m;
+
+ for (m = 0; m < NR_DAMOS_QUOTA_GOAL_METRICS; m++) {
+ if (sysfs_streq(buf, damos_sysfs_quota_goal_metric_strs[m])) {
+ goal->metric = m;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+
static ssize_t target_value_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -880,6 +913,9 @@ static void damos_sysfs_quota_goal_release(struct kobject *kobj)
kfree(container_of(kobj, struct damos_sysfs_quota_goal, kobj));
}
+static struct kobj_attribute damos_sysfs_quota_goal_target_metric_attr =
+ __ATTR_RW_MODE(target_metric, 0600);
+
static struct kobj_attribute damos_sysfs_quota_goal_target_value_attr =
__ATTR_RW_MODE(target_value, 0600);
@@ -887,6 +923,7 @@ static struct kobj_attribute damos_sysfs_quota_goal_current_value_attr =
__ATTR_RW_MODE(current_value, 0600);
static struct attribute *damos_sysfs_quota_goal_attrs[] = {
+ &damos_sysfs_quota_goal_target_metric_attr.attr,
&damos_sysfs_quota_goal_target_value_attr.attr,
&damos_sysfs_quota_goal_current_value_attr.attr,
NULL,
@@ -1139,6 +1176,7 @@ struct damon_sysfs_quotas {
unsigned long ms;
unsigned long sz;
unsigned long reset_interval_ms;
+ unsigned long effective_sz; /* Effective size quota in bytes */
};
static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void)
@@ -1252,6 +1290,15 @@ static ssize_t reset_interval_ms_store(struct kobject *kobj,
return count;
}
+static ssize_t effective_bytes_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct damon_sysfs_quotas *quotas = container_of(kobj,
+ struct damon_sysfs_quotas, kobj);
+
+ return sysfs_emit(buf, "%lu\n", quotas->effective_sz);
+}
+
static void damon_sysfs_quotas_release(struct kobject *kobj)
{
kfree(container_of(kobj, struct damon_sysfs_quotas, kobj));
@@ -1266,10 +1313,14 @@ static struct kobj_attribute damon_sysfs_quotas_sz_attr =
static struct kobj_attribute damon_sysfs_quotas_reset_interval_ms_attr =
__ATTR_RW_MODE(reset_interval_ms, 0600);
+static struct kobj_attribute damon_sysfs_quotas_effective_bytes_attr =
+ __ATTR_RO_MODE(effective_bytes, 0400);
+
static struct attribute *damon_sysfs_quotas_attrs[] = {
&damon_sysfs_quotas_ms_attr.attr,
&damon_sysfs_quotas_sz_attr.attr,
&damon_sysfs_quotas_reset_interval_ms_attr.attr,
+ &damon_sysfs_quotas_effective_bytes_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(damon_sysfs_quotas);
@@ -1868,35 +1919,35 @@ static int damon_sysfs_set_scheme_filters(struct damos *scheme,
return 0;
}
-static unsigned long damos_sysfs_get_quota_score(void *arg)
-{
- return (unsigned long)arg;
-}
-
-static void damos_sysfs_set_quota_score(
+static int damos_sysfs_set_quota_score(
struct damos_sysfs_quota_goals *sysfs_goals,
struct damos_quota *quota)
{
- struct damos_sysfs_quota_goal *sysfs_goal;
+ struct damos_quota_goal *goal, *next;
int i;
- quota->get_score = NULL;
- quota->get_score_arg = (void *)0;
+ damos_for_each_quota_goal_safe(goal, next, quota)
+ damos_destroy_quota_goal(goal);
+
for (i = 0; i < sysfs_goals->nr; i++) {
- sysfs_goal = sysfs_goals->goals_arr[i];
+ struct damos_sysfs_quota_goal *sysfs_goal =
+ sysfs_goals->goals_arr[i];
+
if (!sysfs_goal->target_value)
continue;
- /* Higher score makes scheme less aggressive */
- quota->get_score_arg = (void *)max(
- (unsigned long)quota->get_score_arg,
- sysfs_goal->current_value * 10000 /
+ goal = damos_new_quota_goal(sysfs_goal->metric,
sysfs_goal->target_value);
- quota->get_score = damos_sysfs_get_quota_score;
+ if (!goal)
+ return -ENOMEM;
+ if (sysfs_goal->metric == DAMOS_QUOTA_USER_INPUT)
+ goal->current_value = sysfs_goal->current_value;
+ damos_add_quota_goal(quota, goal);
}
+ return 0;
}
-void damos_sysfs_set_quota_scores(struct damon_sysfs_schemes *sysfs_schemes,
+int damos_sysfs_set_quota_scores(struct damon_sysfs_schemes *sysfs_schemes,
struct damon_ctx *ctx)
{
struct damos *scheme;
@@ -1904,16 +1955,41 @@ void damos_sysfs_set_quota_scores(struct damon_sysfs_schemes *sysfs_schemes,
damon_for_each_scheme(scheme, ctx) {
struct damon_sysfs_scheme *sysfs_scheme;
+ int err;
/* user could have removed the scheme sysfs dir */
if (i >= sysfs_schemes->nr)
break;
sysfs_scheme = sysfs_schemes->schemes_arr[i];
- damos_sysfs_set_quota_score(sysfs_scheme->quotas->goals,
+ err = damos_sysfs_set_quota_score(sysfs_scheme->quotas->goals,
&scheme->quota);
+ if (err)
+ /* kdamond will clean up schemes and terminated */
+ return err;
i++;
}
+ return 0;
+}
+
+void damos_sysfs_update_effective_quotas(
+ struct damon_sysfs_schemes *sysfs_schemes,
+ struct damon_ctx *ctx)
+{
+ struct damos *scheme;
+ int schemes_idx = 0;
+
+ damon_for_each_scheme(scheme, ctx) {
+ struct damon_sysfs_quotas *sysfs_quotas;
+
+ /* user could have removed the scheme sysfs dir */
+ if (schemes_idx >= sysfs_schemes->nr)
+ break;
+
+ sysfs_quotas =
+ sysfs_schemes->schemes_arr[schemes_idx++]->quotas;
+ sysfs_quotas->effective_sz = scheme->quota.esz;
+ }
}
static struct damos *damon_sysfs_mk_scheme(
@@ -1953,13 +2029,17 @@ static struct damos *damon_sysfs_mk_scheme(
.low = sysfs_wmarks->low,
};
- damos_sysfs_set_quota_score(sysfs_quotas->goals, &quota);
-
scheme = damon_new_scheme(&pattern, sysfs_scheme->action,
sysfs_scheme->apply_interval_us, &quota, &wmarks);
if (!scheme)
return NULL;
+ err = damos_sysfs_set_quota_score(sysfs_quotas->goals, &scheme->quota);
+ if (err) {
+ damon_destroy_scheme(scheme);
+ return NULL;
+ }
+
err = damon_sysfs_set_scheme_filters(scheme, sysfs_filters);
if (err) {
damon_destroy_scheme(scheme);
@@ -1995,7 +2075,11 @@ static void damon_sysfs_update_scheme(struct damos *scheme,
scheme->quota.weight_nr_accesses = sysfs_weights->nr_accesses;
scheme->quota.weight_age = sysfs_weights->age;
- damos_sysfs_set_quota_score(sysfs_quotas->goals, &scheme->quota);
+ err = damos_sysfs_set_quota_score(sysfs_quotas->goals, &scheme->quota);
+ if (err) {
+ damon_destroy_scheme(scheme);
+ return;
+ }
scheme->wmarks.metric = sysfs_wmarks->metric;
scheme->wmarks.interval = sysfs_wmarks->interval_us;
@@ -2122,7 +2206,7 @@ static int damon_sysfs_before_damos_apply(struct damon_ctx *ctx,
* callback is registered, damon_sysfs_lock should be held to ensure the
* regions directories exist.
*/
-static int damon_sysfs_after_sampling(struct damon_ctx *ctx)
+void damos_sysfs_mark_finished_regions_updates(struct damon_ctx *ctx)
{
struct damon_sysfs_schemes *sysfs_schemes =
damon_sysfs_schemes_for_damos_callback;
@@ -2138,8 +2222,6 @@ static int damon_sysfs_after_sampling(struct damon_ctx *ctx)
sysfs_regions->upd_status =
DAMOS_TRIED_REGIONS_UPD_FINISHED;
}
-
- return 0;
}
/* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock */
@@ -2212,7 +2294,6 @@ int damon_sysfs_schemes_update_regions_start(
damos_tried_regions_init_upd_status(sysfs_schemes, ctx);
damos_regions_upd_total_bytes_only = total_bytes_only;
ctx->callback.before_damos_apply = damon_sysfs_before_damos_apply;
- ctx->callback.after_sampling = damon_sysfs_after_sampling;
return 0;
}
@@ -2241,7 +2322,6 @@ int damon_sysfs_schemes_update_regions_stop(struct damon_ctx *ctx)
{
damon_sysfs_schemes_for_damos_callback = NULL;
ctx->callback.before_damos_apply = NULL;
- ctx->callback.after_sampling = NULL;
damon_sysfs_schemes_region_idx = 0;
return 0;
}