xref: /linux/mm/damon/ops-common.c (revision 7db28abbea0f7dc1ec4fdfdc149db5fbd9e4c994)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Common Code for Data Access Monitoring
4  */
5 
6 #include <linux/migrate.h>
7 #include <linux/mmu_notifier.h>
8 #include <linux/page_idle.h>
9 #include <linux/pagemap.h>
10 #include <linux/rmap.h>
11 #include <linux/swap.h>
12 #include <linux/leafops.h>
13 
14 #include "../internal.h"
15 #include "ops-common.h"
16 
17 /*
18  * Get an online page for a pfn if it's in the LRU list.  Otherwise, returns
19  * NULL.
20  *
21  * The body of this function is stolen from the 'page_idle_get_folio()'.  We
22  * steal rather than reuse it because the code is quite simple.
23  */
24 struct folio *damon_get_folio(unsigned long pfn)
25 {
26 	struct page *page = pfn_to_online_page(pfn);
27 	struct folio *folio;
28 
29 	if (!page)
30 		return NULL;
31 
32 	folio = page_folio(page);
33 	if (!folio_try_get(folio))
34 		return NULL;
35 	if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) {
36 		folio_put(folio);
37 		folio = NULL;
38 	}
39 	return folio;
40 }
41 
42 void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr)
43 {
44 	pte_t pteval = ptep_get(pte);
45 	struct folio *folio;
46 	bool young = false;
47 	unsigned long pfn;
48 
49 	if (likely(pte_present(pteval)))
50 		pfn = pte_pfn(pteval);
51 	else
52 		pfn = softleaf_to_pfn(softleaf_from_pte(pteval));
53 
54 	folio = damon_get_folio(pfn);
55 	if (!folio)
56 		return;
57 
58 	/*
59 	 * PFN swap PTEs, such as device-exclusive ones, that actually map pages
60 	 * are "old" from a CPU perspective. The MMU notifier takes care of any
61 	 * device aspects.
62 	 */
63 	if (likely(pte_present(pteval)))
64 		young |= ptep_test_and_clear_young(vma, addr, pte);
65 	young |= mmu_notifier_clear_young(vma->vm_mm, addr, addr + PAGE_SIZE);
66 	if (young)
67 		folio_set_young(folio);
68 
69 	folio_set_idle(folio);
70 	folio_put(folio);
71 }
72 
73 void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr)
74 {
75 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
76 	pmd_t pmdval = pmdp_get(pmd);
77 	struct folio *folio;
78 	bool young = false;
79 	unsigned long pfn;
80 
81 	if (likely(pmd_present(pmdval)))
82 		pfn = pmd_pfn(pmdval);
83 	else
84 		pfn = softleaf_to_pfn(softleaf_from_pmd(pmdval));
85 
86 	folio = damon_get_folio(pfn);
87 	if (!folio)
88 		return;
89 
90 	if (likely(pmd_present(pmdval)))
91 		young |= pmdp_test_and_clear_young(vma, addr, pmd);
92 	young |= mmu_notifier_clear_young(vma->vm_mm, addr, addr + HPAGE_PMD_SIZE);
93 	if (young)
94 		folio_set_young(folio);
95 
96 	folio_set_idle(folio);
97 	folio_put(folio);
98 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
99 }
100 
101 #define DAMON_MAX_SUBSCORE	(100)
102 #define DAMON_MAX_AGE_IN_LOG	(32)
103 
104 int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
105 			struct damos *s)
106 {
107 	int freq_subscore;
108 	unsigned int age_in_sec;
109 	int age_in_log, age_subscore;
110 	unsigned int freq_weight = s->quota.weight_nr_accesses;
111 	unsigned int age_weight = s->quota.weight_age;
112 	int hotness;
113 
114 	freq_subscore = mult_frac(damon_nr_accesses_mvsum(r, c),
115 			DAMON_MAX_SUBSCORE,
116 			damon_nr_samples_per_aggr(&c->attrs));
117 
118 	age_in_sec = (unsigned long)r->age * c->attrs.aggr_interval / 1000000;
119 	if (age_in_sec)
120 		age_in_log = min_t(int, ilog2(age_in_sec) + 1,
121 				DAMON_MAX_AGE_IN_LOG);
122 	else
123 		age_in_log = 0;
124 
125 
126 	/* If frequency is 0, higher age means it's colder */
127 	if (freq_subscore == 0)
128 		age_in_log *= -1;
129 
130 	/*
131 	 * Now age_in_log is in [-DAMON_MAX_AGE_IN_LOG, DAMON_MAX_AGE_IN_LOG].
132 	 * Scale it to be in [0, 100] and set it as age subscore.
133 	 */
134 	age_in_log += DAMON_MAX_AGE_IN_LOG;
135 	age_subscore = age_in_log * DAMON_MAX_SUBSCORE /
136 		DAMON_MAX_AGE_IN_LOG / 2;
137 
138 	hotness = (freq_weight * freq_subscore + age_weight * age_subscore);
139 	if (freq_weight + age_weight)
140 		hotness /= freq_weight + age_weight;
141 	/*
142 	 * Transform it to fit in [0, DAMOS_MAX_SCORE]
143 	 */
144 	hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE;
145 	hotness = max(min(hotness, DAMOS_MAX_SCORE), 0);
146 
147 	return hotness;
148 }
149 
150 int damon_cold_score(struct damon_ctx *c, struct damon_region *r,
151 			struct damos *s)
152 {
153 	int hotness = damon_hot_score(c, r, s);
154 
155 	/* Return coldness of the region */
156 	return DAMOS_MAX_SCORE - hotness;
157 }
158 
159 static bool damon_folio_mkold_one(struct folio *folio,
160 		struct vm_area_struct *vma, unsigned long addr, void *arg)
161 {
162 	DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, addr, 0);
163 
164 	while (page_vma_mapped_walk(&pvmw)) {
165 		addr = pvmw.address;
166 		if (pvmw.pte)
167 			damon_ptep_mkold(pvmw.pte, vma, addr);
168 		else
169 			damon_pmdp_mkold(pvmw.pmd, vma, addr);
170 	}
171 	return true;
172 }
173 
174 void damon_folio_mkold(struct folio *folio)
175 {
176 	struct rmap_walk_control rwc = {
177 		.rmap_one = damon_folio_mkold_one,
178 		.anon_lock = folio_lock_anon_vma_read,
179 	};
180 
181 	if (!folio_mapped(folio) || !folio_raw_mapping(folio)) {
182 		folio_set_idle(folio);
183 		return;
184 	}
185 
186 	if (!folio_trylock(folio))
187 		return;
188 
189 	rmap_walk(folio, &rwc);
190 	folio_unlock(folio);
191 
192 }
193 
194 static bool damon_folio_young_one(struct folio *folio,
195 		struct vm_area_struct *vma, unsigned long addr, void *arg)
196 {
197 	bool *accessed = arg;
198 	DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, addr, 0);
199 	pte_t pte;
200 
201 	*accessed = false;
202 	while (page_vma_mapped_walk(&pvmw)) {
203 		addr = pvmw.address;
204 		if (pvmw.pte) {
205 			pte = ptep_get(pvmw.pte);
206 
207 			/*
208 			 * PFN swap PTEs, such as device-exclusive ones, that
209 			 * actually map pages are "old" from a CPU perspective.
210 			 * The MMU notifier takes care of any device aspects.
211 			 */
212 			*accessed = (pte_present(pte) && pte_young(pte)) ||
213 				!folio_test_idle(folio) ||
214 				mmu_notifier_test_young(vma->vm_mm, addr);
215 		} else {
216 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
217 			pmd_t pmd = pmdp_get(pvmw.pmd);
218 
219 			*accessed = (pmd_present(pmd) && pmd_young(pmd)) ||
220 				!folio_test_idle(folio) ||
221 				mmu_notifier_test_young(vma->vm_mm, addr);
222 #else
223 			WARN_ON_ONCE(1);
224 #endif	/* CONFIG_TRANSPARENT_HUGEPAGE */
225 		}
226 		if (*accessed) {
227 			page_vma_mapped_walk_done(&pvmw);
228 			break;
229 		}
230 	}
231 
232 	/* If accessed, stop walking */
233 	return *accessed == false;
234 }
235 
236 bool damon_folio_young(struct folio *folio)
237 {
238 	bool accessed = false;
239 	struct rmap_walk_control rwc = {
240 		.arg = &accessed,
241 		.rmap_one = damon_folio_young_one,
242 		.anon_lock = folio_lock_anon_vma_read,
243 	};
244 
245 	if (!folio_mapped(folio) || !folio_raw_mapping(folio)) {
246 		if (folio_test_idle(folio))
247 			return false;
248 		else
249 			return true;
250 	}
251 
252 	if (!folio_trylock(folio))
253 		return false;
254 
255 	rmap_walk(folio, &rwc);
256 	folio_unlock(folio);
257 
258 	return accessed;
259 }
260 
261 bool damos_folio_filter_match(struct damos_filter *filter, struct folio *folio)
262 {
263 	bool matched = false;
264 	struct mem_cgroup *memcg;
265 	size_t folio_sz;
266 
267 	switch (filter->type) {
268 	case DAMOS_FILTER_TYPE_ANON:
269 		matched = folio_test_anon(folio);
270 		break;
271 	case DAMOS_FILTER_TYPE_ACTIVE:
272 		matched = folio_test_active(folio);
273 		break;
274 	case DAMOS_FILTER_TYPE_MEMCG:
275 		rcu_read_lock();
276 		memcg = folio_memcg_check(folio);
277 		if (!memcg)
278 			matched = false;
279 		else
280 			matched = filter->memcg_id == mem_cgroup_id(memcg);
281 		rcu_read_unlock();
282 		break;
283 	case DAMOS_FILTER_TYPE_YOUNG:
284 		matched = damon_folio_young(folio);
285 		if (matched)
286 			damon_folio_mkold(folio);
287 		break;
288 	case DAMOS_FILTER_TYPE_HUGEPAGE_SIZE:
289 		folio_sz = folio_size(folio);
290 		matched = filter->sz_range.min <= folio_sz &&
291 			  folio_sz <= filter->sz_range.max;
292 		break;
293 	case DAMOS_FILTER_TYPE_UNMAPPED:
294 		matched = !folio_mapped(folio) || !folio_raw_mapping(folio);
295 		break;
296 	default:
297 		break;
298 	}
299 
300 	return matched == filter->matching;
301 }
302 
303 static unsigned int __damon_migrate_folio_list(
304 		struct list_head *migrate_folios, struct pglist_data *pgdat,
305 		int target_nid)
306 {
307 	unsigned int nr_succeeded = 0;
308 	struct migration_target_control mtc = {
309 		/*
310 		 * Allocate from 'node', or fail quickly and quietly.
311 		 * When this happens, 'page' will likely just be discarded
312 		 * instead of migrated.
313 		 */
314 		.gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) |
315 			__GFP_NOMEMALLOC | GFP_NOWAIT | __GFP_THISNODE,
316 		.nid = target_nid,
317 	};
318 
319 	if (pgdat->node_id == target_nid || target_nid == NUMA_NO_NODE)
320 		return 0;
321 
322 	if (list_empty(migrate_folios))
323 		return 0;
324 
325 	/* Migration ignores all cpuset and mempolicy settings */
326 	migrate_pages(migrate_folios, alloc_migration_target, NULL,
327 		      (unsigned long)&mtc, MIGRATE_ASYNC, MR_DAMON,
328 		      &nr_succeeded);
329 
330 	return nr_succeeded;
331 }
332 
333 static unsigned int damon_migrate_folio_list(struct list_head *folio_list,
334 						struct pglist_data *pgdat,
335 						int target_nid)
336 {
337 	unsigned int nr_migrated = 0;
338 	struct folio *folio;
339 	LIST_HEAD(ret_folios);
340 	LIST_HEAD(migrate_folios);
341 
342 	while (!list_empty(folio_list)) {
343 		cond_resched();
344 
345 		folio = lru_to_folio(folio_list);
346 		list_del(&folio->lru);
347 
348 		if (!folio_trylock(folio))
349 			goto keep;
350 
351 		/* Relocate its contents to another node. */
352 		list_add(&folio->lru, &migrate_folios);
353 		folio_unlock(folio);
354 		continue;
355 keep:
356 		list_add(&folio->lru, &ret_folios);
357 	}
358 	/* 'folio_list' is always empty here */
359 
360 	/* Migrate folios selected for migration */
361 	nr_migrated += __damon_migrate_folio_list(
362 			&migrate_folios, pgdat, target_nid);
363 	/*
364 	 * Folios that could not be migrated are still in @migrate_folios.  Add
365 	 * those back on @folio_list
366 	 */
367 	if (!list_empty(&migrate_folios))
368 		list_splice_init(&migrate_folios, folio_list);
369 
370 	try_to_unmap_flush();
371 
372 	list_splice(&ret_folios, folio_list);
373 
374 	while (!list_empty(folio_list)) {
375 		folio = lru_to_folio(folio_list);
376 		list_del(&folio->lru);
377 		node_stat_sub_folio(folio, NR_ISOLATED_ANON +
378 				folio_is_file_lru(folio));
379 		folio_putback_lru(folio);
380 	}
381 
382 	return nr_migrated;
383 }
384 
385 unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid)
386 {
387 	int nid;
388 	unsigned long nr_migrated = 0;
389 	LIST_HEAD(node_folio_list);
390 	unsigned int noreclaim_flag;
391 
392 	if (list_empty(folio_list))
393 		return nr_migrated;
394 
395 	if (target_nid < 0 || target_nid >= MAX_NUMNODES ||
396 			!node_state(target_nid, N_MEMORY)) {
397 		while (!list_empty(folio_list)) {
398 			struct folio *folio = lru_to_folio(folio_list);
399 
400 			list_del(&folio->lru);
401 			node_stat_sub_folio(folio, NR_ISOLATED_ANON +
402 					folio_is_file_lru(folio));
403 			folio_putback_lru(folio);
404 		}
405 		return nr_migrated;
406 	}
407 
408 	noreclaim_flag = memalloc_noreclaim_save();
409 
410 	nid = folio_nid(lru_to_folio(folio_list));
411 	do {
412 		struct folio *folio = lru_to_folio(folio_list);
413 
414 		if (nid == folio_nid(folio)) {
415 			list_move(&folio->lru, &node_folio_list);
416 			continue;
417 		}
418 
419 		nr_migrated += damon_migrate_folio_list(&node_folio_list,
420 							   NODE_DATA(nid),
421 							   target_nid);
422 		nid = folio_nid(lru_to_folio(folio_list));
423 	} while (!list_empty(folio_list));
424 
425 	nr_migrated += damon_migrate_folio_list(&node_folio_list,
426 						   NODE_DATA(nid),
427 						   target_nid);
428 
429 	memalloc_noreclaim_restore(noreclaim_flag);
430 
431 	return nr_migrated;
432 }
433 
434 bool damos_ops_has_filter(struct damos *s)
435 {
436 	struct damos_filter *f;
437 
438 	damos_for_each_ops_filter(f, s)
439 		return true;
440 	return false;
441 }
442