paddr.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * DAMON Primitives for The Physical Address Space
  4. *
  5. * Author: SeongJae Park <sj@kernel.org>
  6. */
  7. #define pr_fmt(fmt) "damon-pa: " fmt
  8. #include <linux/mmu_notifier.h>
  9. #include <linux/page_idle.h>
  10. #include <linux/pagemap.h>
  11. #include <linux/rmap.h>
  12. #include <linux/swap.h>
  13. #include <linux/memory-tiers.h>
  14. #include <linux/migrate.h>
  15. #include <linux/mm_inline.h>
  16. #include "../internal.h"
  17. #include "ops-common.h"
  18. static bool damon_folio_mkold_one(struct folio *folio,
  19. struct vm_area_struct *vma, unsigned long addr, void *arg)
  20. {
  21. DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, addr, 0);
  22. while (page_vma_mapped_walk(&pvmw)) {
  23. addr = pvmw.address;
  24. if (pvmw.pte)
  25. damon_ptep_mkold(pvmw.pte, vma, addr);
  26. else
  27. damon_pmdp_mkold(pvmw.pmd, vma, addr);
  28. }
  29. return true;
  30. }
  31. static void damon_folio_mkold(struct folio *folio)
  32. {
  33. struct rmap_walk_control rwc = {
  34. .rmap_one = damon_folio_mkold_one,
  35. .anon_lock = folio_lock_anon_vma_read,
  36. };
  37. bool need_lock;
  38. if (!folio_mapped(folio) || !folio_raw_mapping(folio)) {
  39. folio_set_idle(folio);
  40. return;
  41. }
  42. need_lock = !folio_test_anon(folio) || folio_test_ksm(folio);
  43. if (need_lock && !folio_trylock(folio))
  44. return;
  45. rmap_walk(folio, &rwc);
  46. if (need_lock)
  47. folio_unlock(folio);
  48. }
  49. static void damon_pa_mkold(unsigned long paddr)
  50. {
  51. struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
  52. if (!folio)
  53. return;
  54. damon_folio_mkold(folio);
  55. folio_put(folio);
  56. }
  57. static void __damon_pa_prepare_access_check(struct damon_region *r)
  58. {
  59. r->sampling_addr = damon_rand(r->ar.start, r->ar.end);
  60. damon_pa_mkold(r->sampling_addr);
  61. }
  62. static void damon_pa_prepare_access_checks(struct damon_ctx *ctx)
  63. {
  64. struct damon_target *t;
  65. struct damon_region *r;
  66. damon_for_each_target(t, ctx) {
  67. damon_for_each_region(r, t)
  68. __damon_pa_prepare_access_check(r);
  69. }
  70. }
  71. static bool damon_folio_young_one(struct folio *folio,
  72. struct vm_area_struct *vma, unsigned long addr, void *arg)
  73. {
  74. bool *accessed = arg;
  75. DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, addr, 0);
  76. *accessed = false;
  77. while (page_vma_mapped_walk(&pvmw)) {
  78. addr = pvmw.address;
  79. if (pvmw.pte) {
  80. *accessed = pte_young(ptep_get(pvmw.pte)) ||
  81. !folio_test_idle(folio) ||
  82. mmu_notifier_test_young(vma->vm_mm, addr);
  83. } else {
  84. #ifdef CONFIG_TRANSPARENT_HUGEPAGE
  85. *accessed = pmd_young(pmdp_get(pvmw.pmd)) ||
  86. !folio_test_idle(folio) ||
  87. mmu_notifier_test_young(vma->vm_mm, addr);
  88. #else
  89. WARN_ON_ONCE(1);
  90. #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
  91. }
  92. if (*accessed) {
  93. page_vma_mapped_walk_done(&pvmw);
  94. break;
  95. }
  96. }
  97. /* If accessed, stop walking */
  98. return *accessed == false;
  99. }
  100. static bool damon_folio_young(struct folio *folio)
  101. {
  102. bool accessed = false;
  103. struct rmap_walk_control rwc = {
  104. .arg = &accessed,
  105. .rmap_one = damon_folio_young_one,
  106. .anon_lock = folio_lock_anon_vma_read,
  107. };
  108. bool need_lock;
  109. if (!folio_mapped(folio) || !folio_raw_mapping(folio)) {
  110. if (folio_test_idle(folio))
  111. return false;
  112. else
  113. return true;
  114. }
  115. need_lock = !folio_test_anon(folio) || folio_test_ksm(folio);
  116. if (need_lock && !folio_trylock(folio))
  117. return false;
  118. rmap_walk(folio, &rwc);
  119. if (need_lock)
  120. folio_unlock(folio);
  121. return accessed;
  122. }
  123. static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
  124. {
  125. struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
  126. bool accessed;
  127. if (!folio)
  128. return false;
  129. accessed = damon_folio_young(folio);
  130. *folio_sz = folio_size(folio);
  131. folio_put(folio);
  132. return accessed;
  133. }
  134. static void __damon_pa_check_access(struct damon_region *r,
  135. struct damon_attrs *attrs)
  136. {
  137. static unsigned long last_addr;
  138. static unsigned long last_folio_sz = PAGE_SIZE;
  139. static bool last_accessed;
  140. /* If the region is in the last checked page, reuse the result */
  141. if (ALIGN_DOWN(last_addr, last_folio_sz) ==
  142. ALIGN_DOWN(r->sampling_addr, last_folio_sz)) {
  143. damon_update_region_access_rate(r, last_accessed, attrs);
  144. return;
  145. }
  146. last_accessed = damon_pa_young(r->sampling_addr, &last_folio_sz);
  147. damon_update_region_access_rate(r, last_accessed, attrs);
  148. last_addr = r->sampling_addr;
  149. }
  150. static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx)
  151. {
  152. struct damon_target *t;
  153. struct damon_region *r;
  154. unsigned int max_nr_accesses = 0;
  155. damon_for_each_target(t, ctx) {
  156. damon_for_each_region(r, t) {
  157. __damon_pa_check_access(r, &ctx->attrs);
  158. max_nr_accesses = max(r->nr_accesses, max_nr_accesses);
  159. }
  160. }
  161. return max_nr_accesses;
  162. }
  163. static bool __damos_pa_filter_out(struct damos_filter *filter,
  164. struct folio *folio)
  165. {
  166. bool matched = false;
  167. struct mem_cgroup *memcg;
  168. switch (filter->type) {
  169. case DAMOS_FILTER_TYPE_ANON:
  170. matched = folio_test_anon(folio);
  171. break;
  172. case DAMOS_FILTER_TYPE_MEMCG:
  173. rcu_read_lock();
  174. memcg = folio_memcg_check(folio);
  175. if (!memcg)
  176. matched = false;
  177. else
  178. matched = filter->memcg_id == mem_cgroup_id(memcg);
  179. rcu_read_unlock();
  180. break;
  181. case DAMOS_FILTER_TYPE_YOUNG:
  182. matched = damon_folio_young(folio);
  183. if (matched)
  184. damon_folio_mkold(folio);
  185. break;
  186. default:
  187. break;
  188. }
  189. return matched == filter->matching;
  190. }
  191. /*
  192. * damos_pa_filter_out - Return true if the page should be filtered out.
  193. */
  194. static bool damos_pa_filter_out(struct damos *scheme, struct folio *folio)
  195. {
  196. struct damos_filter *filter;
  197. damos_for_each_filter(filter, scheme) {
  198. if (__damos_pa_filter_out(filter, folio))
  199. return true;
  200. }
  201. return false;
  202. }
  203. static unsigned long damon_pa_pageout(struct damon_region *r, struct damos *s)
  204. {
  205. unsigned long addr, applied;
  206. LIST_HEAD(folio_list);
  207. bool install_young_filter = true;
  208. struct damos_filter *filter;
  209. /* check access in page level again by default */
  210. damos_for_each_filter(filter, s) {
  211. if (filter->type == DAMOS_FILTER_TYPE_YOUNG) {
  212. install_young_filter = false;
  213. break;
  214. }
  215. }
  216. if (install_young_filter) {
  217. filter = damos_new_filter(DAMOS_FILTER_TYPE_YOUNG, true);
  218. if (!filter)
  219. return 0;
  220. damos_add_filter(s, filter);
  221. }
  222. for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
  223. struct folio *folio = damon_get_folio(PHYS_PFN(addr));
  224. if (!folio)
  225. continue;
  226. if (damos_pa_filter_out(s, folio))
  227. goto put_folio;
  228. folio_clear_referenced(folio);
  229. folio_test_clear_young(folio);
  230. if (!folio_isolate_lru(folio))
  231. goto put_folio;
  232. if (folio_test_unevictable(folio))
  233. folio_putback_lru(folio);
  234. else
  235. list_add(&folio->lru, &folio_list);
  236. put_folio:
  237. folio_put(folio);
  238. }
  239. if (install_young_filter)
  240. damos_destroy_filter(filter);
  241. applied = reclaim_pages(&folio_list);
  242. cond_resched();
  243. return applied * PAGE_SIZE;
  244. }
  245. static inline unsigned long damon_pa_mark_accessed_or_deactivate(
  246. struct damon_region *r, struct damos *s, bool mark_accessed)
  247. {
  248. unsigned long addr, applied = 0;
  249. for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
  250. struct folio *folio = damon_get_folio(PHYS_PFN(addr));
  251. if (!folio)
  252. continue;
  253. if (damos_pa_filter_out(s, folio))
  254. goto put_folio;
  255. if (mark_accessed)
  256. folio_mark_accessed(folio);
  257. else
  258. folio_deactivate(folio);
  259. applied += folio_nr_pages(folio);
  260. put_folio:
  261. folio_put(folio);
  262. }
  263. return applied * PAGE_SIZE;
  264. }
  265. static unsigned long damon_pa_mark_accessed(struct damon_region *r,
  266. struct damos *s)
  267. {
  268. return damon_pa_mark_accessed_or_deactivate(r, s, true);
  269. }
  270. static unsigned long damon_pa_deactivate_pages(struct damon_region *r,
  271. struct damos *s)
  272. {
  273. return damon_pa_mark_accessed_or_deactivate(r, s, false);
  274. }
  275. static unsigned int __damon_pa_migrate_folio_list(
  276. struct list_head *migrate_folios, struct pglist_data *pgdat,
  277. int target_nid)
  278. {
  279. unsigned int nr_succeeded = 0;
  280. nodemask_t allowed_mask = NODE_MASK_NONE;
  281. struct migration_target_control mtc = {
  282. /*
  283. * Allocate from 'node', or fail quickly and quietly.
  284. * When this happens, 'page' will likely just be discarded
  285. * instead of migrated.
  286. */
  287. .gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) |
  288. __GFP_NOWARN | __GFP_NOMEMALLOC | GFP_NOWAIT,
  289. .nid = target_nid,
  290. .nmask = &allowed_mask
  291. };
  292. if (pgdat->node_id == target_nid || target_nid == NUMA_NO_NODE)
  293. return 0;
  294. if (list_empty(migrate_folios))
  295. return 0;
  296. /* Migration ignores all cpuset and mempolicy settings */
  297. migrate_pages(migrate_folios, alloc_migrate_folio, NULL,
  298. (unsigned long)&mtc, MIGRATE_ASYNC, MR_DAMON,
  299. &nr_succeeded);
  300. return nr_succeeded;
  301. }
  302. static unsigned int damon_pa_migrate_folio_list(struct list_head *folio_list,
  303. struct pglist_data *pgdat,
  304. int target_nid)
  305. {
  306. unsigned int nr_migrated = 0;
  307. struct folio *folio;
  308. LIST_HEAD(ret_folios);
  309. LIST_HEAD(migrate_folios);
  310. while (!list_empty(folio_list)) {
  311. struct folio *folio;
  312. cond_resched();
  313. folio = lru_to_folio(folio_list);
  314. list_del(&folio->lru);
  315. if (!folio_trylock(folio))
  316. goto keep;
  317. /* Relocate its contents to another node. */
  318. list_add(&folio->lru, &migrate_folios);
  319. folio_unlock(folio);
  320. continue;
  321. keep:
  322. list_add(&folio->lru, &ret_folios);
  323. }
  324. /* 'folio_list' is always empty here */
  325. /* Migrate folios selected for migration */
  326. nr_migrated += __damon_pa_migrate_folio_list(
  327. &migrate_folios, pgdat, target_nid);
  328. /*
  329. * Folios that could not be migrated are still in @migrate_folios. Add
  330. * those back on @folio_list
  331. */
  332. if (!list_empty(&migrate_folios))
  333. list_splice_init(&migrate_folios, folio_list);
  334. try_to_unmap_flush();
  335. list_splice(&ret_folios, folio_list);
  336. while (!list_empty(folio_list)) {
  337. folio = lru_to_folio(folio_list);
  338. list_del(&folio->lru);
  339. folio_putback_lru(folio);
  340. }
  341. return nr_migrated;
  342. }
  343. static unsigned long damon_pa_migrate_pages(struct list_head *folio_list,
  344. int target_nid)
  345. {
  346. int nid;
  347. unsigned long nr_migrated = 0;
  348. LIST_HEAD(node_folio_list);
  349. unsigned int noreclaim_flag;
  350. if (list_empty(folio_list))
  351. return nr_migrated;
  352. noreclaim_flag = memalloc_noreclaim_save();
  353. nid = folio_nid(lru_to_folio(folio_list));
  354. do {
  355. struct folio *folio = lru_to_folio(folio_list);
  356. if (nid == folio_nid(folio)) {
  357. list_move(&folio->lru, &node_folio_list);
  358. continue;
  359. }
  360. nr_migrated += damon_pa_migrate_folio_list(&node_folio_list,
  361. NODE_DATA(nid),
  362. target_nid);
  363. nid = folio_nid(lru_to_folio(folio_list));
  364. } while (!list_empty(folio_list));
  365. nr_migrated += damon_pa_migrate_folio_list(&node_folio_list,
  366. NODE_DATA(nid),
  367. target_nid);
  368. memalloc_noreclaim_restore(noreclaim_flag);
  369. return nr_migrated;
  370. }
  371. static unsigned long damon_pa_migrate(struct damon_region *r, struct damos *s)
  372. {
  373. unsigned long addr, applied;
  374. LIST_HEAD(folio_list);
  375. for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
  376. struct folio *folio = damon_get_folio(PHYS_PFN(addr));
  377. if (!folio)
  378. continue;
  379. if (damos_pa_filter_out(s, folio))
  380. goto put_folio;
  381. if (!folio_isolate_lru(folio))
  382. goto put_folio;
  383. list_add(&folio->lru, &folio_list);
  384. put_folio:
  385. folio_put(folio);
  386. }
  387. applied = damon_pa_migrate_pages(&folio_list, s->target_nid);
  388. cond_resched();
  389. return applied * PAGE_SIZE;
  390. }
  391. static unsigned long damon_pa_apply_scheme(struct damon_ctx *ctx,
  392. struct damon_target *t, struct damon_region *r,
  393. struct damos *scheme)
  394. {
  395. switch (scheme->action) {
  396. case DAMOS_PAGEOUT:
  397. return damon_pa_pageout(r, scheme);
  398. case DAMOS_LRU_PRIO:
  399. return damon_pa_mark_accessed(r, scheme);
  400. case DAMOS_LRU_DEPRIO:
  401. return damon_pa_deactivate_pages(r, scheme);
  402. case DAMOS_MIGRATE_HOT:
  403. case DAMOS_MIGRATE_COLD:
  404. return damon_pa_migrate(r, scheme);
  405. case DAMOS_STAT:
  406. break;
  407. default:
  408. /* DAMOS actions that not yet supported by 'paddr'. */
  409. break;
  410. }
  411. return 0;
  412. }
  413. static int damon_pa_scheme_score(struct damon_ctx *context,
  414. struct damon_target *t, struct damon_region *r,
  415. struct damos *scheme)
  416. {
  417. switch (scheme->action) {
  418. case DAMOS_PAGEOUT:
  419. return damon_cold_score(context, r, scheme);
  420. case DAMOS_LRU_PRIO:
  421. return damon_hot_score(context, r, scheme);
  422. case DAMOS_LRU_DEPRIO:
  423. return damon_cold_score(context, r, scheme);
  424. case DAMOS_MIGRATE_HOT:
  425. return damon_hot_score(context, r, scheme);
  426. case DAMOS_MIGRATE_COLD:
  427. return damon_cold_score(context, r, scheme);
  428. default:
  429. break;
  430. }
  431. return DAMOS_MAX_SCORE;
  432. }
  433. static int __init damon_pa_initcall(void)
  434. {
  435. struct damon_operations ops = {
  436. .id = DAMON_OPS_PADDR,
  437. .init = NULL,
  438. .update = NULL,
  439. .prepare_access_checks = damon_pa_prepare_access_checks,
  440. .check_accesses = damon_pa_check_accesses,
  441. .reset_aggregated = NULL,
  442. .target_valid = NULL,
  443. .cleanup = NULL,
  444. .apply_scheme = damon_pa_apply_scheme,
  445. .get_scheme_score = damon_pa_scheme_score,
  446. };
  447. return damon_register_ops(&ops);
  448. };
  449. subsys_initcall(damon_pa_initcall);