forked from Minki/linux
f7d911c39c
Patch series "Allow DAMON user code independent of monitoring primitives". In-kernel DAMON user code is required to configure the monitoring context (struct damon_ctx) with proper monitoring primitives (struct damon_primitive). This makes the user code dependent to all supporting monitoring primitives. For example, DAMON debugfs interface depends on both DAMON_VADDR and DAMON_PADDR, though some users have interest in only one use case. As more monitoring primitives are introduced, the problem will be bigger. To minimize such unnecessary dependency, this patchset makes monitoring primitives can be registered by the implemnting code and later dynamically searched and selected by the user code. In addition to that, this patchset renames monitoring primitives to monitoring operations, which is more easy to intuitively understand what it means and how it would be structed. This patch (of 8): DAMON has a set of callback functions called monitoring primitives and let it can be configured with various implementations for easy extension for different address spaces and usages. However, the word 'primitive' is not so explicit. Meanwhile, many other structs resembles similar purpose calls themselves 'operations'. To make the code easier to be understood, this commit renames 'damon_primitives' to 'damon_operations' before it is too late to rename. Link: https://lkml.kernel.org/r/20220215184603.1479-1-sj@kernel.org Link: https://lkml.kernel.org/r/20220215184603.1479-2-sj@kernel.org Signed-off-by: SeongJae Park <sj@kernel.org> Cc: Xin Hao <xhao@linux.alibaba.com> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
134 lines
3.1 KiB
C
134 lines
3.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Common Primitives for Data Access Monitoring
|
|
*
|
|
* Author: SeongJae Park <sj@kernel.org>
|
|
*/
|
|
|
|
#include <linux/mmu_notifier.h>
|
|
#include <linux/page_idle.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/rmap.h>
|
|
|
|
#include "ops-common.h"
|
|
|
|
/*
|
|
* Get an online page for a pfn if it's in the LRU list. Otherwise, returns
|
|
* NULL.
|
|
*
|
|
* The body of this function is stolen from the 'page_idle_get_page()'. We
|
|
* steal rather than reuse it because the code is quite simple.
|
|
*/
|
|
struct page *damon_get_page(unsigned long pfn)
|
|
{
|
|
struct page *page = pfn_to_online_page(pfn);
|
|
|
|
if (!page || !PageLRU(page) || !get_page_unless_zero(page))
|
|
return NULL;
|
|
|
|
if (unlikely(!PageLRU(page))) {
|
|
put_page(page);
|
|
page = NULL;
|
|
}
|
|
return page;
|
|
}
|
|
|
|
void damon_ptep_mkold(pte_t *pte, struct mm_struct *mm, unsigned long addr)
|
|
{
|
|
bool referenced = false;
|
|
struct page *page = damon_get_page(pte_pfn(*pte));
|
|
|
|
if (!page)
|
|
return;
|
|
|
|
if (pte_young(*pte)) {
|
|
referenced = true;
|
|
*pte = pte_mkold(*pte);
|
|
}
|
|
|
|
#ifdef CONFIG_MMU_NOTIFIER
|
|
if (mmu_notifier_clear_young(mm, addr, addr + PAGE_SIZE))
|
|
referenced = true;
|
|
#endif /* CONFIG_MMU_NOTIFIER */
|
|
|
|
if (referenced)
|
|
set_page_young(page);
|
|
|
|
set_page_idle(page);
|
|
put_page(page);
|
|
}
|
|
|
|
void damon_pmdp_mkold(pmd_t *pmd, struct mm_struct *mm, unsigned long addr)
|
|
{
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
bool referenced = false;
|
|
struct page *page = damon_get_page(pmd_pfn(*pmd));
|
|
|
|
if (!page)
|
|
return;
|
|
|
|
if (pmd_young(*pmd)) {
|
|
referenced = true;
|
|
*pmd = pmd_mkold(*pmd);
|
|
}
|
|
|
|
#ifdef CONFIG_MMU_NOTIFIER
|
|
if (mmu_notifier_clear_young(mm, addr,
|
|
addr + ((1UL) << HPAGE_PMD_SHIFT)))
|
|
referenced = true;
|
|
#endif /* CONFIG_MMU_NOTIFIER */
|
|
|
|
if (referenced)
|
|
set_page_young(page);
|
|
|
|
set_page_idle(page);
|
|
put_page(page);
|
|
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
|
}
|
|
|
|
#define DAMON_MAX_SUBSCORE (100)
|
|
#define DAMON_MAX_AGE_IN_LOG (32)
|
|
|
|
int damon_pageout_score(struct damon_ctx *c, struct damon_region *r,
|
|
struct damos *s)
|
|
{
|
|
unsigned int max_nr_accesses;
|
|
int freq_subscore;
|
|
unsigned int age_in_sec;
|
|
int age_in_log, age_subscore;
|
|
unsigned int freq_weight = s->quota.weight_nr_accesses;
|
|
unsigned int age_weight = s->quota.weight_age;
|
|
int hotness;
|
|
|
|
max_nr_accesses = c->aggr_interval / c->sample_interval;
|
|
freq_subscore = r->nr_accesses * DAMON_MAX_SUBSCORE / max_nr_accesses;
|
|
|
|
age_in_sec = (unsigned long)r->age * c->aggr_interval / 1000000;
|
|
for (age_in_log = 0; age_in_log < DAMON_MAX_AGE_IN_LOG && age_in_sec;
|
|
age_in_log++, age_in_sec >>= 1)
|
|
;
|
|
|
|
/* If frequency is 0, higher age means it's colder */
|
|
if (freq_subscore == 0)
|
|
age_in_log *= -1;
|
|
|
|
/*
|
|
* Now age_in_log is in [-DAMON_MAX_AGE_IN_LOG, DAMON_MAX_AGE_IN_LOG].
|
|
* Scale it to be in [0, 100] and set it as age subscore.
|
|
*/
|
|
age_in_log += DAMON_MAX_AGE_IN_LOG;
|
|
age_subscore = age_in_log * DAMON_MAX_SUBSCORE /
|
|
DAMON_MAX_AGE_IN_LOG / 2;
|
|
|
|
hotness = (freq_weight * freq_subscore + age_weight * age_subscore);
|
|
if (freq_weight + age_weight)
|
|
hotness /= freq_weight + age_weight;
|
|
/*
|
|
* Transform it to fit in [0, DAMOS_MAX_SCORE]
|
|
*/
|
|
hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE;
|
|
|
|
/* Return coldness of the region */
|
|
return DAMOS_MAX_SCORE - hotness;
|
|
}
|