@@ -14,6 +14,8 @@
/* Minimal region size. Every damon_region is aligned by this. */
#define DAMON_MIN_REGION PAGE_SIZE
+/* Max priority score for DAMON-based operation schemes */
+#define DAMOS_MAX_SCORE (99)
/**
* struct damon_addr_range - Represents an address region of [@start, @end).
@@ -92,8 +94,18 @@ enum damos_action {
* @sz: Scheme action amount limit in bytes.
* @ms: Scheme action amount charge duration.
*
+ * @weight_sz: Weight of the region's size for prioritization.
+ * @weight_nr_accesses: Weight of the region's nr_accesses for prioritization.
+ * @weight_age: Weight of the region's age for prioritization.
+ *
* To avoid consuming too much CPU time for applying the &struct damos->action
- * to large memory, DAMON applies it to only up to &sz bytes within &ms.
+ * to large memory, DAMON applies it to only up to &sz bytes within &ms. For
+ * selecting regions within the limit, DAMON prioritizes current scheme's
+ * target memory regions using the given &struct
+ * damon_primitive->get_scheme_score. You could customize the prioritization
+ * logic for your environment by setting &weight_sz, &weight_nr_accesses, and
+ * &weight_age, because primitives are encouraged to respect those, though it's
+ * not mandatory.
*
* If &sz is 0, the limit is disabled.
*/
@@ -101,11 +113,18 @@ struct damos_speed_limit {
unsigned long sz;
unsigned long ms;
+ unsigned int weight_sz;
+ unsigned int weight_nr_accesses;
+ unsigned int weight_age;
+
/* private: for limit accounting */
unsigned long charged_sz;
unsigned long charged_from;
struct damon_target *charge_target_from;
unsigned long charge_addr_from;
+
+ unsigned long histogram[DAMOS_MAX_SCORE + 1];
+ unsigned int min_score;
};
/**
@@ -155,6 +174,7 @@ struct damon_ctx;
* @prepare_access_checks: Prepare next access check of target regions.
* @check_accesses: Check the accesses to target regions.
* @reset_aggregated: Reset aggregated accesses monitoring results.
+ * @get_scheme_score: Get the score of a region for a scheme.
* @apply_scheme: Apply a DAMON-based operation scheme.
* @target_valid: Determine if the target is valid.
* @cleanup: Clean up the context.
@@ -182,6 +202,8 @@ struct damon_ctx;
* of its update. The value will be used for regions adjustment threshold.
* @reset_aggregated should reset the access monitoring results that aggregated
* by @check_accesses.
+ * @get_scheme_score should return the priority score of a region for a scheme
+ * as an integer in [0, &DAMOS_MAX_SCORE].
* @apply_scheme is called from @kdamond when a region for user provided
* DAMON-based operation scheme is found. It should apply the scheme's action
* to the region. This is not used for &DAMON_ARBITRARY_TARGET case.
@@ -196,6 +218,9 @@ struct damon_primitive {
void (*prepare_access_checks)(struct damon_ctx *context);
unsigned int (*check_accesses)(struct damon_ctx *context);
void (*reset_aggregated)(struct damon_ctx *context);
+ int (*get_scheme_score)(struct damon_ctx *context,
+ struct damon_target *t, struct damon_region *r,
+ struct damos *scheme);
int (*apply_scheme)(struct damon_ctx *context, struct damon_target *t,
struct damon_region *r, struct damos *scheme);
bool (*target_valid)(void *target);
@@ -12,6 +12,7 @@
#include <linux/kthread.h>
#include <linux/random.h>
#include <linux/slab.h>
+#include <linux/string.h>
#define CREATE_TRACE_POINTS
#include <trace/events/damon.h>
@@ -105,11 +106,13 @@ struct damos *damon_new_scheme(
scheme->limit.sz = limit->sz;
scheme->limit.ms = limit->ms;
+ scheme->limit.weight_sz = limit->weight_sz;
+ scheme->limit.weight_nr_accesses = limit->weight_nr_accesses;
+ scheme->limit.weight_age = limit->weight_age;
scheme->limit.charged_sz = 0;
scheme->limit.charged_from = 0;
scheme->limit.charge_target_from = NULL;
scheme->limit.charge_addr_from = 0;
-
return scheme;
}
@@ -546,6 +549,28 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)
static void damon_split_region_at(struct damon_ctx *ctx,
struct damon_region *r, unsigned long sz_r);
+static bool __damos_valid_target(struct damon_region *r, struct damos *s)
+{
+ unsigned long sz;
+
+ sz = r->ar.end - r->ar.start;
+ return s->min_sz_region <= sz && sz <= s->max_sz_region &&
+ s->min_nr_accesses <= r->nr_accesses &&
+ r->nr_accesses <= s->max_nr_accesses &&
+ s->min_age_region <= r->age && r->age <= s->max_age_region;
+}
+
+static bool damos_valid_target(struct damon_ctx *c, struct damon_target *t,
+ struct damon_region *r, struct damos *s)
+{
+ bool ret = __damos_valid_target(r, s);
+
+ if (!ret || !s->limit.sz || !c->primitive.get_scheme_score)
+ return ret;
+
+ return c->primitive.get_scheme_score(c, t, r, s) >= s->limit.min_score;
+}
+
static void damon_do_apply_schemes(struct damon_ctx *c,
struct damon_target *t,
struct damon_region *r)
@@ -575,17 +600,11 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
limit->charge_addr_from = 0;
}
- sz = r->ar.end - r->ar.start;
- /* Check the target regions condition */
- if (sz < s->min_sz_region || s->max_sz_region < sz)
- continue;
- if (r->nr_accesses < s->min_nr_accesses ||
- s->max_nr_accesses < r->nr_accesses)
- continue;
- if (r->age < s->min_age_region || s->max_age_region < r->age)
+ if (!damos_valid_target(c, t, r, s))
continue;
/* Apply the scheme */
+ sz = r->ar.end - r->ar.start;
if (c->primitive.apply_scheme) {
if (limit->sz && limit->charged_sz + sz > limit->sz) {
sz = limit->sz - limit->charged_sz;
@@ -615,16 +634,44 @@ static void kdamond_apply_schemes(struct damon_ctx *c)
damon_for_each_scheme(s, c) {
struct damos_speed_limit *limit = &s->limit;
+ unsigned long cumulated_sz;
+ unsigned int score, max_score = 0;
if (!limit->sz)
continue;
/* Reset charge window if the duration passed */
- if (time_after_eq(jiffies, limit->charged_from +
- msecs_to_jiffies(limit->ms))) {
+ if (time_after_eq(jiffies, s->limit.charged_from +
+ msecs_to_jiffies(s->limit.ms))) {
limit->charged_from = jiffies;
limit->charged_sz = 0;
}
+
+ if (!c->primitive.get_scheme_score)
+ continue;
+
+ /* Fill up the score histogram */
+ memset(limit->histogram, 0, sizeof(limit->histogram));
+ damon_for_each_target(t, c) {
+ damon_for_each_region(r, t) {
+ if (!__damos_valid_target(r, s))
+ continue;
+ score = c->primitive.get_scheme_score(
+ c, t, r, s);
+ limit->histogram[score] +=
+ r->ar.end - r->ar.start;
+ if (score > max_score)
+ max_score = score;
+ }
+ }
+
+ /* Set the min score limit */
+ for (cumulated_sz = 0, score = max_score; ; score--) {
+ cumulated_sz += limit->histogram[score];
+ if (cumulated_sz >= limit->sz || !score)
+ break;
+ }
+ limit->min_score = score;
}
damon_for_each_target(t, c) {