18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mm_init.c - Memory initialisation verification and debugging
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2008 IBM Corporation, 2008
68c2ecf20Sopenharmony_ci * Author Mel Gorman <mel@csn.ul.ie>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/kobject.h>
128c2ecf20Sopenharmony_ci#include <linux/export.h>
138c2ecf20Sopenharmony_ci#include <linux/memory.h>
148c2ecf20Sopenharmony_ci#include <linux/notifier.h>
158c2ecf20Sopenharmony_ci#include <linux/sched.h>
168c2ecf20Sopenharmony_ci#include <linux/mman.h>
178c2ecf20Sopenharmony_ci#include "internal.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_MEMORY_INIT
208c2ecf20Sopenharmony_ciint __meminitdata mminit_loglevel;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#ifndef SECTIONS_SHIFT
238c2ecf20Sopenharmony_ci#define SECTIONS_SHIFT	0
248c2ecf20Sopenharmony_ci#endif
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* The zonelists are simply reported, validation is manual. */
278c2ecf20Sopenharmony_civoid __init mminit_verify_zonelist(void)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int nid;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (mminit_loglevel < MMINIT_VERIFY)
328c2ecf20Sopenharmony_ci		return;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	for_each_online_node(nid) {
358c2ecf20Sopenharmony_ci		pg_data_t *pgdat = NODE_DATA(nid);
368c2ecf20Sopenharmony_ci		struct zone *zone;
378c2ecf20Sopenharmony_ci		struct zoneref *z;
388c2ecf20Sopenharmony_ci		struct zonelist *zonelist;
398c2ecf20Sopenharmony_ci		int i, listid, zoneid;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci		BUILD_BUG_ON(MAX_ZONELISTS > 2);
428c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_ZONELISTS * MAX_NR_ZONES; i++) {
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci			/* Identify the zone and nodelist */
458c2ecf20Sopenharmony_ci			zoneid = i % MAX_NR_ZONES;
468c2ecf20Sopenharmony_ci			listid = i / MAX_NR_ZONES;
478c2ecf20Sopenharmony_ci			zonelist = &pgdat->node_zonelists[listid];
488c2ecf20Sopenharmony_ci			zone = &pgdat->node_zones[zoneid];
498c2ecf20Sopenharmony_ci			if (!populated_zone(zone))
508c2ecf20Sopenharmony_ci				continue;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci			/* Print information about the zonelist */
538c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "mminit::zonelist %s %d:%s = ",
548c2ecf20Sopenharmony_ci				listid > 0 ? "thisnode" : "general", nid,
558c2ecf20Sopenharmony_ci				zone->name);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci			/* Iterate the zonelist */
588c2ecf20Sopenharmony_ci			for_each_zone_zonelist(zone, z, zonelist, zoneid)
598c2ecf20Sopenharmony_ci				pr_cont("%d:%s ", zone_to_nid(zone), zone->name);
608c2ecf20Sopenharmony_ci			pr_cont("\n");
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_civoid __init mminit_verify_pageflags_layout(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int shift, width;
688c2ecf20Sopenharmony_ci	unsigned long or_mask, add_mask;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	shift = 8 * sizeof(unsigned long);
718c2ecf20Sopenharmony_ci	width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH
728c2ecf20Sopenharmony_ci		- LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH;
738c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths",
748c2ecf20Sopenharmony_ci		"Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n",
758c2ecf20Sopenharmony_ci		SECTIONS_WIDTH,
768c2ecf20Sopenharmony_ci		NODES_WIDTH,
778c2ecf20Sopenharmony_ci		ZONES_WIDTH,
788c2ecf20Sopenharmony_ci		LAST_CPUPID_WIDTH,
798c2ecf20Sopenharmony_ci		KASAN_TAG_WIDTH,
808c2ecf20Sopenharmony_ci		NR_PAGEFLAGS);
818c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts",
828c2ecf20Sopenharmony_ci		"Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n",
838c2ecf20Sopenharmony_ci		SECTIONS_SHIFT,
848c2ecf20Sopenharmony_ci		NODES_SHIFT,
858c2ecf20Sopenharmony_ci		ZONES_SHIFT,
868c2ecf20Sopenharmony_ci		LAST_CPUPID_SHIFT,
878c2ecf20Sopenharmony_ci		KASAN_TAG_WIDTH);
888c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_pgshifts",
898c2ecf20Sopenharmony_ci		"Section %lu Node %lu Zone %lu Lastcpupid %lu Kasantag %lu\n",
908c2ecf20Sopenharmony_ci		(unsigned long)SECTIONS_PGSHIFT,
918c2ecf20Sopenharmony_ci		(unsigned long)NODES_PGSHIFT,
928c2ecf20Sopenharmony_ci		(unsigned long)ZONES_PGSHIFT,
938c2ecf20Sopenharmony_ci		(unsigned long)LAST_CPUPID_PGSHIFT,
948c2ecf20Sopenharmony_ci		(unsigned long)KASAN_TAG_PGSHIFT);
958c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodezoneid",
968c2ecf20Sopenharmony_ci		"Node/Zone ID: %lu -> %lu\n",
978c2ecf20Sopenharmony_ci		(unsigned long)(ZONEID_PGOFF + ZONEID_SHIFT),
988c2ecf20Sopenharmony_ci		(unsigned long)ZONEID_PGOFF);
998c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_usage",
1008c2ecf20Sopenharmony_ci		"location: %d -> %d layout %d -> %d unused %d -> %d page-flags\n",
1018c2ecf20Sopenharmony_ci		shift, width, width, NR_PAGEFLAGS, NR_PAGEFLAGS, 0);
1028c2ecf20Sopenharmony_ci#ifdef NODE_NOT_IN_PAGE_FLAGS
1038c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags",
1048c2ecf20Sopenharmony_ci		"Node not in page flags");
1058c2ecf20Sopenharmony_ci#endif
1068c2ecf20Sopenharmony_ci#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
1078c2ecf20Sopenharmony_ci	mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodeflags",
1088c2ecf20Sopenharmony_ci		"Last cpupid not in page flags");
1098c2ecf20Sopenharmony_ci#endif
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (SECTIONS_WIDTH) {
1128c2ecf20Sopenharmony_ci		shift -= SECTIONS_WIDTH;
1138c2ecf20Sopenharmony_ci		BUG_ON(shift != SECTIONS_PGSHIFT);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci	if (NODES_WIDTH) {
1168c2ecf20Sopenharmony_ci		shift -= NODES_WIDTH;
1178c2ecf20Sopenharmony_ci		BUG_ON(shift != NODES_PGSHIFT);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	if (ZONES_WIDTH) {
1208c2ecf20Sopenharmony_ci		shift -= ZONES_WIDTH;
1218c2ecf20Sopenharmony_ci		BUG_ON(shift != ZONES_PGSHIFT);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Check for bitmask overlaps */
1258c2ecf20Sopenharmony_ci	or_mask = (ZONES_MASK << ZONES_PGSHIFT) |
1268c2ecf20Sopenharmony_ci			(NODES_MASK << NODES_PGSHIFT) |
1278c2ecf20Sopenharmony_ci			(SECTIONS_MASK << SECTIONS_PGSHIFT);
1288c2ecf20Sopenharmony_ci	add_mask = (ZONES_MASK << ZONES_PGSHIFT) +
1298c2ecf20Sopenharmony_ci			(NODES_MASK << NODES_PGSHIFT) +
1308c2ecf20Sopenharmony_ci			(SECTIONS_MASK << SECTIONS_PGSHIFT);
1318c2ecf20Sopenharmony_ci	BUG_ON(or_mask != add_mask);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic __init int set_mminit_loglevel(char *str)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	get_option(&str, &mminit_loglevel);
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ciearly_param("mminit_loglevel", set_mminit_loglevel);
1408c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_MEMORY_INIT */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistruct kobject *mm_kobj;
1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mm_kobj);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
1468c2ecf20Sopenharmony_cis32 vm_committed_as_batch = 32;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_civoid mm_compute_batch(int overcommit_policy)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	u64 memsized_batch;
1518c2ecf20Sopenharmony_ci	s32 nr = num_present_cpus();
1528c2ecf20Sopenharmony_ci	s32 batch = max_t(s32, nr*2, 32);
1538c2ecf20Sopenharmony_ci	unsigned long ram_pages = totalram_pages();
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * For policy OVERCOMMIT_NEVER, set batch size to 0.4% of
1578c2ecf20Sopenharmony_ci	 * (total memory/#cpus), and lift it to 25% for other policies
1588c2ecf20Sopenharmony_ci	 * to easy the possible lock contention for percpu_counter
1598c2ecf20Sopenharmony_ci	 * vm_committed_as, while the max limit is INT_MAX
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	if (overcommit_policy == OVERCOMMIT_NEVER)
1628c2ecf20Sopenharmony_ci		memsized_batch = min_t(u64, ram_pages/nr/256, INT_MAX);
1638c2ecf20Sopenharmony_ci	else
1648c2ecf20Sopenharmony_ci		memsized_batch = min_t(u64, ram_pages/nr/4, INT_MAX);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	vm_committed_as_batch = max_t(s32, memsized_batch, batch);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int __meminit mm_compute_batch_notifier(struct notifier_block *self,
1708c2ecf20Sopenharmony_ci					unsigned long action, void *arg)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	switch (action) {
1738c2ecf20Sopenharmony_ci	case MEM_ONLINE:
1748c2ecf20Sopenharmony_ci	case MEM_OFFLINE:
1758c2ecf20Sopenharmony_ci		mm_compute_batch(sysctl_overcommit_memory);
1768c2ecf20Sopenharmony_ci	default:
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	return NOTIFY_OK;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic struct notifier_block compute_batch_nb __meminitdata = {
1838c2ecf20Sopenharmony_ci	.notifier_call = mm_compute_batch_notifier,
1848c2ecf20Sopenharmony_ci	.priority = IPC_CALLBACK_PRI, /* use lowest priority */
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int __init mm_compute_batch_init(void)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	mm_compute_batch(sysctl_overcommit_memory);
1908c2ecf20Sopenharmony_ci	register_hotmemory_notifier(&compute_batch_nb);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci__initcall(mm_compute_batch_init);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int __init mm_sysfs_init(void)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	mm_kobj = kobject_create_and_add("mm", kernel_kobj);
2028c2ecf20Sopenharmony_ci	if (!mm_kobj)
2038c2ecf20Sopenharmony_ci		return -ENOMEM;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_cipostcore_initcall(mm_sysfs_init);
208