1 // SPDX-License-Identifier: GPL-2.0
3 * Common Primitives for Data Access Monitoring
5 * Author: SeongJae Park <sj@kernel.org>
8 #include <linux/mmu_notifier.h>
9 #include <linux/page_idle.h>
10 #include <linux/pagemap.h>
11 #include <linux/rmap.h>
13 #include "ops-common.h"
16 * Get an online page for a pfn if it's in the LRU list. Otherwise, returns
19 * The body of this function is stolen from the 'page_idle_get_folio()'. We
20 * steal rather than reuse it because the code is quite simple.
22 struct folio
*damon_get_folio(unsigned long pfn
)
24 struct page
*page
= pfn_to_online_page(pfn
);
27 if (!page
|| PageTail(page
))
30 folio
= page_folio(page
);
31 if (!folio_test_lru(folio
) || !folio_try_get(folio
))
33 if (unlikely(page_folio(page
) != folio
|| !folio_test_lru(folio
))) {
40 void damon_ptep_mkold(pte_t
*pte
, struct vm_area_struct
*vma
, unsigned long addr
)
42 struct folio
*folio
= damon_get_folio(pte_pfn(ptep_get(pte
)));
47 if (ptep_clear_young_notify(vma
, addr
, pte
))
48 folio_set_young(folio
);
50 folio_set_idle(folio
);
54 void damon_pmdp_mkold(pmd_t
*pmd
, struct vm_area_struct
*vma
, unsigned long addr
)
56 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
57 struct folio
*folio
= damon_get_folio(pmd_pfn(pmdp_get(pmd
)));
62 if (pmdp_clear_young_notify(vma
, addr
, pmd
))
63 folio_set_young(folio
);
65 folio_set_idle(folio
);
67 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
70 #define DAMON_MAX_SUBSCORE (100)
71 #define DAMON_MAX_AGE_IN_LOG (32)
73 int damon_hot_score(struct damon_ctx
*c
, struct damon_region
*r
,
77 unsigned int age_in_sec
;
78 int age_in_log
, age_subscore
;
79 unsigned int freq_weight
= s
->quota
.weight_nr_accesses
;
80 unsigned int age_weight
= s
->quota
.weight_age
;
83 freq_subscore
= r
->nr_accesses
* DAMON_MAX_SUBSCORE
/
84 damon_max_nr_accesses(&c
->attrs
);
86 age_in_sec
= (unsigned long)r
->age
* c
->attrs
.aggr_interval
/ 1000000;
87 for (age_in_log
= 0; age_in_log
< DAMON_MAX_AGE_IN_LOG
&& age_in_sec
;
88 age_in_log
++, age_in_sec
>>= 1)
91 /* If frequency is 0, higher age means it's colder */
92 if (freq_subscore
== 0)
96 * Now age_in_log is in [-DAMON_MAX_AGE_IN_LOG, DAMON_MAX_AGE_IN_LOG].
97 * Scale it to be in [0, 100] and set it as age subscore.
99 age_in_log
+= DAMON_MAX_AGE_IN_LOG
;
100 age_subscore
= age_in_log
* DAMON_MAX_SUBSCORE
/
101 DAMON_MAX_AGE_IN_LOG
/ 2;
103 hotness
= (freq_weight
* freq_subscore
+ age_weight
* age_subscore
);
104 if (freq_weight
+ age_weight
)
105 hotness
/= freq_weight
+ age_weight
;
107 * Transform it to fit in [0, DAMOS_MAX_SCORE]
109 hotness
= hotness
* DAMOS_MAX_SCORE
/ DAMON_MAX_SUBSCORE
;
114 int damon_cold_score(struct damon_ctx
*c
, struct damon_region
*r
,
117 int hotness
= damon_hot_score(c
, r
, s
);
119 /* Return coldness of the region */
120 return DAMOS_MAX_SCORE
- hotness
;