* struct damos - Represents a Data Access Monitoring-based Operation Scheme.
  * @pattern:           Access pattern of target regions.
  * @action:            &damo_action to be applied to the target regions.
+ * @apply_interval_us: The time between applying the @action.
  * @quota:             Control the aggressiveness of this scheme.
  * @wmarks:            Watermarks for automated (in)activation of this scheme.
  * @filters:           Additional set of &struct damos_filter for &action.
  * @stat:              Statistics of this scheme.
  * @list:              List head for siblings.
  *
- * For each aggregation interval, DAMON finds regions which fit in the
+ * For each @apply_interval_us, DAMON finds regions which fit in the
  * &pattern and applies &action to those. To avoid consuming too much
  * CPU time or IO resources for the &action, "a is used.
  *
+ * If @apply_interval_us is zero, &damon_attrs->aggr_interval is used instead.
+ *
  * To do the work only when needed, schemes can be activated for specific
  * system situations using &wmarks.  If all schemes that registered to the
  * monitoring context are inactive, DAMON stops monitoring either, and just
 struct damos {
        struct damos_access_pattern pattern;
        enum damos_action action;
+       unsigned long apply_interval_us;
+/* private: internal use only */
+       /*
+        * number of sample intervals that should be passed before applying
+        * @action
+        */
+       unsigned long next_apply_sis;
+/* public: */
        struct damos_quota quota;
        struct damos_watermarks wmarks;
        struct list_head filters;
 void damos_destroy_filter(struct damos_filter *f);
 
 struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
-                       enum damos_action action, struct damos_quota *quota,
+                       enum damos_action action,
+                       unsigned long apply_interval_us,
+                       struct damos_quota *quota,
                        struct damos_watermarks *wmarks);
 void damon_add_scheme(struct damon_ctx *ctx, struct damos *s);
 void damon_destroy_scheme(struct damos *s);
 
 }
 
 struct damos *damon_new_scheme(struct damos_access_pattern *pattern,
-                       enum damos_action action, struct damos_quota *quota,
+                       enum damos_action action,
+                       unsigned long apply_interval_us,
+                       struct damos_quota *quota,
                        struct damos_watermarks *wmarks)
 {
        struct damos *scheme;
                return NULL;
        scheme->pattern = *pattern;
        scheme->action = action;
+       scheme->apply_interval_us = apply_interval_us;
+       /*
+        * next_apply_sis will be set when kdamond starts.  While kdamond is
+        * running, it will also updated when it is added to the DAMON context,
+        * or damon_attrs are updated.
+        */
+       scheme->next_apply_sis = 0;
        INIT_LIST_HEAD(&scheme->filters);
        scheme->stat = (struct damos_stat){};
        INIT_LIST_HEAD(&scheme->list);
        return scheme;
 }
 
+static void damos_set_next_apply_sis(struct damos *s, struct damon_ctx *ctx)
+{
+       unsigned long sample_interval = ctx->attrs.sample_interval ?
+               ctx->attrs.sample_interval : 1;
+       unsigned long apply_interval = s->apply_interval_us ?
+               s->apply_interval_us : ctx->attrs.aggr_interval;
+
+       s->next_apply_sis = ctx->passed_sample_intervals +
+               apply_interval / sample_interval;
+}
+
 void damon_add_scheme(struct damon_ctx *ctx, struct damos *s)
 {
        list_add_tail(&s->list, &ctx->schemes);
+       damos_set_next_apply_sis(s, ctx);
 }
 
 static void damon_del_scheme(struct damos *s)
 {
        unsigned long sample_interval = attrs->sample_interval ?
                attrs->sample_interval : 1;
+       struct damos *s;
 
        if (attrs->min_nr_regions < 3)
                return -EINVAL;
 
        damon_update_monitoring_results(ctx, attrs);
        ctx->attrs = *attrs;
+
+       damon_for_each_scheme(s, ctx)
+               damos_set_next_apply_sis(s, ctx);
+
        return 0;
 }
 
        struct damon_target *t;
        struct damon_region *r, *next_r;
        struct damos *s;
+       unsigned long sample_interval = c->attrs.sample_interval ?
+               c->attrs.sample_interval : 1;
+       bool has_schemes_to_apply = false;
 
        damon_for_each_scheme(s, c) {
+               if (c->passed_sample_intervals != s->next_apply_sis)
+                       continue;
+
+               s->next_apply_sis +=
+                       (s->apply_interval_us ? s->apply_interval_us :
+                        c->attrs.aggr_interval) / sample_interval;
+
                if (!s->wmarks.activated)
                        continue;
 
+               has_schemes_to_apply = true;
+
                damos_adjust_quota(c, s);
        }
 
+       if (!has_schemes_to_apply)
+               return;
+
        damon_for_each_target(t, c) {
                damon_for_each_region_safe(r, next_r, t)
                        damon_do_apply_schemes(c, t, r);
 {
        unsigned long sample_interval = ctx->attrs.sample_interval ?
                ctx->attrs.sample_interval : 1;
+       unsigned long apply_interval;
+       struct damos *scheme;
 
        ctx->passed_sample_intervals = 0;
        ctx->next_aggregation_sis = ctx->attrs.aggr_interval / sample_interval;
        ctx->next_ops_update_sis = ctx->attrs.ops_update_interval /
                sample_interval;
+
+       damon_for_each_scheme(scheme, ctx) {
+               apply_interval = scheme->apply_interval_us ?
+                       scheme->apply_interval_us : ctx->attrs.aggr_interval;
+               scheme->next_apply_sis = apply_interval / sample_interval;
+       }
 }
 
 /*
                if (ctx->ops.check_accesses)
                        max_nr_accesses = ctx->ops.check_accesses(ctx);
 
-               sample_interval = ctx->attrs.sample_interval ?
-                       ctx->attrs.sample_interval : 1;
                if (ctx->passed_sample_intervals == next_aggregation_sis) {
-                       ctx->next_aggregation_sis = next_aggregation_sis +
-                               ctx->attrs.aggr_interval / sample_interval;
                        kdamond_merge_regions(ctx,
                                        max_nr_accesses / 10,
                                        sz_limit);
                        if (ctx->callback.after_aggregation &&
                                        ctx->callback.after_aggregation(ctx))
                                break;
-                       if (!list_empty(&ctx->schemes))
-                               kdamond_apply_schemes(ctx);
+               }
+
+               /*
+                * do kdamond_apply_schemes() after kdamond_merge_regions() if
+                * possible, to reduce overhead
+                */
+               if (!list_empty(&ctx->schemes))
+                       kdamond_apply_schemes(ctx);
+
+               sample_interval = ctx->attrs.sample_interval ?
+                       ctx->attrs.sample_interval : 1;
+               if (ctx->passed_sample_intervals == next_aggregation_sis) {
+                       ctx->next_aggregation_sis = next_aggregation_sis +
+                               ctx->attrs.aggr_interval / sample_interval;
+
                        kdamond_reset_aggregated(ctx);
                        kdamond_split_regions(ctx);
                        if (ctx->ops.reset_aggregated)