162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common Primitives for Data Access Monitoring 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: SeongJae Park <sj@kernel.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mmu_notifier.h> 962306a36Sopenharmony_ci#include <linux/page_idle.h> 1062306a36Sopenharmony_ci#include <linux/pagemap.h> 1162306a36Sopenharmony_ci#include <linux/rmap.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "ops-common.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Get an online page for a pfn if it's in the LRU list. Otherwise, returns 1762306a36Sopenharmony_ci * NULL. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * The body of this function is stolen from the 'page_idle_get_folio()'. We 2062306a36Sopenharmony_ci * steal rather than reuse it because the code is quite simple. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistruct folio *damon_get_folio(unsigned long pfn) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct page *page = pfn_to_online_page(pfn); 2562306a36Sopenharmony_ci struct folio *folio; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (!page || PageTail(page)) 2862306a36Sopenharmony_ci return NULL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci folio = page_folio(page); 3162306a36Sopenharmony_ci if (!folio_test_lru(folio) || !folio_try_get(folio)) 3262306a36Sopenharmony_ci return NULL; 3362306a36Sopenharmony_ci if (unlikely(page_folio(page) != folio || !folio_test_lru(folio))) { 3462306a36Sopenharmony_ci folio_put(folio); 3562306a36Sopenharmony_ci folio = NULL; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci return folio; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_civoid damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct folio *folio = damon_get_folio(pte_pfn(ptep_get(pte))); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (!folio) 4562306a36Sopenharmony_ci return; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (ptep_clear_young_notify(vma, addr, pte)) 4862306a36Sopenharmony_ci folio_set_young(folio); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci folio_set_idle(folio); 5162306a36Sopenharmony_ci folio_put(folio); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_civoid damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 5762306a36Sopenharmony_ci struct folio *folio = damon_get_folio(pmd_pfn(pmdp_get(pmd))); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!folio) 6062306a36Sopenharmony_ci return; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (pmdp_clear_young_notify(vma, addr, pmd)) 6362306a36Sopenharmony_ci folio_set_young(folio); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci folio_set_idle(folio); 6662306a36Sopenharmony_ci folio_put(folio); 6762306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define DAMON_MAX_SUBSCORE (100) 7162306a36Sopenharmony_ci#define DAMON_MAX_AGE_IN_LOG (32) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint damon_hot_score(struct damon_ctx *c, struct damon_region *r, 7462306a36Sopenharmony_ci struct damos *s) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int freq_subscore; 7762306a36Sopenharmony_ci unsigned int age_in_sec; 7862306a36Sopenharmony_ci int age_in_log, age_subscore; 7962306a36Sopenharmony_ci unsigned int freq_weight = s->quota.weight_nr_accesses; 8062306a36Sopenharmony_ci unsigned int age_weight = s->quota.weight_age; 8162306a36Sopenharmony_ci int hotness; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci freq_subscore = r->nr_accesses * DAMON_MAX_SUBSCORE / 8462306a36Sopenharmony_ci damon_max_nr_accesses(&c->attrs); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci age_in_sec = (unsigned long)r->age * c->attrs.aggr_interval / 1000000; 8762306a36Sopenharmony_ci for (age_in_log = 0; age_in_log < DAMON_MAX_AGE_IN_LOG && age_in_sec; 8862306a36Sopenharmony_ci age_in_log++, age_in_sec >>= 1) 8962306a36Sopenharmony_ci ; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* If frequency is 0, higher age means it's colder */ 9262306a36Sopenharmony_ci if (freq_subscore == 0) 9362306a36Sopenharmony_ci age_in_log *= -1; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Now age_in_log is in [-DAMON_MAX_AGE_IN_LOG, DAMON_MAX_AGE_IN_LOG]. 9762306a36Sopenharmony_ci * Scale it to be in [0, 100] and set it as age subscore. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci age_in_log += DAMON_MAX_AGE_IN_LOG; 10062306a36Sopenharmony_ci age_subscore = age_in_log * DAMON_MAX_SUBSCORE / 10162306a36Sopenharmony_ci DAMON_MAX_AGE_IN_LOG / 2; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci hotness = (freq_weight * freq_subscore + age_weight * age_subscore); 10462306a36Sopenharmony_ci if (freq_weight + age_weight) 10562306a36Sopenharmony_ci hotness /= freq_weight + age_weight; 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Transform it to fit in [0, DAMOS_MAX_SCORE] 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return hotness; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciint damon_cold_score(struct damon_ctx *c, struct damon_region *r, 11562306a36Sopenharmony_ci struct damos *s) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int hotness = damon_hot_score(c, r, s); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Return coldness of the region */ 12062306a36Sopenharmony_ci return DAMOS_MAX_SCORE - hotness; 12162306a36Sopenharmony_ci} 122