@@ -130,6 +130,7 @@ enum damos_action {
* struct damos_quota_goal - DAMOS scheme quota auto-tuning goal.
* @get_score: Function for getting current score of the goal.
* @get_score_arg: Parameter for @get_score
+ * @list: List head for siblings.
*
* Data structure for getting the current score of the quota tuning goal.
* Calling @get_score with @get_score_arg as the parameter should return the
@@ -140,6 +141,7 @@ enum damos_action {
struct damos_quota_goal {
unsigned long (*get_score)(void *arg);
void *get_score_arg;
+ struct list_head list;
};
/**
@@ -148,6 +150,7 @@ struct damos_quota_goal {
* @ms: Maximum milliseconds that the scheme can use.
* @sz: Maximum bytes of memory that the action can be applied.
* @goal: Quota auto-tuning goal.
+ * @goals: Head of quota tuning goals (&damos_quota_goal) list.
* @esz: Effective size quota in bytes.
*
* @weight_sz: Weight of the region's size for prioritization.
@@ -171,6 +174,8 @@ struct damos_quota_goal {
* every @reset_interval. Then, if the new size quota is smaller than the
* effective quota, it uses the new size quota as the effective quota.
*
+ * If @goals is not empty, same action is taken for each goal of the list.
+ *
* The resulting effective size quota in bytes is set to @esz.
*
* For selecting regions within the quota, DAMON prioritizes current scheme's
@@ -184,6 +189,7 @@ struct damos_quota {
unsigned long ms;
unsigned long sz;
struct damos_quota_goal goal;
+ struct list_head goals;
unsigned long esz;
unsigned int weight_sz;
@@ -648,6 +654,12 @@ static inline unsigned long damon_sz_region(struct damon_region *r)
#define damon_for_each_scheme_safe(s, next, ctx) \
list_for_each_entry_safe(s, next, &(ctx)->schemes, list)
+#define damos_for_each_quota_goal(goal, quota) \
+ list_for_each_entry(goal, "a->goals, list)
+
+#define damos_for_each_quota_goal_safe(goal, next, quota) \
+ list_for_each_entry_safe(goal, next, &(quota)->goals, list)
+
#define damos_for_each_filter(f, scheme) \
list_for_each_entry(f, &(scheme)->filters, list)
@@ -681,6 +693,11 @@ struct damos_filter *damos_new_filter(enum damos_filter_type type,
void damos_add_filter(struct damos *s, struct damos_filter *f);
void damos_destroy_filter(struct damos_filter *f);
+struct damos_quota_goal *damos_new_quota_goal(
+ unsigned long (*get_score)(void *), void *get_score_arg);
+void damos_add_quota_goal(struct damos_quota *q, struct damos_quota_goal *g);
+void damos_destroy_quota_goal(struct damos_quota_goal *goal);
+
struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
enum damos_action action,
unsigned long apply_interval_us,
@@ -299,6 +299,41 @@ void damos_destroy_filter(struct damos_filter *f)
damos_free_filter(f);
}
+struct damos_quota_goal *damos_new_quota_goal(
+ unsigned long (*get_score)(void *), void *get_score_arg)
+{
+ struct damos_quota_goal *goal;
+
+ goal = kmalloc(sizeof(*goal), GFP_KERNEL);
+ if (!goal)
+ return NULL;
+ goal->get_score = get_score;
+ goal->get_score_arg = get_score_arg;
+ INIT_LIST_HEAD(&goal->list);
+ return goal;
+}
+
+void damos_add_quota_goal(struct damos_quota *q, struct damos_quota_goal *g)
+{
+ list_add_tail(&g->list, &q->goals);
+}
+
+static void damos_del_quota_goal(struct damos_quota_goal *g)
+{
+ list_del(&g->list);
+}
+
+static void damos_free_quota_goal(struct damos_quota_goal *g)
+{
+ kfree(g);
+}
+
+void damos_destroy_quota_goal(struct damos_quota_goal *g)
+{
+ damos_del_quota_goal(g);
+ damos_free_quota_goal(g);
+}
+
/* initialize fields of @quota that normally API users wouldn't set */
static struct damos_quota *damos_quota_init(struct damos_quota *quota)
{
@@ -337,6 +372,8 @@ struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
INIT_LIST_HEAD(&scheme->list);
scheme->quota = *(damos_quota_init(quota));
+ /* quota.goals should be separately set by caller */
+ INIT_LIST_HEAD(&scheme->quota.goals);
scheme->wmarks = *wmarks;
scheme->wmarks.activated = true;
@@ -373,8 +410,12 @@ static void damon_free_scheme(struct damos *s)
void damon_destroy_scheme(struct damos *s)
{
+ struct damos_quota_goal *g, *g_next;
struct damos_filter *f, *next;
+ damos_for_each_quota_goal_safe(g, g_next, &s->quota)
+ damos_destroy_quota_goal(g);
+
damos_for_each_filter_safe(f, next, s)
damos_destroy_filter(f);
damon_del_scheme(s);
@@ -1083,22 +1124,44 @@ static unsigned long damon_feed_loop_next_input(unsigned long last_input,
return min_input;
}
-/* Called only if quota->ms, quota->sz, or quota->goal.get_score are set */
+/* Return the highest score since it makes schemes least aggressive */
+static unsigned long damos_quota_score(struct damos_quota *quota)
+{
+ struct damos_quota_goal *goal;
+ unsigned long highest_score = 0;
+
+ if (quota->goal.get_score)
+ highest_score = quota->goal.get_score(
+ quota->goal.get_score_arg);
+
+ damos_for_each_quota_goal(goal, quota)
+ highest_score = max(highest_score,
+ goal->get_score(goal->get_score_arg));
+
+ return highest_score;
+}
+
+/*
+ * Called only if quota->ms, quota->sz, or quota->goal.get_score are set, or
+ * quota->goals is not empty
+ */
static void damos_set_effective_quota(struct damos_quota *quota)
{
unsigned long throughput;
unsigned long esz;
- if (!quota->ms && !quota->goal.get_score) {
+ if (!quota->ms && !quota->goal.get_score &&
+ list_empty("a->goals)) {
quota->esz = quota->sz;
return;
}
- if (quota->goal.get_score) {
+ if (quota->goal.get_score || !list_empty("a->goals)) {
+ unsigned long score = damos_quota_score(quota);
+
quota->esz_bp = damon_feed_loop_next_input(
max(quota->esz_bp, 10000UL),
- quota->goal.get_score(
- quota->goal.get_score_arg));
+ score);
esz = quota->esz_bp / 10000;
}
@@ -1108,7 +1171,7 @@ static void damos_set_effective_quota(struct damos_quota *quota)
quota->total_charged_ns;
else
throughput = PAGE_SIZE * 1024;
- if (quota->goal.get_score)
+ if (quota->goal.get_score || !list_empty("a->goals))
esz = min(throughput * quota->ms, esz);
else
esz = throughput * quota->ms;
@@ -1128,7 +1191,8 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
unsigned long cumulated_sz;
unsigned int score, max_score = 0;
- if (!quota->ms && !quota->sz && !quota->goal.get_score)
+ if (!quota->ms && !quota->sz && !quota->goal.get_score &&
+ list_empty("a->goals))
return;
/* New charge window starts */