162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Data Access Monitor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: SeongJae Park <sjpark@amazon.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "damon: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/damon.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/kthread.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1862306a36Sopenharmony_ci#include <trace/events/damon.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#ifdef CONFIG_DAMON_KUNIT_TEST
2162306a36Sopenharmony_ci#undef DAMON_MIN_REGION
2262306a36Sopenharmony_ci#define DAMON_MIN_REGION 1
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic DEFINE_MUTEX(damon_lock);
2662306a36Sopenharmony_cistatic int nr_running_ctxs;
2762306a36Sopenharmony_cistatic bool running_exclusive_ctxs;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic DEFINE_MUTEX(damon_ops_lock);
3062306a36Sopenharmony_cistatic struct damon_operations damon_registered_ops[NR_DAMON_OPS];
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct kmem_cache *damon_region_cache __ro_after_init;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Should be called under damon_ops_lock with id smaller than NR_DAMON_OPS */
3562306a36Sopenharmony_cistatic bool __damon_is_registered_ops(enum damon_ops_id id)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct damon_operations empty_ops = {};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (!memcmp(&empty_ops, &damon_registered_ops[id], sizeof(empty_ops)))
4062306a36Sopenharmony_ci		return false;
4162306a36Sopenharmony_ci	return true;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * damon_is_registered_ops() - Check if a given damon_operations is registered.
4662306a36Sopenharmony_ci * @id:	Id of the damon_operations to check if registered.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * Return: true if the ops is set, false otherwise.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cibool damon_is_registered_ops(enum damon_ops_id id)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	bool registered;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (id >= NR_DAMON_OPS)
5562306a36Sopenharmony_ci		return false;
5662306a36Sopenharmony_ci	mutex_lock(&damon_ops_lock);
5762306a36Sopenharmony_ci	registered = __damon_is_registered_ops(id);
5862306a36Sopenharmony_ci	mutex_unlock(&damon_ops_lock);
5962306a36Sopenharmony_ci	return registered;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * damon_register_ops() - Register a monitoring operations set to DAMON.
6462306a36Sopenharmony_ci * @ops:	monitoring operations set to register.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * This function registers a monitoring operations set of valid &struct
6762306a36Sopenharmony_ci * damon_operations->id so that others can find and use them later.
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ciint damon_register_ops(struct damon_operations *ops)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int err = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (ops->id >= NR_DAMON_OPS)
7662306a36Sopenharmony_ci		return -EINVAL;
7762306a36Sopenharmony_ci	mutex_lock(&damon_ops_lock);
7862306a36Sopenharmony_ci	/* Fail for already registered ops */
7962306a36Sopenharmony_ci	if (__damon_is_registered_ops(ops->id)) {
8062306a36Sopenharmony_ci		err = -EINVAL;
8162306a36Sopenharmony_ci		goto out;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	damon_registered_ops[ops->id] = *ops;
8462306a36Sopenharmony_ciout:
8562306a36Sopenharmony_ci	mutex_unlock(&damon_ops_lock);
8662306a36Sopenharmony_ci	return err;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * damon_select_ops() - Select a monitoring operations to use with the context.
9162306a36Sopenharmony_ci * @ctx:	monitoring context to use the operations.
9262306a36Sopenharmony_ci * @id:		id of the registered monitoring operations to select.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * This function finds registered monitoring operations set of @id and make
9562306a36Sopenharmony_ci * @ctx to use it.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_ciint damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int err = 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (id >= NR_DAMON_OPS)
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	mutex_lock(&damon_ops_lock);
10762306a36Sopenharmony_ci	if (!__damon_is_registered_ops(id))
10862306a36Sopenharmony_ci		err = -EINVAL;
10962306a36Sopenharmony_ci	else
11062306a36Sopenharmony_ci		ctx->ops = damon_registered_ops[id];
11162306a36Sopenharmony_ci	mutex_unlock(&damon_ops_lock);
11262306a36Sopenharmony_ci	return err;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * Construct a damon_region struct
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * Returns the pointer to the new struct if success, or NULL otherwise
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistruct damon_region *damon_new_region(unsigned long start, unsigned long end)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct damon_region *region;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	region = kmem_cache_alloc(damon_region_cache, GFP_KERNEL);
12562306a36Sopenharmony_ci	if (!region)
12662306a36Sopenharmony_ci		return NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	region->ar.start = start;
12962306a36Sopenharmony_ci	region->ar.end = end;
13062306a36Sopenharmony_ci	region->nr_accesses = 0;
13162306a36Sopenharmony_ci	INIT_LIST_HEAD(&region->list);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	region->age = 0;
13462306a36Sopenharmony_ci	region->last_nr_accesses = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return region;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid damon_add_region(struct damon_region *r, struct damon_target *t)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	list_add_tail(&r->list, &t->regions_list);
14262306a36Sopenharmony_ci	t->nr_regions++;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void damon_del_region(struct damon_region *r, struct damon_target *t)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	list_del(&r->list);
14862306a36Sopenharmony_ci	t->nr_regions--;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void damon_free_region(struct damon_region *r)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	kmem_cache_free(damon_region_cache, r);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid damon_destroy_region(struct damon_region *r, struct damon_target *t)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	damon_del_region(r, t);
15962306a36Sopenharmony_ci	damon_free_region(r);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/*
16362306a36Sopenharmony_ci * Check whether a region is intersecting an address range
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci * Returns true if it is.
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic bool damon_intersect(struct damon_region *r,
16862306a36Sopenharmony_ci		struct damon_addr_range *re)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return !(r->ar.end <= re->start || re->end <= r->ar.start);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*
17462306a36Sopenharmony_ci * Fill holes in regions with new regions.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_cistatic int damon_fill_regions_holes(struct damon_region *first,
17762306a36Sopenharmony_ci		struct damon_region *last, struct damon_target *t)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct damon_region *r = first;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	damon_for_each_region_from(r, t) {
18262306a36Sopenharmony_ci		struct damon_region *next, *newr;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		if (r == last)
18562306a36Sopenharmony_ci			break;
18662306a36Sopenharmony_ci		next = damon_next_region(r);
18762306a36Sopenharmony_ci		if (r->ar.end != next->ar.start) {
18862306a36Sopenharmony_ci			newr = damon_new_region(r->ar.end, next->ar.start);
18962306a36Sopenharmony_ci			if (!newr)
19062306a36Sopenharmony_ci				return -ENOMEM;
19162306a36Sopenharmony_ci			damon_insert_region(newr, r, next, t);
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * damon_set_regions() - Set regions of a target for given address ranges.
19962306a36Sopenharmony_ci * @t:		the given target.
20062306a36Sopenharmony_ci * @ranges:	array of new monitoring target ranges.
20162306a36Sopenharmony_ci * @nr_ranges:	length of @ranges.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * This function adds new regions to, or modify existing regions of a
20462306a36Sopenharmony_ci * monitoring target to fit in specific ranges.
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Return: 0 if success, or negative error code otherwise.
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_ciint damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,
20962306a36Sopenharmony_ci		unsigned int nr_ranges)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct damon_region *r, *next;
21262306a36Sopenharmony_ci	unsigned int i;
21362306a36Sopenharmony_ci	int err;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Remove regions which are not in the new ranges */
21662306a36Sopenharmony_ci	damon_for_each_region_safe(r, next, t) {
21762306a36Sopenharmony_ci		for (i = 0; i < nr_ranges; i++) {
21862306a36Sopenharmony_ci			if (damon_intersect(r, &ranges[i]))
21962306a36Sopenharmony_ci				break;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		if (i == nr_ranges)
22262306a36Sopenharmony_ci			damon_destroy_region(r, t);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	r = damon_first_region(t);
22662306a36Sopenharmony_ci	/* Add new regions or resize existing regions to fit in the ranges */
22762306a36Sopenharmony_ci	for (i = 0; i < nr_ranges; i++) {
22862306a36Sopenharmony_ci		struct damon_region *first = NULL, *last, *newr;
22962306a36Sopenharmony_ci		struct damon_addr_range *range;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		range = &ranges[i];
23262306a36Sopenharmony_ci		/* Get the first/last regions intersecting with the range */
23362306a36Sopenharmony_ci		damon_for_each_region_from(r, t) {
23462306a36Sopenharmony_ci			if (damon_intersect(r, range)) {
23562306a36Sopenharmony_ci				if (!first)
23662306a36Sopenharmony_ci					first = r;
23762306a36Sopenharmony_ci				last = r;
23862306a36Sopenharmony_ci			}
23962306a36Sopenharmony_ci			if (r->ar.start >= range->end)
24062306a36Sopenharmony_ci				break;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci		if (!first) {
24362306a36Sopenharmony_ci			/* no region intersects with this range */
24462306a36Sopenharmony_ci			newr = damon_new_region(
24562306a36Sopenharmony_ci					ALIGN_DOWN(range->start,
24662306a36Sopenharmony_ci						DAMON_MIN_REGION),
24762306a36Sopenharmony_ci					ALIGN(range->end, DAMON_MIN_REGION));
24862306a36Sopenharmony_ci			if (!newr)
24962306a36Sopenharmony_ci				return -ENOMEM;
25062306a36Sopenharmony_ci			damon_insert_region(newr, damon_prev_region(r), r, t);
25162306a36Sopenharmony_ci		} else {
25262306a36Sopenharmony_ci			/* resize intersecting regions to fit in this range */
25362306a36Sopenharmony_ci			first->ar.start = ALIGN_DOWN(range->start,
25462306a36Sopenharmony_ci					DAMON_MIN_REGION);
25562306a36Sopenharmony_ci			last->ar.end = ALIGN(range->end, DAMON_MIN_REGION);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci			/* fill possible holes in the range */
25862306a36Sopenharmony_ci			err = damon_fill_regions_holes(first, last, t);
25962306a36Sopenharmony_ci			if (err)
26062306a36Sopenharmony_ci				return err;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistruct damos_filter *damos_new_filter(enum damos_filter_type type,
26762306a36Sopenharmony_ci		bool matching)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct damos_filter *filter;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	filter = kmalloc(sizeof(*filter), GFP_KERNEL);
27262306a36Sopenharmony_ci	if (!filter)
27362306a36Sopenharmony_ci		return NULL;
27462306a36Sopenharmony_ci	filter->type = type;
27562306a36Sopenharmony_ci	filter->matching = matching;
27662306a36Sopenharmony_ci	INIT_LIST_HEAD(&filter->list);
27762306a36Sopenharmony_ci	return filter;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_civoid damos_add_filter(struct damos *s, struct damos_filter *f)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	list_add_tail(&f->list, &s->filters);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void damos_del_filter(struct damos_filter *f)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	list_del(&f->list);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void damos_free_filter(struct damos_filter *f)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	kfree(f);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_civoid damos_destroy_filter(struct damos_filter *f)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	damos_del_filter(f);
29862306a36Sopenharmony_ci	damos_free_filter(f);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/* initialize private fields of damos_quota and return the pointer */
30262306a36Sopenharmony_cistatic struct damos_quota *damos_quota_init_priv(struct damos_quota *quota)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	quota->total_charged_sz = 0;
30562306a36Sopenharmony_ci	quota->total_charged_ns = 0;
30662306a36Sopenharmony_ci	quota->esz = 0;
30762306a36Sopenharmony_ci	quota->charged_sz = 0;
30862306a36Sopenharmony_ci	quota->charged_from = 0;
30962306a36Sopenharmony_ci	quota->charge_target_from = NULL;
31062306a36Sopenharmony_ci	quota->charge_addr_from = 0;
31162306a36Sopenharmony_ci	return quota;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistruct damos *damon_new_scheme(struct damos_access_pattern *pattern,
31562306a36Sopenharmony_ci			enum damos_action action, struct damos_quota *quota,
31662306a36Sopenharmony_ci			struct damos_watermarks *wmarks)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct damos *scheme;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	scheme = kmalloc(sizeof(*scheme), GFP_KERNEL);
32162306a36Sopenharmony_ci	if (!scheme)
32262306a36Sopenharmony_ci		return NULL;
32362306a36Sopenharmony_ci	scheme->pattern = *pattern;
32462306a36Sopenharmony_ci	scheme->action = action;
32562306a36Sopenharmony_ci	INIT_LIST_HEAD(&scheme->filters);
32662306a36Sopenharmony_ci	scheme->stat = (struct damos_stat){};
32762306a36Sopenharmony_ci	INIT_LIST_HEAD(&scheme->list);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	scheme->quota = *(damos_quota_init_priv(quota));
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	scheme->wmarks = *wmarks;
33262306a36Sopenharmony_ci	scheme->wmarks.activated = true;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return scheme;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_civoid damon_add_scheme(struct damon_ctx *ctx, struct damos *s)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	list_add_tail(&s->list, &ctx->schemes);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void damon_del_scheme(struct damos *s)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	list_del(&s->list);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void damon_free_scheme(struct damos *s)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	kfree(s);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_civoid damon_destroy_scheme(struct damos *s)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct damos_filter *f, *next;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	damos_for_each_filter_safe(f, next, s)
35762306a36Sopenharmony_ci		damos_destroy_filter(f);
35862306a36Sopenharmony_ci	damon_del_scheme(s);
35962306a36Sopenharmony_ci	damon_free_scheme(s);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci * Construct a damon_target struct
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Returns the pointer to the new struct if success, or NULL otherwise
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_cistruct damon_target *damon_new_target(void)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct damon_target *t;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	t = kmalloc(sizeof(*t), GFP_KERNEL);
37262306a36Sopenharmony_ci	if (!t)
37362306a36Sopenharmony_ci		return NULL;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	t->pid = NULL;
37662306a36Sopenharmony_ci	t->nr_regions = 0;
37762306a36Sopenharmony_ci	INIT_LIST_HEAD(&t->regions_list);
37862306a36Sopenharmony_ci	INIT_LIST_HEAD(&t->list);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return t;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_civoid damon_add_target(struct damon_ctx *ctx, struct damon_target *t)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	list_add_tail(&t->list, &ctx->adaptive_targets);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cibool damon_targets_empty(struct damon_ctx *ctx)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	return list_empty(&ctx->adaptive_targets);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic void damon_del_target(struct damon_target *t)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	list_del(&t->list);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_civoid damon_free_target(struct damon_target *t)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct damon_region *r, *next;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	damon_for_each_region_safe(r, next, t)
40362306a36Sopenharmony_ci		damon_free_region(r);
40462306a36Sopenharmony_ci	kfree(t);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_civoid damon_destroy_target(struct damon_target *t)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	damon_del_target(t);
41062306a36Sopenharmony_ci	damon_free_target(t);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ciunsigned int damon_nr_regions(struct damon_target *t)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	return t->nr_regions;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistruct damon_ctx *damon_new_ctx(void)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct damon_ctx *ctx;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
42362306a36Sopenharmony_ci	if (!ctx)
42462306a36Sopenharmony_ci		return NULL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	init_completion(&ctx->kdamond_started);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	ctx->attrs.sample_interval = 5 * 1000;
42962306a36Sopenharmony_ci	ctx->attrs.aggr_interval = 100 * 1000;
43062306a36Sopenharmony_ci	ctx->attrs.ops_update_interval = 60 * 1000 * 1000;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	ctx->passed_sample_intervals = 0;
43362306a36Sopenharmony_ci	/* These will be set from kdamond_init_intervals_sis() */
43462306a36Sopenharmony_ci	ctx->next_aggregation_sis = 0;
43562306a36Sopenharmony_ci	ctx->next_ops_update_sis = 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	mutex_init(&ctx->kdamond_lock);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	ctx->attrs.min_nr_regions = 10;
44062306a36Sopenharmony_ci	ctx->attrs.max_nr_regions = 1000;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->adaptive_targets);
44362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ctx->schemes);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return ctx;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void damon_destroy_targets(struct damon_ctx *ctx)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct damon_target *t, *next_t;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (ctx->ops.cleanup) {
45362306a36Sopenharmony_ci		ctx->ops.cleanup(ctx);
45462306a36Sopenharmony_ci		return;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	damon_for_each_target_safe(t, next_t, ctx)
45862306a36Sopenharmony_ci		damon_destroy_target(t);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_civoid damon_destroy_ctx(struct damon_ctx *ctx)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct damos *s, *next_s;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	damon_destroy_targets(ctx);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	damon_for_each_scheme_safe(s, next_s, ctx)
46862306a36Sopenharmony_ci		damon_destroy_scheme(s);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	kfree(ctx);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic unsigned int damon_age_for_new_attrs(unsigned int age,
47462306a36Sopenharmony_ci		struct damon_attrs *old_attrs, struct damon_attrs *new_attrs)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	return age * old_attrs->aggr_interval / new_attrs->aggr_interval;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/* convert access ratio in bp (per 10,000) to nr_accesses */
48062306a36Sopenharmony_cistatic unsigned int damon_accesses_bp_to_nr_accesses(
48162306a36Sopenharmony_ci		unsigned int accesses_bp, struct damon_attrs *attrs)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	return accesses_bp * damon_max_nr_accesses(attrs) / 10000;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* convert nr_accesses to access ratio in bp (per 10,000) */
48762306a36Sopenharmony_cistatic unsigned int damon_nr_accesses_to_accesses_bp(
48862306a36Sopenharmony_ci		unsigned int nr_accesses, struct damon_attrs *attrs)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	return nr_accesses * 10000 / damon_max_nr_accesses(attrs);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic unsigned int damon_nr_accesses_for_new_attrs(unsigned int nr_accesses,
49462306a36Sopenharmony_ci		struct damon_attrs *old_attrs, struct damon_attrs *new_attrs)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	return damon_accesses_bp_to_nr_accesses(
49762306a36Sopenharmony_ci			damon_nr_accesses_to_accesses_bp(
49862306a36Sopenharmony_ci				nr_accesses, old_attrs),
49962306a36Sopenharmony_ci			new_attrs);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void damon_update_monitoring_result(struct damon_region *r,
50362306a36Sopenharmony_ci		struct damon_attrs *old_attrs, struct damon_attrs *new_attrs)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	r->nr_accesses = damon_nr_accesses_for_new_attrs(r->nr_accesses,
50662306a36Sopenharmony_ci			old_attrs, new_attrs);
50762306a36Sopenharmony_ci	r->age = damon_age_for_new_attrs(r->age, old_attrs, new_attrs);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/*
51162306a36Sopenharmony_ci * region->nr_accesses is the number of sampling intervals in the last
51262306a36Sopenharmony_ci * aggregation interval that access to the region has found, and region->age is
51362306a36Sopenharmony_ci * the number of aggregation intervals that its access pattern has maintained.
51462306a36Sopenharmony_ci * For the reason, the real meaning of the two fields depend on current
51562306a36Sopenharmony_ci * sampling interval and aggregation interval.  This function updates
51662306a36Sopenharmony_ci * ->nr_accesses and ->age of given damon_ctx's regions for new damon_attrs.
51762306a36Sopenharmony_ci */
51862306a36Sopenharmony_cistatic void damon_update_monitoring_results(struct damon_ctx *ctx,
51962306a36Sopenharmony_ci		struct damon_attrs *new_attrs)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct damon_attrs *old_attrs = &ctx->attrs;
52262306a36Sopenharmony_ci	struct damon_target *t;
52362306a36Sopenharmony_ci	struct damon_region *r;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* if any interval is zero, simply forgive conversion */
52662306a36Sopenharmony_ci	if (!old_attrs->sample_interval || !old_attrs->aggr_interval ||
52762306a36Sopenharmony_ci			!new_attrs->sample_interval ||
52862306a36Sopenharmony_ci			!new_attrs->aggr_interval)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	damon_for_each_target(t, ctx)
53262306a36Sopenharmony_ci		damon_for_each_region(r, t)
53362306a36Sopenharmony_ci			damon_update_monitoring_result(
53462306a36Sopenharmony_ci					r, old_attrs, new_attrs);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/**
53862306a36Sopenharmony_ci * damon_set_attrs() - Set attributes for the monitoring.
53962306a36Sopenharmony_ci * @ctx:		monitoring context
54062306a36Sopenharmony_ci * @attrs:		monitoring attributes
54162306a36Sopenharmony_ci *
54262306a36Sopenharmony_ci * This function should not be called while the kdamond is running.
54362306a36Sopenharmony_ci * Every time interval is in micro-seconds.
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_ciint damon_set_attrs(struct damon_ctx *ctx, struct damon_attrs *attrs)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	unsigned long sample_interval = attrs->sample_interval ?
55062306a36Sopenharmony_ci		attrs->sample_interval : 1;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (attrs->min_nr_regions < 3)
55362306a36Sopenharmony_ci		return -EINVAL;
55462306a36Sopenharmony_ci	if (attrs->min_nr_regions > attrs->max_nr_regions)
55562306a36Sopenharmony_ci		return -EINVAL;
55662306a36Sopenharmony_ci	if (attrs->sample_interval > attrs->aggr_interval)
55762306a36Sopenharmony_ci		return -EINVAL;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	ctx->next_aggregation_sis = ctx->passed_sample_intervals +
56062306a36Sopenharmony_ci		attrs->aggr_interval / sample_interval;
56162306a36Sopenharmony_ci	ctx->next_ops_update_sis = ctx->passed_sample_intervals +
56262306a36Sopenharmony_ci		attrs->ops_update_interval / sample_interval;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	damon_update_monitoring_results(ctx, attrs);
56562306a36Sopenharmony_ci	ctx->attrs = *attrs;
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/**
57062306a36Sopenharmony_ci * damon_set_schemes() - Set data access monitoring based operation schemes.
57162306a36Sopenharmony_ci * @ctx:	monitoring context
57262306a36Sopenharmony_ci * @schemes:	array of the schemes
57362306a36Sopenharmony_ci * @nr_schemes:	number of entries in @schemes
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * This function should not be called while the kdamond of the context is
57662306a36Sopenharmony_ci * running.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_civoid damon_set_schemes(struct damon_ctx *ctx, struct damos **schemes,
57962306a36Sopenharmony_ci			ssize_t nr_schemes)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	struct damos *s, *next;
58262306a36Sopenharmony_ci	ssize_t i;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	damon_for_each_scheme_safe(s, next, ctx)
58562306a36Sopenharmony_ci		damon_destroy_scheme(s);
58662306a36Sopenharmony_ci	for (i = 0; i < nr_schemes; i++)
58762306a36Sopenharmony_ci		damon_add_scheme(ctx, schemes[i]);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/**
59162306a36Sopenharmony_ci * damon_nr_running_ctxs() - Return number of currently running contexts.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_ciint damon_nr_running_ctxs(void)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	int nr_ctxs;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	mutex_lock(&damon_lock);
59862306a36Sopenharmony_ci	nr_ctxs = nr_running_ctxs;
59962306a36Sopenharmony_ci	mutex_unlock(&damon_lock);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return nr_ctxs;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/* Returns the size upper limit for each monitoring region */
60562306a36Sopenharmony_cistatic unsigned long damon_region_sz_limit(struct damon_ctx *ctx)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct damon_target *t;
60862306a36Sopenharmony_ci	struct damon_region *r;
60962306a36Sopenharmony_ci	unsigned long sz = 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	damon_for_each_target(t, ctx) {
61262306a36Sopenharmony_ci		damon_for_each_region(r, t)
61362306a36Sopenharmony_ci			sz += damon_sz_region(r);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (ctx->attrs.min_nr_regions)
61762306a36Sopenharmony_ci		sz /= ctx->attrs.min_nr_regions;
61862306a36Sopenharmony_ci	if (sz < DAMON_MIN_REGION)
61962306a36Sopenharmony_ci		sz = DAMON_MIN_REGION;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return sz;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int kdamond_fn(void *data);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/*
62762306a36Sopenharmony_ci * __damon_start() - Starts monitoring with given context.
62862306a36Sopenharmony_ci * @ctx:	monitoring context
62962306a36Sopenharmony_ci *
63062306a36Sopenharmony_ci * This function should be called while damon_lock is hold.
63162306a36Sopenharmony_ci *
63262306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
63362306a36Sopenharmony_ci */
63462306a36Sopenharmony_cistatic int __damon_start(struct damon_ctx *ctx)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	int err = -EBUSY;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
63962306a36Sopenharmony_ci	if (!ctx->kdamond) {
64062306a36Sopenharmony_ci		err = 0;
64162306a36Sopenharmony_ci		reinit_completion(&ctx->kdamond_started);
64262306a36Sopenharmony_ci		ctx->kdamond = kthread_run(kdamond_fn, ctx, "kdamond.%d",
64362306a36Sopenharmony_ci				nr_running_ctxs);
64462306a36Sopenharmony_ci		if (IS_ERR(ctx->kdamond)) {
64562306a36Sopenharmony_ci			err = PTR_ERR(ctx->kdamond);
64662306a36Sopenharmony_ci			ctx->kdamond = NULL;
64762306a36Sopenharmony_ci		} else {
64862306a36Sopenharmony_ci			wait_for_completion(&ctx->kdamond_started);
64962306a36Sopenharmony_ci		}
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return err;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci/**
65762306a36Sopenharmony_ci * damon_start() - Starts the monitorings for a given group of contexts.
65862306a36Sopenharmony_ci * @ctxs:	an array of the pointers for contexts to start monitoring
65962306a36Sopenharmony_ci * @nr_ctxs:	size of @ctxs
66062306a36Sopenharmony_ci * @exclusive:	exclusiveness of this contexts group
66162306a36Sopenharmony_ci *
66262306a36Sopenharmony_ci * This function starts a group of monitoring threads for a group of monitoring
66362306a36Sopenharmony_ci * contexts.  One thread per each context is created and run in parallel.  The
66462306a36Sopenharmony_ci * caller should handle synchronization between the threads by itself.  If
66562306a36Sopenharmony_ci * @exclusive is true and a group of threads that created by other
66662306a36Sopenharmony_ci * 'damon_start()' call is currently running, this function does nothing but
66762306a36Sopenharmony_ci * returns -EBUSY.
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
67062306a36Sopenharmony_ci */
67162306a36Sopenharmony_ciint damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	int i;
67462306a36Sopenharmony_ci	int err = 0;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	mutex_lock(&damon_lock);
67762306a36Sopenharmony_ci	if ((exclusive && nr_running_ctxs) ||
67862306a36Sopenharmony_ci			(!exclusive && running_exclusive_ctxs)) {
67962306a36Sopenharmony_ci		mutex_unlock(&damon_lock);
68062306a36Sopenharmony_ci		return -EBUSY;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	for (i = 0; i < nr_ctxs; i++) {
68462306a36Sopenharmony_ci		err = __damon_start(ctxs[i]);
68562306a36Sopenharmony_ci		if (err)
68662306a36Sopenharmony_ci			break;
68762306a36Sopenharmony_ci		nr_running_ctxs++;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	if (exclusive && nr_running_ctxs)
69062306a36Sopenharmony_ci		running_exclusive_ctxs = true;
69162306a36Sopenharmony_ci	mutex_unlock(&damon_lock);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	return err;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci/*
69762306a36Sopenharmony_ci * __damon_stop() - Stops monitoring of a given context.
69862306a36Sopenharmony_ci * @ctx:	monitoring context
69962306a36Sopenharmony_ci *
70062306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
70162306a36Sopenharmony_ci */
70262306a36Sopenharmony_cistatic int __damon_stop(struct damon_ctx *ctx)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	struct task_struct *tsk;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
70762306a36Sopenharmony_ci	tsk = ctx->kdamond;
70862306a36Sopenharmony_ci	if (tsk) {
70962306a36Sopenharmony_ci		get_task_struct(tsk);
71062306a36Sopenharmony_ci		mutex_unlock(&ctx->kdamond_lock);
71162306a36Sopenharmony_ci		kthread_stop(tsk);
71262306a36Sopenharmony_ci		put_task_struct(tsk);
71362306a36Sopenharmony_ci		return 0;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return -EPERM;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci * damon_stop() - Stops the monitorings for a given group of contexts.
72262306a36Sopenharmony_ci * @ctxs:	an array of the pointers for contexts to stop monitoring
72362306a36Sopenharmony_ci * @nr_ctxs:	size of @ctxs
72462306a36Sopenharmony_ci *
72562306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
72662306a36Sopenharmony_ci */
72762306a36Sopenharmony_ciint damon_stop(struct damon_ctx **ctxs, int nr_ctxs)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	int i, err = 0;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	for (i = 0; i < nr_ctxs; i++) {
73262306a36Sopenharmony_ci		/* nr_running_ctxs is decremented in kdamond_fn */
73362306a36Sopenharmony_ci		err = __damon_stop(ctxs[i]);
73462306a36Sopenharmony_ci		if (err)
73562306a36Sopenharmony_ci			break;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci	return err;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/*
74162306a36Sopenharmony_ci * Reset the aggregated monitoring results ('nr_accesses' of each region).
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cistatic void kdamond_reset_aggregated(struct damon_ctx *c)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct damon_target *t;
74662306a36Sopenharmony_ci	unsigned int ti = 0;	/* target's index */
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	damon_for_each_target(t, c) {
74962306a36Sopenharmony_ci		struct damon_region *r;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		damon_for_each_region(r, t) {
75262306a36Sopenharmony_ci			trace_damon_aggregated(t, ti, r, damon_nr_regions(t));
75362306a36Sopenharmony_ci			r->last_nr_accesses = r->nr_accesses;
75462306a36Sopenharmony_ci			r->nr_accesses = 0;
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci		ti++;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic void damon_split_region_at(struct damon_target *t,
76162306a36Sopenharmony_ci				  struct damon_region *r, unsigned long sz_r);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic bool __damos_valid_target(struct damon_region *r, struct damos *s)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	unsigned long sz;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	sz = damon_sz_region(r);
76862306a36Sopenharmony_ci	return s->pattern.min_sz_region <= sz &&
76962306a36Sopenharmony_ci		sz <= s->pattern.max_sz_region &&
77062306a36Sopenharmony_ci		s->pattern.min_nr_accesses <= r->nr_accesses &&
77162306a36Sopenharmony_ci		r->nr_accesses <= s->pattern.max_nr_accesses &&
77262306a36Sopenharmony_ci		s->pattern.min_age_region <= r->age &&
77362306a36Sopenharmony_ci		r->age <= s->pattern.max_age_region;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic bool damos_valid_target(struct damon_ctx *c, struct damon_target *t,
77762306a36Sopenharmony_ci		struct damon_region *r, struct damos *s)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	bool ret = __damos_valid_target(r, s);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (!ret || !s->quota.esz || !c->ops.get_scheme_score)
78262306a36Sopenharmony_ci		return ret;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return c->ops.get_scheme_score(c, t, r, s) >= s->quota.min_score;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/*
78862306a36Sopenharmony_ci * damos_skip_charged_region() - Check if the given region or starting part of
78962306a36Sopenharmony_ci * it is already charged for the DAMOS quota.
79062306a36Sopenharmony_ci * @t:	The target of the region.
79162306a36Sopenharmony_ci * @rp:	The pointer to the region.
79262306a36Sopenharmony_ci * @s:	The scheme to be applied.
79362306a36Sopenharmony_ci *
79462306a36Sopenharmony_ci * If a quota of a scheme has exceeded in a quota charge window, the scheme's
79562306a36Sopenharmony_ci * action would applied to only a part of the target access pattern fulfilling
79662306a36Sopenharmony_ci * regions.  To avoid applying the scheme action to only already applied
79762306a36Sopenharmony_ci * regions, DAMON skips applying the scheme action to the regions that charged
79862306a36Sopenharmony_ci * in the previous charge window.
79962306a36Sopenharmony_ci *
80062306a36Sopenharmony_ci * This function checks if a given region should be skipped or not for the
80162306a36Sopenharmony_ci * reason.  If only the starting part of the region has previously charged,
80262306a36Sopenharmony_ci * this function splits the region into two so that the second one covers the
80362306a36Sopenharmony_ci * area that not charged in the previous charge widnow and saves the second
80462306a36Sopenharmony_ci * region in *rp and returns false, so that the caller can apply DAMON action
80562306a36Sopenharmony_ci * to the second one.
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * Return: true if the region should be entirely skipped, false otherwise.
80862306a36Sopenharmony_ci */
80962306a36Sopenharmony_cistatic bool damos_skip_charged_region(struct damon_target *t,
81062306a36Sopenharmony_ci		struct damon_region **rp, struct damos *s)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct damon_region *r = *rp;
81362306a36Sopenharmony_ci	struct damos_quota *quota = &s->quota;
81462306a36Sopenharmony_ci	unsigned long sz_to_skip;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* Skip previously charged regions */
81762306a36Sopenharmony_ci	if (quota->charge_target_from) {
81862306a36Sopenharmony_ci		if (t != quota->charge_target_from)
81962306a36Sopenharmony_ci			return true;
82062306a36Sopenharmony_ci		if (r == damon_last_region(t)) {
82162306a36Sopenharmony_ci			quota->charge_target_from = NULL;
82262306a36Sopenharmony_ci			quota->charge_addr_from = 0;
82362306a36Sopenharmony_ci			return true;
82462306a36Sopenharmony_ci		}
82562306a36Sopenharmony_ci		if (quota->charge_addr_from &&
82662306a36Sopenharmony_ci				r->ar.end <= quota->charge_addr_from)
82762306a36Sopenharmony_ci			return true;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		if (quota->charge_addr_from && r->ar.start <
83062306a36Sopenharmony_ci				quota->charge_addr_from) {
83162306a36Sopenharmony_ci			sz_to_skip = ALIGN_DOWN(quota->charge_addr_from -
83262306a36Sopenharmony_ci					r->ar.start, DAMON_MIN_REGION);
83362306a36Sopenharmony_ci			if (!sz_to_skip) {
83462306a36Sopenharmony_ci				if (damon_sz_region(r) <= DAMON_MIN_REGION)
83562306a36Sopenharmony_ci					return true;
83662306a36Sopenharmony_ci				sz_to_skip = DAMON_MIN_REGION;
83762306a36Sopenharmony_ci			}
83862306a36Sopenharmony_ci			damon_split_region_at(t, r, sz_to_skip);
83962306a36Sopenharmony_ci			r = damon_next_region(r);
84062306a36Sopenharmony_ci			*rp = r;
84162306a36Sopenharmony_ci		}
84262306a36Sopenharmony_ci		quota->charge_target_from = NULL;
84362306a36Sopenharmony_ci		quota->charge_addr_from = 0;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci	return false;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic void damos_update_stat(struct damos *s,
84962306a36Sopenharmony_ci		unsigned long sz_tried, unsigned long sz_applied)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	s->stat.nr_tried++;
85262306a36Sopenharmony_ci	s->stat.sz_tried += sz_tried;
85362306a36Sopenharmony_ci	if (sz_applied)
85462306a36Sopenharmony_ci		s->stat.nr_applied++;
85562306a36Sopenharmony_ci	s->stat.sz_applied += sz_applied;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic bool __damos_filter_out(struct damon_ctx *ctx, struct damon_target *t,
85962306a36Sopenharmony_ci		struct damon_region *r, struct damos_filter *filter)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	bool matched = false;
86262306a36Sopenharmony_ci	struct damon_target *ti;
86362306a36Sopenharmony_ci	int target_idx = 0;
86462306a36Sopenharmony_ci	unsigned long start, end;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	switch (filter->type) {
86762306a36Sopenharmony_ci	case DAMOS_FILTER_TYPE_TARGET:
86862306a36Sopenharmony_ci		damon_for_each_target(ti, ctx) {
86962306a36Sopenharmony_ci			if (ti == t)
87062306a36Sopenharmony_ci				break;
87162306a36Sopenharmony_ci			target_idx++;
87262306a36Sopenharmony_ci		}
87362306a36Sopenharmony_ci		matched = target_idx == filter->target_idx;
87462306a36Sopenharmony_ci		break;
87562306a36Sopenharmony_ci	case DAMOS_FILTER_TYPE_ADDR:
87662306a36Sopenharmony_ci		start = ALIGN_DOWN(filter->addr_range.start, DAMON_MIN_REGION);
87762306a36Sopenharmony_ci		end = ALIGN_DOWN(filter->addr_range.end, DAMON_MIN_REGION);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		/* inside the range */
88062306a36Sopenharmony_ci		if (start <= r->ar.start && r->ar.end <= end) {
88162306a36Sopenharmony_ci			matched = true;
88262306a36Sopenharmony_ci			break;
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci		/* outside of the range */
88562306a36Sopenharmony_ci		if (r->ar.end <= start || end <= r->ar.start) {
88662306a36Sopenharmony_ci			matched = false;
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci		}
88962306a36Sopenharmony_ci		/* start before the range and overlap */
89062306a36Sopenharmony_ci		if (r->ar.start < start) {
89162306a36Sopenharmony_ci			damon_split_region_at(t, r, start - r->ar.start);
89262306a36Sopenharmony_ci			matched = false;
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci		/* start inside the range */
89662306a36Sopenharmony_ci		damon_split_region_at(t, r, end - r->ar.start);
89762306a36Sopenharmony_ci		matched = true;
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	default:
90062306a36Sopenharmony_ci		return false;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	return matched == filter->matching;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic bool damos_filter_out(struct damon_ctx *ctx, struct damon_target *t,
90762306a36Sopenharmony_ci		struct damon_region *r, struct damos *s)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	struct damos_filter *filter;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	damos_for_each_filter(filter, s) {
91262306a36Sopenharmony_ci		if (__damos_filter_out(ctx, t, r, filter))
91362306a36Sopenharmony_ci			return true;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci	return false;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
91962306a36Sopenharmony_ci		struct damon_region *r, struct damos *s)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct damos_quota *quota = &s->quota;
92262306a36Sopenharmony_ci	unsigned long sz = damon_sz_region(r);
92362306a36Sopenharmony_ci	struct timespec64 begin, end;
92462306a36Sopenharmony_ci	unsigned long sz_applied = 0;
92562306a36Sopenharmony_ci	int err = 0;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (c->ops.apply_scheme) {
92862306a36Sopenharmony_ci		if (quota->esz && quota->charged_sz + sz > quota->esz) {
92962306a36Sopenharmony_ci			sz = ALIGN_DOWN(quota->esz - quota->charged_sz,
93062306a36Sopenharmony_ci					DAMON_MIN_REGION);
93162306a36Sopenharmony_ci			if (!sz)
93262306a36Sopenharmony_ci				goto update_stat;
93362306a36Sopenharmony_ci			damon_split_region_at(t, r, sz);
93462306a36Sopenharmony_ci		}
93562306a36Sopenharmony_ci		if (damos_filter_out(c, t, r, s))
93662306a36Sopenharmony_ci			return;
93762306a36Sopenharmony_ci		ktime_get_coarse_ts64(&begin);
93862306a36Sopenharmony_ci		if (c->callback.before_damos_apply)
93962306a36Sopenharmony_ci			err = c->callback.before_damos_apply(c, t, r, s);
94062306a36Sopenharmony_ci		if (!err)
94162306a36Sopenharmony_ci			sz_applied = c->ops.apply_scheme(c, t, r, s);
94262306a36Sopenharmony_ci		ktime_get_coarse_ts64(&end);
94362306a36Sopenharmony_ci		quota->total_charged_ns += timespec64_to_ns(&end) -
94462306a36Sopenharmony_ci			timespec64_to_ns(&begin);
94562306a36Sopenharmony_ci		quota->charged_sz += sz;
94662306a36Sopenharmony_ci		if (quota->esz && quota->charged_sz >= quota->esz) {
94762306a36Sopenharmony_ci			quota->charge_target_from = t;
94862306a36Sopenharmony_ci			quota->charge_addr_from = r->ar.end + 1;
94962306a36Sopenharmony_ci		}
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci	if (s->action != DAMOS_STAT)
95262306a36Sopenharmony_ci		r->age = 0;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ciupdate_stat:
95562306a36Sopenharmony_ci	damos_update_stat(s, sz, sz_applied);
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic void damon_do_apply_schemes(struct damon_ctx *c,
95962306a36Sopenharmony_ci				   struct damon_target *t,
96062306a36Sopenharmony_ci				   struct damon_region *r)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct damos *s;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	damon_for_each_scheme(s, c) {
96562306a36Sopenharmony_ci		struct damos_quota *quota = &s->quota;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		if (!s->wmarks.activated)
96862306a36Sopenharmony_ci			continue;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/* Check the quota */
97162306a36Sopenharmony_ci		if (quota->esz && quota->charged_sz >= quota->esz)
97262306a36Sopenharmony_ci			continue;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		if (damos_skip_charged_region(t, &r, s))
97562306a36Sopenharmony_ci			continue;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci		if (!damos_valid_target(c, t, r, s))
97862306a36Sopenharmony_ci			continue;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		damos_apply_scheme(c, t, r, s);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/* Shouldn't be called if quota->ms and quota->sz are zero */
98562306a36Sopenharmony_cistatic void damos_set_effective_quota(struct damos_quota *quota)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	unsigned long throughput;
98862306a36Sopenharmony_ci	unsigned long esz;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (!quota->ms) {
99162306a36Sopenharmony_ci		quota->esz = quota->sz;
99262306a36Sopenharmony_ci		return;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (quota->total_charged_ns)
99662306a36Sopenharmony_ci		throughput = quota->total_charged_sz * 1000000 /
99762306a36Sopenharmony_ci			quota->total_charged_ns;
99862306a36Sopenharmony_ci	else
99962306a36Sopenharmony_ci		throughput = PAGE_SIZE * 1024;
100062306a36Sopenharmony_ci	esz = throughput * quota->ms;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (quota->sz && quota->sz < esz)
100362306a36Sopenharmony_ci		esz = quota->sz;
100462306a36Sopenharmony_ci	quota->esz = esz;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct damos_quota *quota = &s->quota;
101062306a36Sopenharmony_ci	struct damon_target *t;
101162306a36Sopenharmony_ci	struct damon_region *r;
101262306a36Sopenharmony_ci	unsigned long cumulated_sz;
101362306a36Sopenharmony_ci	unsigned int score, max_score = 0;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (!quota->ms && !quota->sz)
101662306a36Sopenharmony_ci		return;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* New charge window starts */
101962306a36Sopenharmony_ci	if (time_after_eq(jiffies, quota->charged_from +
102062306a36Sopenharmony_ci				msecs_to_jiffies(quota->reset_interval))) {
102162306a36Sopenharmony_ci		if (quota->esz && quota->charged_sz >= quota->esz)
102262306a36Sopenharmony_ci			s->stat.qt_exceeds++;
102362306a36Sopenharmony_ci		quota->total_charged_sz += quota->charged_sz;
102462306a36Sopenharmony_ci		quota->charged_from = jiffies;
102562306a36Sopenharmony_ci		quota->charged_sz = 0;
102662306a36Sopenharmony_ci		damos_set_effective_quota(quota);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (!c->ops.get_scheme_score)
103062306a36Sopenharmony_ci		return;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Fill up the score histogram */
103362306a36Sopenharmony_ci	memset(quota->histogram, 0, sizeof(quota->histogram));
103462306a36Sopenharmony_ci	damon_for_each_target(t, c) {
103562306a36Sopenharmony_ci		damon_for_each_region(r, t) {
103662306a36Sopenharmony_ci			if (!__damos_valid_target(r, s))
103762306a36Sopenharmony_ci				continue;
103862306a36Sopenharmony_ci			score = c->ops.get_scheme_score(c, t, r, s);
103962306a36Sopenharmony_ci			quota->histogram[score] += damon_sz_region(r);
104062306a36Sopenharmony_ci			if (score > max_score)
104162306a36Sopenharmony_ci				max_score = score;
104262306a36Sopenharmony_ci		}
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* Set the min score limit */
104662306a36Sopenharmony_ci	for (cumulated_sz = 0, score = max_score; ; score--) {
104762306a36Sopenharmony_ci		cumulated_sz += quota->histogram[score];
104862306a36Sopenharmony_ci		if (cumulated_sz >= quota->esz || !score)
104962306a36Sopenharmony_ci			break;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci	quota->min_score = score;
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic void kdamond_apply_schemes(struct damon_ctx *c)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct damon_target *t;
105762306a36Sopenharmony_ci	struct damon_region *r, *next_r;
105862306a36Sopenharmony_ci	struct damos *s;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	damon_for_each_scheme(s, c) {
106162306a36Sopenharmony_ci		if (!s->wmarks.activated)
106262306a36Sopenharmony_ci			continue;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		damos_adjust_quota(c, s);
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	damon_for_each_target(t, c) {
106862306a36Sopenharmony_ci		damon_for_each_region_safe(r, next_r, t)
106962306a36Sopenharmony_ci			damon_do_apply_schemes(c, t, r);
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/*
107462306a36Sopenharmony_ci * Merge two adjacent regions into one region
107562306a36Sopenharmony_ci */
107662306a36Sopenharmony_cistatic void damon_merge_two_regions(struct damon_target *t,
107762306a36Sopenharmony_ci		struct damon_region *l, struct damon_region *r)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	unsigned long sz_l = damon_sz_region(l), sz_r = damon_sz_region(r);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	l->nr_accesses = (l->nr_accesses * sz_l + r->nr_accesses * sz_r) /
108262306a36Sopenharmony_ci			(sz_l + sz_r);
108362306a36Sopenharmony_ci	l->age = (l->age * sz_l + r->age * sz_r) / (sz_l + sz_r);
108462306a36Sopenharmony_ci	l->ar.end = r->ar.end;
108562306a36Sopenharmony_ci	damon_destroy_region(r, t);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci/*
108962306a36Sopenharmony_ci * Merge adjacent regions having similar access frequencies
109062306a36Sopenharmony_ci *
109162306a36Sopenharmony_ci * t		target affected by this merge operation
109262306a36Sopenharmony_ci * thres	'->nr_accesses' diff threshold for the merge
109362306a36Sopenharmony_ci * sz_limit	size upper limit of each region
109462306a36Sopenharmony_ci */
109562306a36Sopenharmony_cistatic void damon_merge_regions_of(struct damon_target *t, unsigned int thres,
109662306a36Sopenharmony_ci				   unsigned long sz_limit)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	struct damon_region *r, *prev = NULL, *next;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	damon_for_each_region_safe(r, next, t) {
110162306a36Sopenharmony_ci		if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
110262306a36Sopenharmony_ci			r->age = 0;
110362306a36Sopenharmony_ci		else
110462306a36Sopenharmony_ci			r->age++;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci		if (prev && prev->ar.end == r->ar.start &&
110762306a36Sopenharmony_ci		    abs(prev->nr_accesses - r->nr_accesses) <= thres &&
110862306a36Sopenharmony_ci		    damon_sz_region(prev) + damon_sz_region(r) <= sz_limit)
110962306a36Sopenharmony_ci			damon_merge_two_regions(t, prev, r);
111062306a36Sopenharmony_ci		else
111162306a36Sopenharmony_ci			prev = r;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci/*
111662306a36Sopenharmony_ci * Merge adjacent regions having similar access frequencies
111762306a36Sopenharmony_ci *
111862306a36Sopenharmony_ci * threshold	'->nr_accesses' diff threshold for the merge
111962306a36Sopenharmony_ci * sz_limit	size upper limit of each region
112062306a36Sopenharmony_ci *
112162306a36Sopenharmony_ci * This function merges monitoring target regions which are adjacent and their
112262306a36Sopenharmony_ci * access frequencies are similar.  This is for minimizing the monitoring
112362306a36Sopenharmony_ci * overhead under the dynamically changeable access pattern.  If a merge was
112462306a36Sopenharmony_ci * unnecessarily made, later 'kdamond_split_regions()' will revert it.
112562306a36Sopenharmony_ci */
112662306a36Sopenharmony_cistatic void kdamond_merge_regions(struct damon_ctx *c, unsigned int threshold,
112762306a36Sopenharmony_ci				  unsigned long sz_limit)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct damon_target *t;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	damon_for_each_target(t, c)
113262306a36Sopenharmony_ci		damon_merge_regions_of(t, threshold, sz_limit);
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci/*
113662306a36Sopenharmony_ci * Split a region in two
113762306a36Sopenharmony_ci *
113862306a36Sopenharmony_ci * r		the region to be split
113962306a36Sopenharmony_ci * sz_r		size of the first sub-region that will be made
114062306a36Sopenharmony_ci */
114162306a36Sopenharmony_cistatic void damon_split_region_at(struct damon_target *t,
114262306a36Sopenharmony_ci				  struct damon_region *r, unsigned long sz_r)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	struct damon_region *new;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	new = damon_new_region(r->ar.start + sz_r, r->ar.end);
114762306a36Sopenharmony_ci	if (!new)
114862306a36Sopenharmony_ci		return;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	r->ar.end = new->ar.start;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	new->age = r->age;
115362306a36Sopenharmony_ci	new->last_nr_accesses = r->last_nr_accesses;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	damon_insert_region(new, r, damon_next_region(r), t);
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci/* Split every region in the given target into 'nr_subs' regions */
115962306a36Sopenharmony_cistatic void damon_split_regions_of(struct damon_target *t, int nr_subs)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct damon_region *r, *next;
116262306a36Sopenharmony_ci	unsigned long sz_region, sz_sub = 0;
116362306a36Sopenharmony_ci	int i;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	damon_for_each_region_safe(r, next, t) {
116662306a36Sopenharmony_ci		sz_region = damon_sz_region(r);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		for (i = 0; i < nr_subs - 1 &&
116962306a36Sopenharmony_ci				sz_region > 2 * DAMON_MIN_REGION; i++) {
117062306a36Sopenharmony_ci			/*
117162306a36Sopenharmony_ci			 * Randomly select size of left sub-region to be at
117262306a36Sopenharmony_ci			 * least 10 percent and at most 90% of original region
117362306a36Sopenharmony_ci			 */
117462306a36Sopenharmony_ci			sz_sub = ALIGN_DOWN(damon_rand(1, 10) *
117562306a36Sopenharmony_ci					sz_region / 10, DAMON_MIN_REGION);
117662306a36Sopenharmony_ci			/* Do not allow blank region */
117762306a36Sopenharmony_ci			if (sz_sub == 0 || sz_sub >= sz_region)
117862306a36Sopenharmony_ci				continue;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci			damon_split_region_at(t, r, sz_sub);
118162306a36Sopenharmony_ci			sz_region = sz_sub;
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci/*
118762306a36Sopenharmony_ci * Split every target region into randomly-sized small regions
118862306a36Sopenharmony_ci *
118962306a36Sopenharmony_ci * This function splits every target region into random-sized small regions if
119062306a36Sopenharmony_ci * current total number of the regions is equal or smaller than half of the
119162306a36Sopenharmony_ci * user-specified maximum number of regions.  This is for maximizing the
119262306a36Sopenharmony_ci * monitoring accuracy under the dynamically changeable access patterns.  If a
119362306a36Sopenharmony_ci * split was unnecessarily made, later 'kdamond_merge_regions()' will revert
119462306a36Sopenharmony_ci * it.
119562306a36Sopenharmony_ci */
119662306a36Sopenharmony_cistatic void kdamond_split_regions(struct damon_ctx *ctx)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct damon_target *t;
119962306a36Sopenharmony_ci	unsigned int nr_regions = 0;
120062306a36Sopenharmony_ci	static unsigned int last_nr_regions;
120162306a36Sopenharmony_ci	int nr_subregions = 2;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	damon_for_each_target(t, ctx)
120462306a36Sopenharmony_ci		nr_regions += damon_nr_regions(t);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (nr_regions > ctx->attrs.max_nr_regions / 2)
120762306a36Sopenharmony_ci		return;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* Maybe the middle of the region has different access frequency */
121062306a36Sopenharmony_ci	if (last_nr_regions == nr_regions &&
121162306a36Sopenharmony_ci			nr_regions < ctx->attrs.max_nr_regions / 3)
121262306a36Sopenharmony_ci		nr_subregions = 3;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	damon_for_each_target(t, ctx)
121562306a36Sopenharmony_ci		damon_split_regions_of(t, nr_subregions);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	last_nr_regions = nr_regions;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci/*
122162306a36Sopenharmony_ci * Check whether current monitoring should be stopped
122262306a36Sopenharmony_ci *
122362306a36Sopenharmony_ci * The monitoring is stopped when either the user requested to stop, or all
122462306a36Sopenharmony_ci * monitoring targets are invalid.
122562306a36Sopenharmony_ci *
122662306a36Sopenharmony_ci * Returns true if need to stop current monitoring.
122762306a36Sopenharmony_ci */
122862306a36Sopenharmony_cistatic bool kdamond_need_stop(struct damon_ctx *ctx)
122962306a36Sopenharmony_ci{
123062306a36Sopenharmony_ci	struct damon_target *t;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if (kthread_should_stop())
123362306a36Sopenharmony_ci		return true;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (!ctx->ops.target_valid)
123662306a36Sopenharmony_ci		return false;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	damon_for_each_target(t, ctx) {
123962306a36Sopenharmony_ci		if (ctx->ops.target_valid(t))
124062306a36Sopenharmony_ci			return false;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return true;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic unsigned long damos_wmark_metric_value(enum damos_wmark_metric metric)
124762306a36Sopenharmony_ci{
124862306a36Sopenharmony_ci	struct sysinfo i;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	switch (metric) {
125162306a36Sopenharmony_ci	case DAMOS_WMARK_FREE_MEM_RATE:
125262306a36Sopenharmony_ci		si_meminfo(&i);
125362306a36Sopenharmony_ci		return i.freeram * 1000 / i.totalram;
125462306a36Sopenharmony_ci	default:
125562306a36Sopenharmony_ci		break;
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci	return -EINVAL;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci/*
126162306a36Sopenharmony_ci * Returns zero if the scheme is active.  Else, returns time to wait for next
126262306a36Sopenharmony_ci * watermark check in micro-seconds.
126362306a36Sopenharmony_ci */
126462306a36Sopenharmony_cistatic unsigned long damos_wmark_wait_us(struct damos *scheme)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	unsigned long metric;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (scheme->wmarks.metric == DAMOS_WMARK_NONE)
126962306a36Sopenharmony_ci		return 0;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	metric = damos_wmark_metric_value(scheme->wmarks.metric);
127262306a36Sopenharmony_ci	/* higher than high watermark or lower than low watermark */
127362306a36Sopenharmony_ci	if (metric > scheme->wmarks.high || scheme->wmarks.low > metric) {
127462306a36Sopenharmony_ci		if (scheme->wmarks.activated)
127562306a36Sopenharmony_ci			pr_debug("deactivate a scheme (%d) for %s wmark\n",
127662306a36Sopenharmony_ci					scheme->action,
127762306a36Sopenharmony_ci					metric > scheme->wmarks.high ?
127862306a36Sopenharmony_ci					"high" : "low");
127962306a36Sopenharmony_ci		scheme->wmarks.activated = false;
128062306a36Sopenharmony_ci		return scheme->wmarks.interval;
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* inactive and higher than middle watermark */
128462306a36Sopenharmony_ci	if ((scheme->wmarks.high >= metric && metric >= scheme->wmarks.mid) &&
128562306a36Sopenharmony_ci			!scheme->wmarks.activated)
128662306a36Sopenharmony_ci		return scheme->wmarks.interval;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (!scheme->wmarks.activated)
128962306a36Sopenharmony_ci		pr_debug("activate a scheme (%d)\n", scheme->action);
129062306a36Sopenharmony_ci	scheme->wmarks.activated = true;
129162306a36Sopenharmony_ci	return 0;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic void kdamond_usleep(unsigned long usecs)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	/* See Documentation/timers/timers-howto.rst for the thresholds */
129762306a36Sopenharmony_ci	if (usecs > 20 * USEC_PER_MSEC)
129862306a36Sopenharmony_ci		schedule_timeout_idle(usecs_to_jiffies(usecs));
129962306a36Sopenharmony_ci	else
130062306a36Sopenharmony_ci		usleep_idle_range(usecs, usecs + 1);
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/* Returns negative error code if it's not activated but should return */
130462306a36Sopenharmony_cistatic int kdamond_wait_activation(struct damon_ctx *ctx)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	struct damos *s;
130762306a36Sopenharmony_ci	unsigned long wait_time;
130862306a36Sopenharmony_ci	unsigned long min_wait_time = 0;
130962306a36Sopenharmony_ci	bool init_wait_time = false;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	while (!kdamond_need_stop(ctx)) {
131262306a36Sopenharmony_ci		damon_for_each_scheme(s, ctx) {
131362306a36Sopenharmony_ci			wait_time = damos_wmark_wait_us(s);
131462306a36Sopenharmony_ci			if (!init_wait_time || wait_time < min_wait_time) {
131562306a36Sopenharmony_ci				init_wait_time = true;
131662306a36Sopenharmony_ci				min_wait_time = wait_time;
131762306a36Sopenharmony_ci			}
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci		if (!min_wait_time)
132062306a36Sopenharmony_ci			return 0;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci		kdamond_usleep(min_wait_time);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		if (ctx->callback.after_wmarks_check &&
132562306a36Sopenharmony_ci				ctx->callback.after_wmarks_check(ctx))
132662306a36Sopenharmony_ci			break;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci	return -EBUSY;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic void kdamond_init_intervals_sis(struct damon_ctx *ctx)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	unsigned long sample_interval = ctx->attrs.sample_interval ?
133462306a36Sopenharmony_ci		ctx->attrs.sample_interval : 1;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	ctx->passed_sample_intervals = 0;
133762306a36Sopenharmony_ci	ctx->next_aggregation_sis = ctx->attrs.aggr_interval / sample_interval;
133862306a36Sopenharmony_ci	ctx->next_ops_update_sis = ctx->attrs.ops_update_interval /
133962306a36Sopenharmony_ci		sample_interval;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/*
134362306a36Sopenharmony_ci * The monitoring daemon that runs as a kernel thread
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_cistatic int kdamond_fn(void *data)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct damon_ctx *ctx = data;
134862306a36Sopenharmony_ci	struct damon_target *t;
134962306a36Sopenharmony_ci	struct damon_region *r, *next;
135062306a36Sopenharmony_ci	unsigned int max_nr_accesses = 0;
135162306a36Sopenharmony_ci	unsigned long sz_limit = 0;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	pr_debug("kdamond (%d) starts\n", current->pid);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	complete(&ctx->kdamond_started);
135662306a36Sopenharmony_ci	kdamond_init_intervals_sis(ctx);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	if (ctx->ops.init)
135962306a36Sopenharmony_ci		ctx->ops.init(ctx);
136062306a36Sopenharmony_ci	if (ctx->callback.before_start && ctx->callback.before_start(ctx))
136162306a36Sopenharmony_ci		goto done;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	sz_limit = damon_region_sz_limit(ctx);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	while (!kdamond_need_stop(ctx)) {
136662306a36Sopenharmony_ci		/*
136762306a36Sopenharmony_ci		 * ctx->attrs and ctx->next_{aggregation,ops_update}_sis could
136862306a36Sopenharmony_ci		 * be changed from after_wmarks_check() or after_aggregation()
136962306a36Sopenharmony_ci		 * callbacks.  Read the values here, and use those for this
137062306a36Sopenharmony_ci		 * iteration.  That is, damon_set_attrs() updated new values
137162306a36Sopenharmony_ci		 * are respected from next iteration.
137262306a36Sopenharmony_ci		 */
137362306a36Sopenharmony_ci		unsigned long next_aggregation_sis = ctx->next_aggregation_sis;
137462306a36Sopenharmony_ci		unsigned long next_ops_update_sis = ctx->next_ops_update_sis;
137562306a36Sopenharmony_ci		unsigned long sample_interval = ctx->attrs.sample_interval;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci		if (kdamond_wait_activation(ctx))
137862306a36Sopenharmony_ci			break;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		if (ctx->ops.prepare_access_checks)
138162306a36Sopenharmony_ci			ctx->ops.prepare_access_checks(ctx);
138262306a36Sopenharmony_ci		if (ctx->callback.after_sampling &&
138362306a36Sopenharmony_ci				ctx->callback.after_sampling(ctx))
138462306a36Sopenharmony_ci			break;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci		kdamond_usleep(sample_interval);
138762306a36Sopenharmony_ci		ctx->passed_sample_intervals++;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		if (ctx->ops.check_accesses)
139062306a36Sopenharmony_ci			max_nr_accesses = ctx->ops.check_accesses(ctx);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		sample_interval = ctx->attrs.sample_interval ?
139362306a36Sopenharmony_ci			ctx->attrs.sample_interval : 1;
139462306a36Sopenharmony_ci		if (ctx->passed_sample_intervals == next_aggregation_sis) {
139562306a36Sopenharmony_ci			ctx->next_aggregation_sis = next_aggregation_sis +
139662306a36Sopenharmony_ci				ctx->attrs.aggr_interval / sample_interval;
139762306a36Sopenharmony_ci			kdamond_merge_regions(ctx,
139862306a36Sopenharmony_ci					max_nr_accesses / 10,
139962306a36Sopenharmony_ci					sz_limit);
140062306a36Sopenharmony_ci			if (ctx->callback.after_aggregation &&
140162306a36Sopenharmony_ci					ctx->callback.after_aggregation(ctx))
140262306a36Sopenharmony_ci				break;
140362306a36Sopenharmony_ci			if (!list_empty(&ctx->schemes))
140462306a36Sopenharmony_ci				kdamond_apply_schemes(ctx);
140562306a36Sopenharmony_ci			kdamond_reset_aggregated(ctx);
140662306a36Sopenharmony_ci			kdamond_split_regions(ctx);
140762306a36Sopenharmony_ci			if (ctx->ops.reset_aggregated)
140862306a36Sopenharmony_ci				ctx->ops.reset_aggregated(ctx);
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		if (ctx->passed_sample_intervals == next_ops_update_sis) {
141262306a36Sopenharmony_ci			ctx->next_ops_update_sis = next_ops_update_sis +
141362306a36Sopenharmony_ci				ctx->attrs.ops_update_interval /
141462306a36Sopenharmony_ci				sample_interval;
141562306a36Sopenharmony_ci			if (ctx->ops.update)
141662306a36Sopenharmony_ci				ctx->ops.update(ctx);
141762306a36Sopenharmony_ci			sz_limit = damon_region_sz_limit(ctx);
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_cidone:
142162306a36Sopenharmony_ci	damon_for_each_target(t, ctx) {
142262306a36Sopenharmony_ci		damon_for_each_region_safe(r, next, t)
142362306a36Sopenharmony_ci			damon_destroy_region(r, t);
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (ctx->callback.before_terminate)
142762306a36Sopenharmony_ci		ctx->callback.before_terminate(ctx);
142862306a36Sopenharmony_ci	if (ctx->ops.cleanup)
142962306a36Sopenharmony_ci		ctx->ops.cleanup(ctx);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	pr_debug("kdamond (%d) finishes\n", current->pid);
143262306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
143362306a36Sopenharmony_ci	ctx->kdamond = NULL;
143462306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	mutex_lock(&damon_lock);
143762306a36Sopenharmony_ci	nr_running_ctxs--;
143862306a36Sopenharmony_ci	if (!nr_running_ctxs && running_exclusive_ctxs)
143962306a36Sopenharmony_ci		running_exclusive_ctxs = false;
144062306a36Sopenharmony_ci	mutex_unlock(&damon_lock);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	return 0;
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci/*
144662306a36Sopenharmony_ci * struct damon_system_ram_region - System RAM resource address region of
144762306a36Sopenharmony_ci *				    [@start, @end).
144862306a36Sopenharmony_ci * @start:	Start address of the region (inclusive).
144962306a36Sopenharmony_ci * @end:	End address of the region (exclusive).
145062306a36Sopenharmony_ci */
145162306a36Sopenharmony_cistruct damon_system_ram_region {
145262306a36Sopenharmony_ci	unsigned long start;
145362306a36Sopenharmony_ci	unsigned long end;
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistatic int walk_system_ram(struct resource *res, void *arg)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	struct damon_system_ram_region *a = arg;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (a->end - a->start < resource_size(res)) {
146162306a36Sopenharmony_ci		a->start = res->start;
146262306a36Sopenharmony_ci		a->end = res->end;
146362306a36Sopenharmony_ci	}
146462306a36Sopenharmony_ci	return 0;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci/*
146862306a36Sopenharmony_ci * Find biggest 'System RAM' resource and store its start and end address in
146962306a36Sopenharmony_ci * @start and @end, respectively.  If no System RAM is found, returns false.
147062306a36Sopenharmony_ci */
147162306a36Sopenharmony_cistatic bool damon_find_biggest_system_ram(unsigned long *start,
147262306a36Sopenharmony_ci						unsigned long *end)
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct damon_system_ram_region arg = {};
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	walk_system_ram_res(0, ULONG_MAX, &arg, walk_system_ram);
147862306a36Sopenharmony_ci	if (arg.end <= arg.start)
147962306a36Sopenharmony_ci		return false;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	*start = arg.start;
148262306a36Sopenharmony_ci	*end = arg.end;
148362306a36Sopenharmony_ci	return true;
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/**
148762306a36Sopenharmony_ci * damon_set_region_biggest_system_ram_default() - Set the region of the given
148862306a36Sopenharmony_ci * monitoring target as requested, or biggest 'System RAM'.
148962306a36Sopenharmony_ci * @t:		The monitoring target to set the region.
149062306a36Sopenharmony_ci * @start:	The pointer to the start address of the region.
149162306a36Sopenharmony_ci * @end:	The pointer to the end address of the region.
149262306a36Sopenharmony_ci *
149362306a36Sopenharmony_ci * This function sets the region of @t as requested by @start and @end.  If the
149462306a36Sopenharmony_ci * values of @start and @end are zero, however, this function finds the biggest
149562306a36Sopenharmony_ci * 'System RAM' resource and sets the region to cover the resource.  In the
149662306a36Sopenharmony_ci * latter case, this function saves the start and end addresses of the resource
149762306a36Sopenharmony_ci * in @start and @end, respectively.
149862306a36Sopenharmony_ci *
149962306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
150062306a36Sopenharmony_ci */
150162306a36Sopenharmony_ciint damon_set_region_biggest_system_ram_default(struct damon_target *t,
150262306a36Sopenharmony_ci			unsigned long *start, unsigned long *end)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct damon_addr_range addr_range;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (*start > *end)
150762306a36Sopenharmony_ci		return -EINVAL;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	if (!*start && !*end &&
151062306a36Sopenharmony_ci		!damon_find_biggest_system_ram(start, end))
151162306a36Sopenharmony_ci		return -EINVAL;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	addr_range.start = *start;
151462306a36Sopenharmony_ci	addr_range.end = *end;
151562306a36Sopenharmony_ci	return damon_set_regions(t, &addr_range, 1);
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cistatic int __init damon_init(void)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	damon_region_cache = KMEM_CACHE(damon_region, 0);
152162306a36Sopenharmony_ci	if (unlikely(!damon_region_cache)) {
152262306a36Sopenharmony_ci		pr_err("creating damon_region_cache fails\n");
152362306a36Sopenharmony_ci		return -ENOMEM;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	return 0;
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cisubsys_initcall(damon_init);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci#include "core-test.h"
1532