paddr.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * DAMON Code 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/mm_inline.h>
  15. #include "../internal.h"
  16. #include "ops-common.h"
  17. static phys_addr_t damon_pa_phys_addr(
  18. unsigned long addr, unsigned long addr_unit)
  19. {
  20. return (phys_addr_t)addr * addr_unit;
  21. }
  22. static unsigned long damon_pa_core_addr(
  23. phys_addr_t pa, unsigned long addr_unit)
  24. {
  25. /*
  26. * Use div_u64() for avoiding linking errors related with __udivdi3,
  27. * __aeabi_uldivmod, or similar problems. This should also improve the
  28. * performance optimization (read div_u64() comment for the detail).
  29. */
  30. if (sizeof(pa) == 8 && sizeof(addr_unit) == 4)
  31. return div_u64(pa, addr_unit);
  32. return pa / addr_unit;
  33. }
  34. static void damon_pa_mkold(phys_addr_t paddr)
  35. {
  36. struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
  37. if (!folio)
  38. return;
  39. damon_folio_mkold(folio);
  40. folio_put(folio);
  41. }
  42. static void __damon_pa_prepare_access_check(struct damon_region *r,
  43. unsigned long addr_unit)
  44. {
  45. r->sampling_addr = damon_rand(r->ar.start, r->ar.end);
  46. damon_pa_mkold(damon_pa_phys_addr(r->sampling_addr, addr_unit));
  47. }
  48. static void damon_pa_prepare_access_checks(struct damon_ctx *ctx)
  49. {
  50. struct damon_target *t;
  51. struct damon_region *r;
  52. damon_for_each_target(t, ctx) {
  53. damon_for_each_region(r, t)
  54. __damon_pa_prepare_access_check(r, ctx->addr_unit);
  55. }
  56. }
  57. static bool damon_pa_young(phys_addr_t paddr, unsigned long *folio_sz)
  58. {
  59. struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
  60. bool accessed;
  61. if (!folio)
  62. return false;
  63. accessed = damon_folio_young(folio);
  64. *folio_sz = folio_size(folio);
  65. folio_put(folio);
  66. return accessed;
  67. }
  68. static void __damon_pa_check_access(struct damon_region *r,
  69. struct damon_attrs *attrs, unsigned long addr_unit)
  70. {
  71. static phys_addr_t last_addr;
  72. static unsigned long last_folio_sz = PAGE_SIZE;
  73. static bool last_accessed;
  74. phys_addr_t sampling_addr = damon_pa_phys_addr(
  75. r->sampling_addr, addr_unit);
  76. /* If the region is in the last checked page, reuse the result */
  77. if (ALIGN_DOWN(last_addr, last_folio_sz) ==
  78. ALIGN_DOWN(sampling_addr, last_folio_sz)) {
  79. damon_update_region_access_rate(r, last_accessed, attrs);
  80. return;
  81. }
  82. last_accessed = damon_pa_young(sampling_addr, &last_folio_sz);
  83. damon_update_region_access_rate(r, last_accessed, attrs);
  84. last_addr = sampling_addr;
  85. }
  86. static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx)
  87. {
  88. struct damon_target *t;
  89. struct damon_region *r;
  90. unsigned int max_nr_accesses = 0;
  91. damon_for_each_target(t, ctx) {
  92. damon_for_each_region(r, t) {
  93. __damon_pa_check_access(
  94. r, &ctx->attrs, ctx->addr_unit);
  95. max_nr_accesses = max(r->nr_accesses, max_nr_accesses);
  96. }
  97. }
  98. return max_nr_accesses;
  99. }
  100. /*
  101. * damos_pa_filter_out - Return true if the page should be filtered out.
  102. */
  103. static bool damos_pa_filter_out(struct damos *scheme, struct folio *folio)
  104. {
  105. struct damos_filter *filter;
  106. if (scheme->core_filters_allowed)
  107. return false;
  108. damos_for_each_ops_filter(filter, scheme) {
  109. if (damos_folio_filter_match(filter, folio))
  110. return !filter->allow;
  111. }
  112. return scheme->ops_filters_default_reject;
  113. }
  114. static bool damon_pa_invalid_damos_folio(struct folio *folio, struct damos *s)
  115. {
  116. if (!folio)
  117. return true;
  118. if (folio == s->last_applied) {
  119. folio_put(folio);
  120. return true;
  121. }
  122. return false;
  123. }
  124. static unsigned long damon_pa_pageout(struct damon_region *r,
  125. unsigned long addr_unit, struct damos *s,
  126. unsigned long *sz_filter_passed)
  127. {
  128. phys_addr_t addr, applied;
  129. LIST_HEAD(folio_list);
  130. bool install_young_filter = true;
  131. struct damos_filter *filter;
  132. struct folio *folio = NULL;
  133. /* check access in page level again by default */
  134. damos_for_each_ops_filter(filter, s) {
  135. if (filter->type == DAMOS_FILTER_TYPE_YOUNG) {
  136. install_young_filter = false;
  137. break;
  138. }
  139. }
  140. if (install_young_filter) {
  141. filter = damos_new_filter(
  142. DAMOS_FILTER_TYPE_YOUNG, true, false);
  143. if (!filter)
  144. return 0;
  145. damos_add_filter(s, filter);
  146. }
  147. addr = damon_pa_phys_addr(r->ar.start, addr_unit);
  148. while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
  149. folio = damon_get_folio(PHYS_PFN(addr));
  150. if (damon_pa_invalid_damos_folio(folio, s)) {
  151. addr += PAGE_SIZE;
  152. continue;
  153. }
  154. if (damos_pa_filter_out(s, folio))
  155. goto put_folio;
  156. else
  157. *sz_filter_passed += folio_size(folio) / addr_unit;
  158. folio_clear_referenced(folio);
  159. folio_test_clear_young(folio);
  160. if (!folio_isolate_lru(folio))
  161. goto put_folio;
  162. if (folio_test_unevictable(folio))
  163. folio_putback_lru(folio);
  164. else
  165. list_add(&folio->lru, &folio_list);
  166. put_folio:
  167. addr += folio_size(folio);
  168. folio_put(folio);
  169. }
  170. if (install_young_filter)
  171. damos_destroy_filter(filter);
  172. applied = reclaim_pages(&folio_list);
  173. cond_resched();
  174. s->last_applied = folio;
  175. return damon_pa_core_addr(applied * PAGE_SIZE, addr_unit);
  176. }
  177. static inline unsigned long damon_pa_de_activate(
  178. struct damon_region *r, unsigned long addr_unit,
  179. struct damos *s, bool activate,
  180. unsigned long *sz_filter_passed)
  181. {
  182. phys_addr_t addr, applied = 0;
  183. struct folio *folio = NULL;
  184. addr = damon_pa_phys_addr(r->ar.start, addr_unit);
  185. while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
  186. folio = damon_get_folio(PHYS_PFN(addr));
  187. if (damon_pa_invalid_damos_folio(folio, s)) {
  188. addr += PAGE_SIZE;
  189. continue;
  190. }
  191. if (damos_pa_filter_out(s, folio))
  192. goto put_folio;
  193. else
  194. *sz_filter_passed += folio_size(folio) / addr_unit;
  195. if (activate)
  196. folio_activate(folio);
  197. else
  198. folio_deactivate(folio);
  199. applied += folio_nr_pages(folio);
  200. put_folio:
  201. addr += folio_size(folio);
  202. folio_put(folio);
  203. }
  204. s->last_applied = folio;
  205. return damon_pa_core_addr(applied * PAGE_SIZE, addr_unit);
  206. }
  207. static unsigned long damon_pa_activate_pages(struct damon_region *r,
  208. unsigned long addr_unit, struct damos *s,
  209. unsigned long *sz_filter_passed)
  210. {
  211. return damon_pa_de_activate(r, addr_unit, s, true, sz_filter_passed);
  212. }
  213. static unsigned long damon_pa_deactivate_pages(struct damon_region *r,
  214. unsigned long addr_unit, struct damos *s,
  215. unsigned long *sz_filter_passed)
  216. {
  217. return damon_pa_de_activate(r, addr_unit, s, false, sz_filter_passed);
  218. }
  219. static unsigned long damon_pa_migrate(struct damon_region *r,
  220. unsigned long addr_unit, struct damos *s,
  221. unsigned long *sz_filter_passed)
  222. {
  223. phys_addr_t addr, applied;
  224. LIST_HEAD(folio_list);
  225. struct folio *folio = NULL;
  226. addr = damon_pa_phys_addr(r->ar.start, addr_unit);
  227. while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
  228. folio = damon_get_folio(PHYS_PFN(addr));
  229. if (damon_pa_invalid_damos_folio(folio, s)) {
  230. addr += PAGE_SIZE;
  231. continue;
  232. }
  233. if (damos_pa_filter_out(s, folio))
  234. goto put_folio;
  235. else
  236. *sz_filter_passed += folio_size(folio) / addr_unit;
  237. if (!folio_isolate_lru(folio))
  238. goto put_folio;
  239. list_add(&folio->lru, &folio_list);
  240. put_folio:
  241. addr += folio_size(folio);
  242. folio_put(folio);
  243. }
  244. applied = damon_migrate_pages(&folio_list, s->target_nid);
  245. cond_resched();
  246. s->last_applied = folio;
  247. return damon_pa_core_addr(applied * PAGE_SIZE, addr_unit);
  248. }
  249. static unsigned long damon_pa_stat(struct damon_region *r,
  250. unsigned long addr_unit, struct damos *s,
  251. unsigned long *sz_filter_passed)
  252. {
  253. phys_addr_t addr;
  254. struct folio *folio = NULL;
  255. if (!damos_ops_has_filter(s))
  256. return 0;
  257. addr = damon_pa_phys_addr(r->ar.start, addr_unit);
  258. while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
  259. folio = damon_get_folio(PHYS_PFN(addr));
  260. if (damon_pa_invalid_damos_folio(folio, s)) {
  261. addr += PAGE_SIZE;
  262. continue;
  263. }
  264. if (!damos_pa_filter_out(s, folio))
  265. *sz_filter_passed += folio_size(folio) / addr_unit;
  266. addr += folio_size(folio);
  267. folio_put(folio);
  268. }
  269. s->last_applied = folio;
  270. return 0;
  271. }
  272. static unsigned long damon_pa_apply_scheme(struct damon_ctx *ctx,
  273. struct damon_target *t, struct damon_region *r,
  274. struct damos *scheme, unsigned long *sz_filter_passed)
  275. {
  276. unsigned long aunit = ctx->addr_unit;
  277. switch (scheme->action) {
  278. case DAMOS_PAGEOUT:
  279. return damon_pa_pageout(r, aunit, scheme, sz_filter_passed);
  280. case DAMOS_LRU_PRIO:
  281. return damon_pa_activate_pages(r, aunit, scheme,
  282. sz_filter_passed);
  283. case DAMOS_LRU_DEPRIO:
  284. return damon_pa_deactivate_pages(r, aunit, scheme,
  285. sz_filter_passed);
  286. case DAMOS_MIGRATE_HOT:
  287. case DAMOS_MIGRATE_COLD:
  288. return damon_pa_migrate(r, aunit, scheme, sz_filter_passed);
  289. case DAMOS_STAT:
  290. return damon_pa_stat(r, aunit, scheme, sz_filter_passed);
  291. default:
  292. /* DAMOS actions that not yet supported by 'paddr'. */
  293. break;
  294. }
  295. return 0;
  296. }
  297. static int damon_pa_scheme_score(struct damon_ctx *context,
  298. struct damon_target *t, struct damon_region *r,
  299. struct damos *scheme)
  300. {
  301. switch (scheme->action) {
  302. case DAMOS_PAGEOUT:
  303. return damon_cold_score(context, r, scheme);
  304. case DAMOS_LRU_PRIO:
  305. return damon_hot_score(context, r, scheme);
  306. case DAMOS_LRU_DEPRIO:
  307. return damon_cold_score(context, r, scheme);
  308. case DAMOS_MIGRATE_HOT:
  309. return damon_hot_score(context, r, scheme);
  310. case DAMOS_MIGRATE_COLD:
  311. return damon_cold_score(context, r, scheme);
  312. default:
  313. break;
  314. }
  315. return DAMOS_MAX_SCORE;
  316. }
  317. static int __init damon_pa_initcall(void)
  318. {
  319. struct damon_operations ops = {
  320. .id = DAMON_OPS_PADDR,
  321. .init = NULL,
  322. .update = NULL,
  323. .prepare_access_checks = damon_pa_prepare_access_checks,
  324. .check_accesses = damon_pa_check_accesses,
  325. .target_valid = NULL,
  326. .apply_scheme = damon_pa_apply_scheme,
  327. .get_scheme_score = damon_pa_scheme_score,
  328. };
  329. return damon_register_ops(&ops);
  330. };
  331. subsys_initcall(damon_pa_initcall);