18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2008
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Guest page hinting for unused pages.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/mm.h>
148c2ecf20Sopenharmony_ci#include <linux/memblock.h>
158c2ecf20Sopenharmony_ci#include <linux/gfp.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <asm/facility.h>
188c2ecf20Sopenharmony_ci#include <asm/page-states.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic int cmma_flag = 1;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int __init cmma(char *str)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	bool enabled;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (!kstrtobool(str, &enabled))
278c2ecf20Sopenharmony_ci		cmma_flag = enabled;
288c2ecf20Sopenharmony_ci	return 1;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci__setup("cmma=", cmma);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline int cmma_test_essa(void)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	register unsigned long tmp asm("0") = 0;
358c2ecf20Sopenharmony_ci	register int rc asm("1");
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* test ESSA_GET_STATE */
388c2ecf20Sopenharmony_ci	asm volatile(
398c2ecf20Sopenharmony_ci		"	.insn	rrf,0xb9ab0000,%1,%1,%2,0\n"
408c2ecf20Sopenharmony_ci		"0:     la      %0,0\n"
418c2ecf20Sopenharmony_ci		"1:\n"
428c2ecf20Sopenharmony_ci		EX_TABLE(0b,1b)
438c2ecf20Sopenharmony_ci		: "=&d" (rc), "+&d" (tmp)
448c2ecf20Sopenharmony_ci		: "i" (ESSA_GET_STATE), "0" (-EOPNOTSUPP));
458c2ecf20Sopenharmony_ci	return rc;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid __init cmma_init(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	if (!cmma_flag)
518c2ecf20Sopenharmony_ci		return;
528c2ecf20Sopenharmony_ci	if (cmma_test_essa()) {
538c2ecf20Sopenharmony_ci		cmma_flag = 0;
548c2ecf20Sopenharmony_ci		return;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	if (test_facility(147))
578c2ecf20Sopenharmony_ci		cmma_flag = 2;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline unsigned char get_page_state(struct page *page)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned char state;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	asm volatile("	.insn	rrf,0xb9ab0000,%0,%1,%2,0"
658c2ecf20Sopenharmony_ci		     : "=&d" (state)
668c2ecf20Sopenharmony_ci		     : "a" (page_to_phys(page)),
678c2ecf20Sopenharmony_ci		       "i" (ESSA_GET_STATE));
688c2ecf20Sopenharmony_ci	return state & 0x3f;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline void set_page_unused(struct page *page, int order)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int i, rc;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
768c2ecf20Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
778c2ecf20Sopenharmony_ci			     : "=&d" (rc)
788c2ecf20Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
798c2ecf20Sopenharmony_ci			       "i" (ESSA_SET_UNUSED));
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline void set_page_stable_dat(struct page *page, int order)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	int i, rc;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
878c2ecf20Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
888c2ecf20Sopenharmony_ci			     : "=&d" (rc)
898c2ecf20Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
908c2ecf20Sopenharmony_ci			       "i" (ESSA_SET_STABLE));
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic inline void set_page_stable_nodat(struct page *page, int order)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int i, rc;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	for (i = 0; i < (1 << order); i++)
988c2ecf20Sopenharmony_ci		asm volatile(".insn rrf,0xb9ab0000,%0,%1,%2,0"
998c2ecf20Sopenharmony_ci			     : "=&d" (rc)
1008c2ecf20Sopenharmony_ci			     : "a" (page_to_phys(page + i)),
1018c2ecf20Sopenharmony_ci			       "i" (ESSA_SET_STABLE_NODAT));
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void mark_kernel_pmd(pud_t *pud, unsigned long addr, unsigned long end)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	unsigned long next;
1078c2ecf20Sopenharmony_ci	struct page *page;
1088c2ecf20Sopenharmony_ci	pmd_t *pmd;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	pmd = pmd_offset(pud, addr);
1118c2ecf20Sopenharmony_ci	do {
1128c2ecf20Sopenharmony_ci		next = pmd_addr_end(addr, end);
1138c2ecf20Sopenharmony_ci		if (pmd_none(*pmd) || pmd_large(*pmd))
1148c2ecf20Sopenharmony_ci			continue;
1158c2ecf20Sopenharmony_ci		page = phys_to_page(pmd_val(*pmd));
1168c2ecf20Sopenharmony_ci		set_bit(PG_arch_1, &page->flags);
1178c2ecf20Sopenharmony_ci	} while (pmd++, addr = next, addr != end);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void mark_kernel_pud(p4d_t *p4d, unsigned long addr, unsigned long end)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	unsigned long next;
1238c2ecf20Sopenharmony_ci	struct page *page;
1248c2ecf20Sopenharmony_ci	pud_t *pud;
1258c2ecf20Sopenharmony_ci	int i;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	pud = pud_offset(p4d, addr);
1288c2ecf20Sopenharmony_ci	do {
1298c2ecf20Sopenharmony_ci		next = pud_addr_end(addr, end);
1308c2ecf20Sopenharmony_ci		if (pud_none(*pud) || pud_large(*pud))
1318c2ecf20Sopenharmony_ci			continue;
1328c2ecf20Sopenharmony_ci		if (!pud_folded(*pud)) {
1338c2ecf20Sopenharmony_ci			page = phys_to_page(pud_val(*pud));
1348c2ecf20Sopenharmony_ci			for (i = 0; i < 4; i++)
1358c2ecf20Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci		mark_kernel_pmd(pud, addr, next);
1388c2ecf20Sopenharmony_ci	} while (pud++, addr = next, addr != end);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void mark_kernel_p4d(pgd_t *pgd, unsigned long addr, unsigned long end)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	unsigned long next;
1448c2ecf20Sopenharmony_ci	struct page *page;
1458c2ecf20Sopenharmony_ci	p4d_t *p4d;
1468c2ecf20Sopenharmony_ci	int i;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	p4d = p4d_offset(pgd, addr);
1498c2ecf20Sopenharmony_ci	do {
1508c2ecf20Sopenharmony_ci		next = p4d_addr_end(addr, end);
1518c2ecf20Sopenharmony_ci		if (p4d_none(*p4d))
1528c2ecf20Sopenharmony_ci			continue;
1538c2ecf20Sopenharmony_ci		if (!p4d_folded(*p4d)) {
1548c2ecf20Sopenharmony_ci			page = phys_to_page(p4d_val(*p4d));
1558c2ecf20Sopenharmony_ci			for (i = 0; i < 4; i++)
1568c2ecf20Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci		mark_kernel_pud(p4d, addr, next);
1598c2ecf20Sopenharmony_ci	} while (p4d++, addr = next, addr != end);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void mark_kernel_pgd(void)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	unsigned long addr, next;
1658c2ecf20Sopenharmony_ci	struct page *page;
1668c2ecf20Sopenharmony_ci	pgd_t *pgd;
1678c2ecf20Sopenharmony_ci	int i;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	addr = 0;
1708c2ecf20Sopenharmony_ci	pgd = pgd_offset_k(addr);
1718c2ecf20Sopenharmony_ci	do {
1728c2ecf20Sopenharmony_ci		next = pgd_addr_end(addr, MODULES_END);
1738c2ecf20Sopenharmony_ci		if (pgd_none(*pgd))
1748c2ecf20Sopenharmony_ci			continue;
1758c2ecf20Sopenharmony_ci		if (!pgd_folded(*pgd)) {
1768c2ecf20Sopenharmony_ci			page = phys_to_page(pgd_val(*pgd));
1778c2ecf20Sopenharmony_ci			for (i = 0; i < 4; i++)
1788c2ecf20Sopenharmony_ci				set_bit(PG_arch_1, &page[i].flags);
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci		mark_kernel_p4d(pgd, addr, next);
1818c2ecf20Sopenharmony_ci	} while (pgd++, addr = next, addr != MODULES_END);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_civoid __init cmma_init_nodat(void)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct page *page;
1878c2ecf20Sopenharmony_ci	unsigned long start, end, ix;
1888c2ecf20Sopenharmony_ci	int i;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (cmma_flag < 2)
1918c2ecf20Sopenharmony_ci		return;
1928c2ecf20Sopenharmony_ci	/* Mark pages used in kernel page tables */
1938c2ecf20Sopenharmony_ci	mark_kernel_pgd();
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* Set all kernel pages not used for page tables to stable/no-dat */
1968c2ecf20Sopenharmony_ci	for_each_mem_pfn_range(i, MAX_NUMNODES, &start, &end, NULL) {
1978c2ecf20Sopenharmony_ci		page = pfn_to_page(start);
1988c2ecf20Sopenharmony_ci		for (ix = start; ix < end; ix++, page++) {
1998c2ecf20Sopenharmony_ci			if (__test_and_clear_bit(PG_arch_1, &page->flags))
2008c2ecf20Sopenharmony_ci				continue;	/* skip page table pages */
2018c2ecf20Sopenharmony_ci			if (!list_empty(&page->lru))
2028c2ecf20Sopenharmony_ci				continue;	/* skip free pages */
2038c2ecf20Sopenharmony_ci			set_page_stable_nodat(page, 0);
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_civoid arch_free_page(struct page *page, int order)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	if (!cmma_flag)
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	set_page_unused(page, order);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_civoid arch_alloc_page(struct page *page, int order)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	if (!cmma_flag)
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci	if (cmma_flag < 2)
2208c2ecf20Sopenharmony_ci		set_page_stable_dat(page, order);
2218c2ecf20Sopenharmony_ci	else
2228c2ecf20Sopenharmony_ci		set_page_stable_nodat(page, order);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_civoid arch_set_page_dat(struct page *page, int order)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (!cmma_flag)
2288c2ecf20Sopenharmony_ci		return;
2298c2ecf20Sopenharmony_ci	set_page_stable_dat(page, order);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_civoid arch_set_page_nodat(struct page *page, int order)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	if (cmma_flag < 2)
2358c2ecf20Sopenharmony_ci		return;
2368c2ecf20Sopenharmony_ci	set_page_stable_nodat(page, order);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint arch_test_page_nodat(struct page *page)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	unsigned char state;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (cmma_flag < 2)
2448c2ecf20Sopenharmony_ci		return 0;
2458c2ecf20Sopenharmony_ci	state = get_page_state(page);
2468c2ecf20Sopenharmony_ci	return !!(state & 0x20);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_civoid arch_set_page_states(int make_stable)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	unsigned long flags, order, t;
2528c2ecf20Sopenharmony_ci	struct list_head *l;
2538c2ecf20Sopenharmony_ci	struct page *page;
2548c2ecf20Sopenharmony_ci	struct zone *zone;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (!cmma_flag)
2578c2ecf20Sopenharmony_ci		return;
2588c2ecf20Sopenharmony_ci	if (make_stable)
2598c2ecf20Sopenharmony_ci		drain_local_pages(NULL);
2608c2ecf20Sopenharmony_ci	for_each_populated_zone(zone) {
2618c2ecf20Sopenharmony_ci		spin_lock_irqsave(&zone->lock, flags);
2628c2ecf20Sopenharmony_ci		for_each_migratetype_order(order, t) {
2638c2ecf20Sopenharmony_ci			list_for_each(l, &zone->free_area[order].free_list[t]) {
2648c2ecf20Sopenharmony_ci				page = list_entry(l, struct page, lru);
2658c2ecf20Sopenharmony_ci				if (make_stable)
2668c2ecf20Sopenharmony_ci					set_page_stable_dat(page, order);
2678c2ecf20Sopenharmony_ci				else
2688c2ecf20Sopenharmony_ci					set_page_unused(page, order);
2698c2ecf20Sopenharmony_ci			}
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&zone->lock, flags);
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci}
274