xref: /openbmc/linux/drivers/base/node.c (revision 08d9dbe7)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
310fbcf4cSKay Sievers  * Basic Node interface support
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include <linux/module.h>
71da177e4SLinus Torvalds #include <linux/init.h>
81da177e4SLinus Torvalds #include <linux/mm.h>
9c04fc586SGary Hade #include <linux/memory.h>
10fa25c503SKOSAKI Motohiro #include <linux/vmstat.h>
116e259e7dSAndrew Morton #include <linux/notifier.h>
121da177e4SLinus Torvalds #include <linux/node.h>
131da177e4SLinus Torvalds #include <linux/hugetlb.h>
14ed4a6d7fSMel Gorman #include <linux/compaction.h>
151da177e4SLinus Torvalds #include <linux/cpumask.h>
161da177e4SLinus Torvalds #include <linux/topology.h>
171da177e4SLinus Torvalds #include <linux/nodemask.h>
1876b67ed9SKAMEZAWA Hiroyuki #include <linux/cpu.h>
19bde631a5SLee Schermerhorn #include <linux/device.h>
2008d9dbe7SKeith Busch #include <linux/pm_runtime.h>
21af936a16SLee Schermerhorn #include <linux/swap.h>
2218e5b539STejun Heo #include <linux/slab.h>
231da177e4SLinus Torvalds 
2410fbcf4cSKay Sievers static struct bus_type node_subsys = {
25af5ca3f4SKay Sievers 	.name = "node",
2610fbcf4cSKay Sievers 	.dev_name = "node",
271da177e4SLinus Torvalds };
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 
305aaba363SSudeep Holla static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf)
311da177e4SLinus Torvalds {
32064f0e93SZhen Lei 	ssize_t n;
33064f0e93SZhen Lei 	cpumask_var_t mask;
341da177e4SLinus Torvalds 	struct node *node_dev = to_node(dev);
351da177e4SLinus Torvalds 
3639106dcfSMike Travis 	/* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */
3739106dcfSMike Travis 	BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1));
381da177e4SLinus Torvalds 
39064f0e93SZhen Lei 	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
40064f0e93SZhen Lei 		return 0;
41064f0e93SZhen Lei 
42064f0e93SZhen Lei 	cpumask_and(mask, cpumask_of_node(node_dev->dev.id), cpu_online_mask);
43064f0e93SZhen Lei 	n = cpumap_print_to_pagebuf(list, buf, mask);
44064f0e93SZhen Lei 	free_cpumask_var(mask);
45064f0e93SZhen Lei 
46064f0e93SZhen Lei 	return n;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
4910fbcf4cSKay Sievers static inline ssize_t node_read_cpumask(struct device *dev,
5010fbcf4cSKay Sievers 				struct device_attribute *attr, char *buf)
5139106dcfSMike Travis {
525aaba363SSudeep Holla 	return node_read_cpumap(dev, false, buf);
5339106dcfSMike Travis }
5410fbcf4cSKay Sievers static inline ssize_t node_read_cpulist(struct device *dev,
5510fbcf4cSKay Sievers 				struct device_attribute *attr, char *buf)
5639106dcfSMike Travis {
575aaba363SSudeep Holla 	return node_read_cpumap(dev, true, buf);
5839106dcfSMike Travis }
5939106dcfSMike Travis 
6010fbcf4cSKay Sievers static DEVICE_ATTR(cpumap,  S_IRUGO, node_read_cpumask, NULL);
6110fbcf4cSKay Sievers static DEVICE_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL);
621da177e4SLinus Torvalds 
6308d9dbe7SKeith Busch /**
6408d9dbe7SKeith Busch  * struct node_access_nodes - Access class device to hold user visible
6508d9dbe7SKeith Busch  * 			      relationships to other nodes.
6608d9dbe7SKeith Busch  * @dev:	Device for this memory access class
6708d9dbe7SKeith Busch  * @list_node:	List element in the node's access list
6808d9dbe7SKeith Busch  * @access:	The access class rank
6908d9dbe7SKeith Busch  */
7008d9dbe7SKeith Busch struct node_access_nodes {
7108d9dbe7SKeith Busch 	struct device		dev;
7208d9dbe7SKeith Busch 	struct list_head	list_node;
7308d9dbe7SKeith Busch 	unsigned		access;
7408d9dbe7SKeith Busch };
7508d9dbe7SKeith Busch #define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev)
7608d9dbe7SKeith Busch 
7708d9dbe7SKeith Busch static struct attribute *node_init_access_node_attrs[] = {
7808d9dbe7SKeith Busch 	NULL,
7908d9dbe7SKeith Busch };
8008d9dbe7SKeith Busch 
8108d9dbe7SKeith Busch static struct attribute *node_targ_access_node_attrs[] = {
8208d9dbe7SKeith Busch 	NULL,
8308d9dbe7SKeith Busch };
8408d9dbe7SKeith Busch 
8508d9dbe7SKeith Busch static const struct attribute_group initiators = {
8608d9dbe7SKeith Busch 	.name	= "initiators",
8708d9dbe7SKeith Busch 	.attrs	= node_init_access_node_attrs,
8808d9dbe7SKeith Busch };
8908d9dbe7SKeith Busch 
9008d9dbe7SKeith Busch static const struct attribute_group targets = {
9108d9dbe7SKeith Busch 	.name	= "targets",
9208d9dbe7SKeith Busch 	.attrs	= node_targ_access_node_attrs,
9308d9dbe7SKeith Busch };
9408d9dbe7SKeith Busch 
9508d9dbe7SKeith Busch static const struct attribute_group *node_access_node_groups[] = {
9608d9dbe7SKeith Busch 	&initiators,
9708d9dbe7SKeith Busch 	&targets,
9808d9dbe7SKeith Busch 	NULL,
9908d9dbe7SKeith Busch };
10008d9dbe7SKeith Busch 
10108d9dbe7SKeith Busch static void node_remove_accesses(struct node *node)
10208d9dbe7SKeith Busch {
10308d9dbe7SKeith Busch 	struct node_access_nodes *c, *cnext;
10408d9dbe7SKeith Busch 
10508d9dbe7SKeith Busch 	list_for_each_entry_safe(c, cnext, &node->access_list, list_node) {
10608d9dbe7SKeith Busch 		list_del(&c->list_node);
10708d9dbe7SKeith Busch 		device_unregister(&c->dev);
10808d9dbe7SKeith Busch 	}
10908d9dbe7SKeith Busch }
11008d9dbe7SKeith Busch 
11108d9dbe7SKeith Busch static void node_access_release(struct device *dev)
11208d9dbe7SKeith Busch {
11308d9dbe7SKeith Busch 	kfree(to_access_nodes(dev));
11408d9dbe7SKeith Busch }
11508d9dbe7SKeith Busch 
11608d9dbe7SKeith Busch static struct node_access_nodes *node_init_node_access(struct node *node,
11708d9dbe7SKeith Busch 						       unsigned access)
11808d9dbe7SKeith Busch {
11908d9dbe7SKeith Busch 	struct node_access_nodes *access_node;
12008d9dbe7SKeith Busch 	struct device *dev;
12108d9dbe7SKeith Busch 
12208d9dbe7SKeith Busch 	list_for_each_entry(access_node, &node->access_list, list_node)
12308d9dbe7SKeith Busch 		if (access_node->access == access)
12408d9dbe7SKeith Busch 			return access_node;
12508d9dbe7SKeith Busch 
12608d9dbe7SKeith Busch 	access_node = kzalloc(sizeof(*access_node), GFP_KERNEL);
12708d9dbe7SKeith Busch 	if (!access_node)
12808d9dbe7SKeith Busch 		return NULL;
12908d9dbe7SKeith Busch 
13008d9dbe7SKeith Busch 	access_node->access = access;
13108d9dbe7SKeith Busch 	dev = &access_node->dev;
13208d9dbe7SKeith Busch 	dev->parent = &node->dev;
13308d9dbe7SKeith Busch 	dev->release = node_access_release;
13408d9dbe7SKeith Busch 	dev->groups = node_access_node_groups;
13508d9dbe7SKeith Busch 	if (dev_set_name(dev, "access%u", access))
13608d9dbe7SKeith Busch 		goto free;
13708d9dbe7SKeith Busch 
13808d9dbe7SKeith Busch 	if (device_register(dev))
13908d9dbe7SKeith Busch 		goto free_name;
14008d9dbe7SKeith Busch 
14108d9dbe7SKeith Busch 	pm_runtime_no_callbacks(dev);
14208d9dbe7SKeith Busch 	list_add_tail(&access_node->list_node, &node->access_list);
14308d9dbe7SKeith Busch 	return access_node;
14408d9dbe7SKeith Busch free_name:
14508d9dbe7SKeith Busch 	kfree_const(dev->kobj.name);
14608d9dbe7SKeith Busch free:
14708d9dbe7SKeith Busch 	kfree(access_node);
14808d9dbe7SKeith Busch 	return NULL;
14908d9dbe7SKeith Busch }
15008d9dbe7SKeith Busch 
1511da177e4SLinus Torvalds #define K(x) ((x) << (PAGE_SHIFT - 10))
15210fbcf4cSKay Sievers static ssize_t node_read_meminfo(struct device *dev,
15310fbcf4cSKay Sievers 			struct device_attribute *attr, char *buf)
1541da177e4SLinus Torvalds {
1551da177e4SLinus Torvalds 	int n;
1561da177e4SLinus Torvalds 	int nid = dev->id;
157599d0c95SMel Gorman 	struct pglist_data *pgdat = NODE_DATA(nid);
1581da177e4SLinus Torvalds 	struct sysinfo i;
15961f94e18SVlastimil Babka 	unsigned long sreclaimable, sunreclaimable;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	si_meminfo_node(&i, nid);
16261f94e18SVlastimil Babka 	sreclaimable = node_page_state(pgdat, NR_SLAB_RECLAIMABLE);
16361f94e18SVlastimil Babka 	sunreclaimable = node_page_state(pgdat, NR_SLAB_UNRECLAIMABLE);
1647ee92255SKOSAKI Motohiro 	n = sprintf(buf,
1651da177e4SLinus Torvalds 		       "Node %d MemTotal:       %8lu kB\n"
1661da177e4SLinus Torvalds 		       "Node %d MemFree:        %8lu kB\n"
1671da177e4SLinus Torvalds 		       "Node %d MemUsed:        %8lu kB\n"
1681da177e4SLinus Torvalds 		       "Node %d Active:         %8lu kB\n"
1691da177e4SLinus Torvalds 		       "Node %d Inactive:       %8lu kB\n"
1704f98a2feSRik van Riel 		       "Node %d Active(anon):   %8lu kB\n"
1714f98a2feSRik van Riel 		       "Node %d Inactive(anon): %8lu kB\n"
1724f98a2feSRik van Riel 		       "Node %d Active(file):   %8lu kB\n"
1734f98a2feSRik van Riel 		       "Node %d Inactive(file): %8lu kB\n"
1745344b7e6SNick Piggin 		       "Node %d Unevictable:    %8lu kB\n"
1757ee92255SKOSAKI Motohiro 		       "Node %d Mlocked:        %8lu kB\n",
1767ee92255SKOSAKI Motohiro 		       nid, K(i.totalram),
1777ee92255SKOSAKI Motohiro 		       nid, K(i.freeram),
1787ee92255SKOSAKI Motohiro 		       nid, K(i.totalram - i.freeram),
179599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_ACTIVE_ANON) +
180599d0c95SMel Gorman 				node_page_state(pgdat, NR_ACTIVE_FILE)),
181599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_INACTIVE_ANON) +
182599d0c95SMel Gorman 				node_page_state(pgdat, NR_INACTIVE_FILE)),
183599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_ACTIVE_ANON)),
184599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_INACTIVE_ANON)),
185599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_ACTIVE_FILE)),
186599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_INACTIVE_FILE)),
187599d0c95SMel Gorman 		       nid, K(node_page_state(pgdat, NR_UNEVICTABLE)),
18875ef7184SMel Gorman 		       nid, K(sum_zone_node_page_state(nid, NR_MLOCK)));
1897ee92255SKOSAKI Motohiro 
190182e8e23SChristoph Lameter #ifdef CONFIG_HIGHMEM
1917ee92255SKOSAKI Motohiro 	n += sprintf(buf + n,
1921da177e4SLinus Torvalds 		       "Node %d HighTotal:      %8lu kB\n"
1931da177e4SLinus Torvalds 		       "Node %d HighFree:       %8lu kB\n"
1941da177e4SLinus Torvalds 		       "Node %d LowTotal:       %8lu kB\n"
1957ee92255SKOSAKI Motohiro 		       "Node %d LowFree:        %8lu kB\n",
1967ee92255SKOSAKI Motohiro 		       nid, K(i.totalhigh),
1977ee92255SKOSAKI Motohiro 		       nid, K(i.freehigh),
1987ee92255SKOSAKI Motohiro 		       nid, K(i.totalram - i.totalhigh),
1997ee92255SKOSAKI Motohiro 		       nid, K(i.freeram - i.freehigh));
200182e8e23SChristoph Lameter #endif
2017ee92255SKOSAKI Motohiro 	n += sprintf(buf + n,
202c07e02dbSMartin Hicks 		       "Node %d Dirty:          %8lu kB\n"
203c07e02dbSMartin Hicks 		       "Node %d Writeback:      %8lu kB\n"
204347ce434SChristoph Lameter 		       "Node %d FilePages:      %8lu kB\n"
205c07e02dbSMartin Hicks 		       "Node %d Mapped:         %8lu kB\n"
206f3dbd344SChristoph Lameter 		       "Node %d AnonPages:      %8lu kB\n"
2074b02108aSKOSAKI Motohiro 		       "Node %d Shmem:          %8lu kB\n"
208c6a7f572SKOSAKI Motohiro 		       "Node %d KernelStack:    %8lu kB\n"
209df849a15SChristoph Lameter 		       "Node %d PageTables:     %8lu kB\n"
210f5ef68daSAndrew Morton 		       "Node %d NFS_Unstable:   %8lu kB\n"
211d2c5e30cSChristoph Lameter 		       "Node %d Bounce:         %8lu kB\n"
212fc3ba692SMiklos Szeredi 		       "Node %d WritebackTmp:   %8lu kB\n"
21361f94e18SVlastimil Babka 		       "Node %d KReclaimable:   %8lu kB\n"
214972d1a7bSChristoph Lameter 		       "Node %d Slab:           %8lu kB\n"
215972d1a7bSChristoph Lameter 		       "Node %d SReclaimable:   %8lu kB\n"
21605b258e9SDavid Rientjes 		       "Node %d SUnreclaim:     %8lu kB\n"
21705b258e9SDavid Rientjes #ifdef CONFIG_TRANSPARENT_HUGEPAGE
21805b258e9SDavid Rientjes 		       "Node %d AnonHugePages:  %8lu kB\n"
21965c45377SKirill A. Shutemov 		       "Node %d ShmemHugePages: %8lu kB\n"
22065c45377SKirill A. Shutemov 		       "Node %d ShmemPmdMapped: %8lu kB\n"
22105b258e9SDavid Rientjes #endif
22205b258e9SDavid Rientjes 			,
22311fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_FILE_DIRTY)),
22411fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_WRITEBACK)),
22511fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_FILE_PAGES)),
22650658e2eSMel Gorman 		       nid, K(node_page_state(pgdat, NR_FILE_MAPPED)),
2274b9d0fabSMel Gorman 		       nid, K(node_page_state(pgdat, NR_ANON_MAPPED)),
228cc7452b6SRafael Aquini 		       nid, K(i.sharedram),
229d30dd8beSAndy Lutomirski 		       nid, sum_zone_node_page_state(nid, NR_KERNEL_STACK_KB),
23075ef7184SMel Gorman 		       nid, K(sum_zone_node_page_state(nid, NR_PAGETABLE)),
23111fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
23275ef7184SMel Gorman 		       nid, K(sum_zone_node_page_state(nid, NR_BOUNCE)),
23311fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
23461f94e18SVlastimil Babka 		       nid, K(sreclaimable +
23561f94e18SVlastimil Babka 			      node_page_state(pgdat, NR_KERNEL_MISC_RECLAIMABLE)),
23661f94e18SVlastimil Babka 		       nid, K(sreclaimable + sunreclaimable),
23761f94e18SVlastimil Babka 		       nid, K(sreclaimable),
23861f94e18SVlastimil Babka 		       nid, K(sunreclaimable)
23905b258e9SDavid Rientjes #ifdef CONFIG_TRANSPARENT_HUGEPAGE
24061f94e18SVlastimil Babka 		       ,
24111fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_ANON_THPS) *
24265c45377SKirill A. Shutemov 				       HPAGE_PMD_NR),
24311fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_SHMEM_THPS) *
24465c45377SKirill A. Shutemov 				       HPAGE_PMD_NR),
24511fb9989SMel Gorman 		       nid, K(node_page_state(pgdat, NR_SHMEM_PMDMAPPED) *
24661f94e18SVlastimil Babka 				       HPAGE_PMD_NR)
24705b258e9SDavid Rientjes #endif
24861f94e18SVlastimil Babka 		       );
2491da177e4SLinus Torvalds 	n += hugetlb_report_node_meminfo(nid, buf + n);
2501da177e4SLinus Torvalds 	return n;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds #undef K
25410fbcf4cSKay Sievers static DEVICE_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL);
2551da177e4SLinus Torvalds 
25610fbcf4cSKay Sievers static ssize_t node_read_numastat(struct device *dev,
25710fbcf4cSKay Sievers 				struct device_attribute *attr, char *buf)
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds 	return sprintf(buf,
2601da177e4SLinus Torvalds 		       "numa_hit %lu\n"
2611da177e4SLinus Torvalds 		       "numa_miss %lu\n"
2621da177e4SLinus Torvalds 		       "numa_foreign %lu\n"
2631da177e4SLinus Torvalds 		       "interleave_hit %lu\n"
2641da177e4SLinus Torvalds 		       "local_node %lu\n"
2651da177e4SLinus Torvalds 		       "other_node %lu\n",
2663a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_HIT),
2673a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_MISS),
2683a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_FOREIGN),
2693a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_INTERLEAVE_HIT),
2703a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_LOCAL),
2713a321d2aSKemi Wang 		       sum_zone_numa_state(dev->id, NUMA_OTHER));
2721da177e4SLinus Torvalds }
27310fbcf4cSKay Sievers static DEVICE_ATTR(numastat, S_IRUGO, node_read_numastat, NULL);
2741da177e4SLinus Torvalds 
27510fbcf4cSKay Sievers static ssize_t node_read_vmstat(struct device *dev,
27610fbcf4cSKay Sievers 				struct device_attribute *attr, char *buf)
2772ac39037SMichael Rubin {
2782ac39037SMichael Rubin 	int nid = dev->id;
27975ef7184SMel Gorman 	struct pglist_data *pgdat = NODE_DATA(nid);
280fa25c503SKOSAKI Motohiro 	int i;
281fa25c503SKOSAKI Motohiro 	int n = 0;
282fa25c503SKOSAKI Motohiro 
283fa25c503SKOSAKI Motohiro 	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
284fa25c503SKOSAKI Motohiro 		n += sprintf(buf+n, "%s %lu\n", vmstat_text[i],
28575ef7184SMel Gorman 			     sum_zone_node_page_state(nid, i));
28675ef7184SMel Gorman 
2873a321d2aSKemi Wang #ifdef CONFIG_NUMA
2883a321d2aSKemi Wang 	for (i = 0; i < NR_VM_NUMA_STAT_ITEMS; i++)
28975ef7184SMel Gorman 		n += sprintf(buf+n, "%s %lu\n",
29075ef7184SMel Gorman 			     vmstat_text[i + NR_VM_ZONE_STAT_ITEMS],
2913a321d2aSKemi Wang 			     sum_zone_numa_state(nid, i));
2923a321d2aSKemi Wang #endif
2933a321d2aSKemi Wang 
2943a321d2aSKemi Wang 	for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)
2953a321d2aSKemi Wang 		n += sprintf(buf+n, "%s %lu\n",
2963a321d2aSKemi Wang 			     vmstat_text[i + NR_VM_ZONE_STAT_ITEMS +
2973a321d2aSKemi Wang 			     NR_VM_NUMA_STAT_ITEMS],
29875ef7184SMel Gorman 			     node_page_state(pgdat, i));
299fa25c503SKOSAKI Motohiro 
300fa25c503SKOSAKI Motohiro 	return n;
3012ac39037SMichael Rubin }
30210fbcf4cSKay Sievers static DEVICE_ATTR(vmstat, S_IRUGO, node_read_vmstat, NULL);
3032ac39037SMichael Rubin 
30410fbcf4cSKay Sievers static ssize_t node_read_distance(struct device *dev,
30510fbcf4cSKay Sievers 			struct device_attribute *attr, char *buf)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds 	int nid = dev->id;
3081da177e4SLinus Torvalds 	int len = 0;
3091da177e4SLinus Torvalds 	int i;
3101da177e4SLinus Torvalds 
31112ee3c0aSDavid Rientjes 	/*
31212ee3c0aSDavid Rientjes 	 * buf is currently PAGE_SIZE in length and each node needs 4 chars
31312ee3c0aSDavid Rientjes 	 * at the most (distance + space or newline).
31412ee3c0aSDavid Rientjes 	 */
31512ee3c0aSDavid Rientjes 	BUILD_BUG_ON(MAX_NUMNODES * 4 > PAGE_SIZE);
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	for_each_online_node(i)
3181da177e4SLinus Torvalds 		len += sprintf(buf + len, "%s%d", i ? " " : "", node_distance(nid, i));
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	len += sprintf(buf + len, "\n");
3211da177e4SLinus Torvalds 	return len;
3221da177e4SLinus Torvalds }
32310fbcf4cSKay Sievers static DEVICE_ATTR(distance, S_IRUGO, node_read_distance, NULL);
3241da177e4SLinus Torvalds 
3253c9b8aafSTakashi Iwai static struct attribute *node_dev_attrs[] = {
3263c9b8aafSTakashi Iwai 	&dev_attr_cpumap.attr,
3273c9b8aafSTakashi Iwai 	&dev_attr_cpulist.attr,
3283c9b8aafSTakashi Iwai 	&dev_attr_meminfo.attr,
3293c9b8aafSTakashi Iwai 	&dev_attr_numastat.attr,
3303c9b8aafSTakashi Iwai 	&dev_attr_distance.attr,
3313c9b8aafSTakashi Iwai 	&dev_attr_vmstat.attr,
3323c9b8aafSTakashi Iwai 	NULL
3333c9b8aafSTakashi Iwai };
3347ca7ec40SGreg Kroah-Hartman ATTRIBUTE_GROUPS(node_dev);
3353c9b8aafSTakashi Iwai 
3369a305230SLee Schermerhorn #ifdef CONFIG_HUGETLBFS
3379a305230SLee Schermerhorn /*
3389a305230SLee Schermerhorn  * hugetlbfs per node attributes registration interface:
3399a305230SLee Schermerhorn  * When/if hugetlb[fs] subsystem initializes [sometime after this module],
3404faf8d95SLee Schermerhorn  * it will register its per node attributes for all online nodes with
3414faf8d95SLee Schermerhorn  * memory.  It will also call register_hugetlbfs_with_node(), below, to
3429a305230SLee Schermerhorn  * register its attribute registration functions with this node driver.
3439a305230SLee Schermerhorn  * Once these hooks have been initialized, the node driver will call into
3449a305230SLee Schermerhorn  * the hugetlb module to [un]register attributes for hot-plugged nodes.
3459a305230SLee Schermerhorn  */
3469a305230SLee Schermerhorn static node_registration_func_t __hugetlb_register_node;
3479a305230SLee Schermerhorn static node_registration_func_t __hugetlb_unregister_node;
3489a305230SLee Schermerhorn 
34939da08cbSLee Schermerhorn static inline bool hugetlb_register_node(struct node *node)
3509a305230SLee Schermerhorn {
3514faf8d95SLee Schermerhorn 	if (__hugetlb_register_node &&
3528cebfcd0SLai Jiangshan 			node_state(node->dev.id, N_MEMORY)) {
3539a305230SLee Schermerhorn 		__hugetlb_register_node(node);
35439da08cbSLee Schermerhorn 		return true;
35539da08cbSLee Schermerhorn 	}
35639da08cbSLee Schermerhorn 	return false;
3579a305230SLee Schermerhorn }
3589a305230SLee Schermerhorn 
3599a305230SLee Schermerhorn static inline void hugetlb_unregister_node(struct node *node)
3609a305230SLee Schermerhorn {
3619a305230SLee Schermerhorn 	if (__hugetlb_unregister_node)
3629a305230SLee Schermerhorn 		__hugetlb_unregister_node(node);
3639a305230SLee Schermerhorn }
3649a305230SLee Schermerhorn 
3659a305230SLee Schermerhorn void register_hugetlbfs_with_node(node_registration_func_t doregister,
3669a305230SLee Schermerhorn 				  node_registration_func_t unregister)
3679a305230SLee Schermerhorn {
3689a305230SLee Schermerhorn 	__hugetlb_register_node   = doregister;
3699a305230SLee Schermerhorn 	__hugetlb_unregister_node = unregister;
3709a305230SLee Schermerhorn }
3719a305230SLee Schermerhorn #else
3729a305230SLee Schermerhorn static inline void hugetlb_register_node(struct node *node) {}
3739a305230SLee Schermerhorn 
3749a305230SLee Schermerhorn static inline void hugetlb_unregister_node(struct node *node) {}
3759a305230SLee Schermerhorn #endif
3769a305230SLee Schermerhorn 
3778c7b5b4eSYasuaki Ishimatsu static void node_device_release(struct device *dev)
3788c7b5b4eSYasuaki Ishimatsu {
3798c7b5b4eSYasuaki Ishimatsu 	struct node *node = to_node(dev);
3808c7b5b4eSYasuaki Ishimatsu 
3818c7b5b4eSYasuaki Ishimatsu #if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) && defined(CONFIG_HUGETLBFS)
3828c7b5b4eSYasuaki Ishimatsu 	/*
3838c7b5b4eSYasuaki Ishimatsu 	 * We schedule the work only when a memory section is
3848c7b5b4eSYasuaki Ishimatsu 	 * onlined/offlined on this node. When we come here,
3858c7b5b4eSYasuaki Ishimatsu 	 * all the memory on this node has been offlined,
3868c7b5b4eSYasuaki Ishimatsu 	 * so we won't enqueue new work to this work.
3878c7b5b4eSYasuaki Ishimatsu 	 *
3888c7b5b4eSYasuaki Ishimatsu 	 * The work is using node->node_work, so we should
3898c7b5b4eSYasuaki Ishimatsu 	 * flush work before freeing the memory.
3908c7b5b4eSYasuaki Ishimatsu 	 */
3918c7b5b4eSYasuaki Ishimatsu 	flush_work(&node->node_work);
3928c7b5b4eSYasuaki Ishimatsu #endif
3938c7b5b4eSYasuaki Ishimatsu 	kfree(node);
3948c7b5b4eSYasuaki Ishimatsu }
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds /*
397405ae7d3SRobert P. J. Day  * register_node - Setup a sysfs device for a node.
3981da177e4SLinus Torvalds  * @num - Node number to use when creating the device.
3991da177e4SLinus Torvalds  *
4001da177e4SLinus Torvalds  * Initialize and register the node device.
4011da177e4SLinus Torvalds  */
402a7be6e5aSDou Liyang static int register_node(struct node *node, int num)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	int error;
4051da177e4SLinus Torvalds 
40610fbcf4cSKay Sievers 	node->dev.id = num;
40710fbcf4cSKay Sievers 	node->dev.bus = &node_subsys;
4088c7b5b4eSYasuaki Ishimatsu 	node->dev.release = node_device_release;
4097ca7ec40SGreg Kroah-Hartman 	node->dev.groups = node_dev_groups;
41010fbcf4cSKay Sievers 	error = device_register(&node->dev);
4111da177e4SLinus Torvalds 
412c1cc0d51SArvind Yadav 	if (error)
413c1cc0d51SArvind Yadav 		put_device(&node->dev);
414c1cc0d51SArvind Yadav 	else {
4159a305230SLee Schermerhorn 		hugetlb_register_node(node);
416ed4a6d7fSMel Gorman 
417ed4a6d7fSMel Gorman 		compaction_register_node(node);
4181da177e4SLinus Torvalds 	}
4191da177e4SLinus Torvalds 	return error;
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
4224b45099bSKeiichiro Tokunaga /**
4234b45099bSKeiichiro Tokunaga  * unregister_node - unregister a node device
4244b45099bSKeiichiro Tokunaga  * @node: node going away
4254b45099bSKeiichiro Tokunaga  *
4264b45099bSKeiichiro Tokunaga  * Unregisters a node device @node.  All the devices on the node must be
4274b45099bSKeiichiro Tokunaga  * unregistered before calling this function.
4284b45099bSKeiichiro Tokunaga  */
4294b45099bSKeiichiro Tokunaga void unregister_node(struct node *node)
4304b45099bSKeiichiro Tokunaga {
4314faf8d95SLee Schermerhorn 	hugetlb_unregister_node(node);		/* no-op, if memoryless node */
43208d9dbe7SKeith Busch 	node_remove_accesses(node);
43310fbcf4cSKay Sievers 	device_unregister(&node->dev);
4344b45099bSKeiichiro Tokunaga }
4354b45099bSKeiichiro Tokunaga 
4368732794bSWen Congyang struct node *node_devices[MAX_NUMNODES];
4370fc44159SYasunori Goto 
43876b67ed9SKAMEZAWA Hiroyuki /*
43976b67ed9SKAMEZAWA Hiroyuki  * register cpu under node
44076b67ed9SKAMEZAWA Hiroyuki  */
44176b67ed9SKAMEZAWA Hiroyuki int register_cpu_under_node(unsigned int cpu, unsigned int nid)
44276b67ed9SKAMEZAWA Hiroyuki {
4431830794aSAlex Chiang 	int ret;
4448a25a2fdSKay Sievers 	struct device *obj;
445f8246f31SAlex Chiang 
446f8246f31SAlex Chiang 	if (!node_online(nid))
447f8246f31SAlex Chiang 		return 0;
448f8246f31SAlex Chiang 
4498a25a2fdSKay Sievers 	obj = get_cpu_device(cpu);
45076b67ed9SKAMEZAWA Hiroyuki 	if (!obj)
45176b67ed9SKAMEZAWA Hiroyuki 		return 0;
452f8246f31SAlex Chiang 
4538732794bSWen Congyang 	ret = sysfs_create_link(&node_devices[nid]->dev.kobj,
45476b67ed9SKAMEZAWA Hiroyuki 				&obj->kobj,
45576b67ed9SKAMEZAWA Hiroyuki 				kobject_name(&obj->kobj));
4561830794aSAlex Chiang 	if (ret)
4571830794aSAlex Chiang 		return ret;
4581830794aSAlex Chiang 
4591830794aSAlex Chiang 	return sysfs_create_link(&obj->kobj,
4608732794bSWen Congyang 				 &node_devices[nid]->dev.kobj,
4618732794bSWen Congyang 				 kobject_name(&node_devices[nid]->dev.kobj));
46276b67ed9SKAMEZAWA Hiroyuki }
46376b67ed9SKAMEZAWA Hiroyuki 
46408d9dbe7SKeith Busch /**
46508d9dbe7SKeith Busch  * register_memory_node_under_compute_node - link memory node to its compute
46608d9dbe7SKeith Busch  *					     node for a given access class.
46708d9dbe7SKeith Busch  * @mem_node:	Memory node number
46808d9dbe7SKeith Busch  * @cpu_node:	Cpu  node number
46908d9dbe7SKeith Busch  * @access:	Access class to register
47008d9dbe7SKeith Busch  *
47108d9dbe7SKeith Busch  * Description:
47208d9dbe7SKeith Busch  * 	For use with platforms that may have separate memory and compute nodes.
47308d9dbe7SKeith Busch  * 	This function will export node relationships linking which memory
47408d9dbe7SKeith Busch  * 	initiator nodes can access memory targets at a given ranked access
47508d9dbe7SKeith Busch  * 	class.
47608d9dbe7SKeith Busch  */
47708d9dbe7SKeith Busch int register_memory_node_under_compute_node(unsigned int mem_nid,
47808d9dbe7SKeith Busch 					    unsigned int cpu_nid,
47908d9dbe7SKeith Busch 					    unsigned access)
48008d9dbe7SKeith Busch {
48108d9dbe7SKeith Busch 	struct node *init_node, *targ_node;
48208d9dbe7SKeith Busch 	struct node_access_nodes *initiator, *target;
48308d9dbe7SKeith Busch 	int ret;
48408d9dbe7SKeith Busch 
48508d9dbe7SKeith Busch 	if (!node_online(cpu_nid) || !node_online(mem_nid))
48608d9dbe7SKeith Busch 		return -ENODEV;
48708d9dbe7SKeith Busch 
48808d9dbe7SKeith Busch 	init_node = node_devices[cpu_nid];
48908d9dbe7SKeith Busch 	targ_node = node_devices[mem_nid];
49008d9dbe7SKeith Busch 	initiator = node_init_node_access(init_node, access);
49108d9dbe7SKeith Busch 	target = node_init_node_access(targ_node, access);
49208d9dbe7SKeith Busch 	if (!initiator || !target)
49308d9dbe7SKeith Busch 		return -ENOMEM;
49408d9dbe7SKeith Busch 
49508d9dbe7SKeith Busch 	ret = sysfs_add_link_to_group(&initiator->dev.kobj, "targets",
49608d9dbe7SKeith Busch 				      &targ_node->dev.kobj,
49708d9dbe7SKeith Busch 				      dev_name(&targ_node->dev));
49808d9dbe7SKeith Busch 	if (ret)
49908d9dbe7SKeith Busch 		return ret;
50008d9dbe7SKeith Busch 
50108d9dbe7SKeith Busch 	ret = sysfs_add_link_to_group(&target->dev.kobj, "initiators",
50208d9dbe7SKeith Busch 				      &init_node->dev.kobj,
50308d9dbe7SKeith Busch 				      dev_name(&init_node->dev));
50408d9dbe7SKeith Busch 	if (ret)
50508d9dbe7SKeith Busch 		goto err;
50608d9dbe7SKeith Busch 
50708d9dbe7SKeith Busch 	return 0;
50808d9dbe7SKeith Busch  err:
50908d9dbe7SKeith Busch 	sysfs_remove_link_from_group(&initiator->dev.kobj, "targets",
51008d9dbe7SKeith Busch 				     dev_name(&targ_node->dev));
51108d9dbe7SKeith Busch 	return ret;
51208d9dbe7SKeith Busch }
51308d9dbe7SKeith Busch 
51476b67ed9SKAMEZAWA Hiroyuki int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
51576b67ed9SKAMEZAWA Hiroyuki {
5168a25a2fdSKay Sievers 	struct device *obj;
517b9d52dadSAlex Chiang 
518b9d52dadSAlex Chiang 	if (!node_online(nid))
519b9d52dadSAlex Chiang 		return 0;
520b9d52dadSAlex Chiang 
5218a25a2fdSKay Sievers 	obj = get_cpu_device(cpu);
522b9d52dadSAlex Chiang 	if (!obj)
523b9d52dadSAlex Chiang 		return 0;
524b9d52dadSAlex Chiang 
5258732794bSWen Congyang 	sysfs_remove_link(&node_devices[nid]->dev.kobj,
52676b67ed9SKAMEZAWA Hiroyuki 			  kobject_name(&obj->kobj));
5271830794aSAlex Chiang 	sysfs_remove_link(&obj->kobj,
5288732794bSWen Congyang 			  kobject_name(&node_devices[nid]->dev.kobj));
529b9d52dadSAlex Chiang 
53076b67ed9SKAMEZAWA Hiroyuki 	return 0;
53176b67ed9SKAMEZAWA Hiroyuki }
53276b67ed9SKAMEZAWA Hiroyuki 
533c04fc586SGary Hade #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
534bd721ea7SFabian Frederick static int __ref get_nid_for_pfn(unsigned long pfn)
535c04fc586SGary Hade {
536c04fc586SGary Hade 	if (!pfn_valid_within(pfn))
537c04fc586SGary Hade 		return -1;
5383a80a7faSMel Gorman #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
5398cdde385SThomas Gleixner 	if (system_state < SYSTEM_RUNNING)
5403a80a7faSMel Gorman 		return early_pfn_to_nid(pfn);
5413a80a7faSMel Gorman #endif
542c04fc586SGary Hade 	return pfn_to_nid(pfn);
543c04fc586SGary Hade }
544c04fc586SGary Hade 
545c04fc586SGary Hade /* register memory section under specified node if it spans that node */
5464fbce633SOscar Salvador int register_mem_sect_under_node(struct memory_block *mem_blk, void *arg)
547c04fc586SGary Hade {
5484fbce633SOscar Salvador 	int ret, nid = *(int *)arg;
549c04fc586SGary Hade 	unsigned long pfn, sect_start_pfn, sect_end_pfn;
550c04fc586SGary Hade 
551d0dc12e8SPavel Tatashin 	mem_blk->nid = nid;
552d3360164SNathan Fontenot 
553d3360164SNathan Fontenot 	sect_start_pfn = section_nr_to_pfn(mem_blk->start_section_nr);
554d3360164SNathan Fontenot 	sect_end_pfn = section_nr_to_pfn(mem_blk->end_section_nr);
555d3360164SNathan Fontenot 	sect_end_pfn += PAGES_PER_SECTION - 1;
556c04fc586SGary Hade 	for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
557c04fc586SGary Hade 		int page_nid;
558c04fc586SGary Hade 
55904697858SYinghai Lu 		/*
56004697858SYinghai Lu 		 * memory block could have several absent sections from start.
56104697858SYinghai Lu 		 * skip pfn range from absent section
56204697858SYinghai Lu 		 */
56304697858SYinghai Lu 		if (!pfn_present(pfn)) {
56404697858SYinghai Lu 			pfn = round_down(pfn + PAGES_PER_SECTION,
56504697858SYinghai Lu 					 PAGES_PER_SECTION) - 1;
56604697858SYinghai Lu 			continue;
56704697858SYinghai Lu 		}
56804697858SYinghai Lu 
569fc44f7f9SPavel Tatashin 		/*
570fc44f7f9SPavel Tatashin 		 * We need to check if page belongs to nid only for the boot
571fc44f7f9SPavel Tatashin 		 * case, during hotplug we know that all pages in the memory
572fc44f7f9SPavel Tatashin 		 * block belong to the same node.
573fc44f7f9SPavel Tatashin 		 */
5744fbce633SOscar Salvador 		if (system_state == SYSTEM_BOOTING) {
575c04fc586SGary Hade 			page_nid = get_nid_for_pfn(pfn);
576c04fc586SGary Hade 			if (page_nid < 0)
577c04fc586SGary Hade 				continue;
578c04fc586SGary Hade 			if (page_nid != nid)
579c04fc586SGary Hade 				continue;
580fc44f7f9SPavel Tatashin 		}
5818732794bSWen Congyang 		ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj,
58210fbcf4cSKay Sievers 					&mem_blk->dev.kobj,
58310fbcf4cSKay Sievers 					kobject_name(&mem_blk->dev.kobj));
584dee5d0d5SAlex Chiang 		if (ret)
585dee5d0d5SAlex Chiang 			return ret;
586dee5d0d5SAlex Chiang 
58710fbcf4cSKay Sievers 		return sysfs_create_link_nowarn(&mem_blk->dev.kobj,
5888732794bSWen Congyang 				&node_devices[nid]->dev.kobj,
5898732794bSWen Congyang 				kobject_name(&node_devices[nid]->dev.kobj));
590c04fc586SGary Hade 	}
591c04fc586SGary Hade 	/* mem section does not span the specified node */
592c04fc586SGary Hade 	return 0;
593c04fc586SGary Hade }
594c04fc586SGary Hade 
595c04fc586SGary Hade /* unregister memory section under all nodes that it spans */
596d3360164SNathan Fontenot int unregister_mem_sect_under_nodes(struct memory_block *mem_blk,
597d3360164SNathan Fontenot 				    unsigned long phys_index)
598c04fc586SGary Hade {
5999ae49fabSDavid Rientjes 	NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL);
600c04fc586SGary Hade 	unsigned long pfn, sect_start_pfn, sect_end_pfn;
601c04fc586SGary Hade 
6029ae49fabSDavid Rientjes 	if (!mem_blk) {
6039ae49fabSDavid Rientjes 		NODEMASK_FREE(unlinked_nodes);
604c04fc586SGary Hade 		return -EFAULT;
6059ae49fabSDavid Rientjes 	}
6069ae49fabSDavid Rientjes 	if (!unlinked_nodes)
6079ae49fabSDavid Rientjes 		return -ENOMEM;
6089ae49fabSDavid Rientjes 	nodes_clear(*unlinked_nodes);
609d3360164SNathan Fontenot 
610d3360164SNathan Fontenot 	sect_start_pfn = section_nr_to_pfn(phys_index);
611c04fc586SGary Hade 	sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
612c04fc586SGary Hade 	for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
61347504980SRoel Kluin 		int nid;
614c04fc586SGary Hade 
615c04fc586SGary Hade 		nid = get_nid_for_pfn(pfn);
616c04fc586SGary Hade 		if (nid < 0)
617c04fc586SGary Hade 			continue;
618c04fc586SGary Hade 		if (!node_online(nid))
619c04fc586SGary Hade 			continue;
6209ae49fabSDavid Rientjes 		if (node_test_and_set(nid, *unlinked_nodes))
621c04fc586SGary Hade 			continue;
6228732794bSWen Congyang 		sysfs_remove_link(&node_devices[nid]->dev.kobj,
62310fbcf4cSKay Sievers 			 kobject_name(&mem_blk->dev.kobj));
62410fbcf4cSKay Sievers 		sysfs_remove_link(&mem_blk->dev.kobj,
6258732794bSWen Congyang 			 kobject_name(&node_devices[nid]->dev.kobj));
626c04fc586SGary Hade 	}
6279ae49fabSDavid Rientjes 	NODEMASK_FREE(unlinked_nodes);
628c04fc586SGary Hade 	return 0;
629c04fc586SGary Hade }
630c04fc586SGary Hade 
6314fbce633SOscar Salvador int link_mem_sections(int nid, unsigned long start_pfn, unsigned long end_pfn)
632c04fc586SGary Hade {
6334fbce633SOscar Salvador 	return walk_memory_range(start_pfn, end_pfn, (void *)&nid,
6344fbce633SOscar Salvador 					register_mem_sect_under_node);
635c04fc586SGary Hade }
6364faf8d95SLee Schermerhorn 
63739da08cbSLee Schermerhorn #ifdef CONFIG_HUGETLBFS
6384faf8d95SLee Schermerhorn /*
6394faf8d95SLee Schermerhorn  * Handle per node hstate attribute [un]registration on transistions
6404faf8d95SLee Schermerhorn  * to/from memoryless state.
6414faf8d95SLee Schermerhorn  */
64239da08cbSLee Schermerhorn static void node_hugetlb_work(struct work_struct *work)
64339da08cbSLee Schermerhorn {
64439da08cbSLee Schermerhorn 	struct node *node = container_of(work, struct node, node_work);
64539da08cbSLee Schermerhorn 
64639da08cbSLee Schermerhorn 	/*
64739da08cbSLee Schermerhorn 	 * We only get here when a node transitions to/from memoryless state.
64839da08cbSLee Schermerhorn 	 * We can detect which transition occurred by examining whether the
64939da08cbSLee Schermerhorn 	 * node has memory now.  hugetlb_register_node() already check this
65039da08cbSLee Schermerhorn 	 * so we try to register the attributes.  If that fails, then the
65139da08cbSLee Schermerhorn 	 * node has transitioned to memoryless, try to unregister the
65239da08cbSLee Schermerhorn 	 * attributes.
65339da08cbSLee Schermerhorn 	 */
65439da08cbSLee Schermerhorn 	if (!hugetlb_register_node(node))
65539da08cbSLee Schermerhorn 		hugetlb_unregister_node(node);
65639da08cbSLee Schermerhorn }
65739da08cbSLee Schermerhorn 
65839da08cbSLee Schermerhorn static void init_node_hugetlb_work(int nid)
65939da08cbSLee Schermerhorn {
6608732794bSWen Congyang 	INIT_WORK(&node_devices[nid]->node_work, node_hugetlb_work);
66139da08cbSLee Schermerhorn }
6624faf8d95SLee Schermerhorn 
6634faf8d95SLee Schermerhorn static int node_memory_callback(struct notifier_block *self,
6644faf8d95SLee Schermerhorn 				unsigned long action, void *arg)
6654faf8d95SLee Schermerhorn {
6664faf8d95SLee Schermerhorn 	struct memory_notify *mnb = arg;
6674faf8d95SLee Schermerhorn 	int nid = mnb->status_change_nid;
6684faf8d95SLee Schermerhorn 
6694faf8d95SLee Schermerhorn 	switch (action) {
67039da08cbSLee Schermerhorn 	case MEM_ONLINE:
67139da08cbSLee Schermerhorn 	case MEM_OFFLINE:
67239da08cbSLee Schermerhorn 		/*
67339da08cbSLee Schermerhorn 		 * offload per node hstate [un]registration to a work thread
67439da08cbSLee Schermerhorn 		 * when transitioning to/from memoryless state.
67539da08cbSLee Schermerhorn 		 */
6764faf8d95SLee Schermerhorn 		if (nid != NUMA_NO_NODE)
6778732794bSWen Congyang 			schedule_work(&node_devices[nid]->node_work);
6784faf8d95SLee Schermerhorn 		break;
67939da08cbSLee Schermerhorn 
6804faf8d95SLee Schermerhorn 	case MEM_GOING_ONLINE:
6814faf8d95SLee Schermerhorn 	case MEM_GOING_OFFLINE:
6824faf8d95SLee Schermerhorn 	case MEM_CANCEL_ONLINE:
6834faf8d95SLee Schermerhorn 	case MEM_CANCEL_OFFLINE:
6844faf8d95SLee Schermerhorn 	default:
6854faf8d95SLee Schermerhorn 		break;
6864faf8d95SLee Schermerhorn 	}
6874faf8d95SLee Schermerhorn 
6884faf8d95SLee Schermerhorn 	return NOTIFY_OK;
6894faf8d95SLee Schermerhorn }
69039da08cbSLee Schermerhorn #endif	/* CONFIG_HUGETLBFS */
69139da08cbSLee Schermerhorn #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
69239da08cbSLee Schermerhorn 
69339da08cbSLee Schermerhorn #if !defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || \
69439da08cbSLee Schermerhorn     !defined(CONFIG_HUGETLBFS)
6954faf8d95SLee Schermerhorn static inline int node_memory_callback(struct notifier_block *self,
6964faf8d95SLee Schermerhorn 				unsigned long action, void *arg)
6974faf8d95SLee Schermerhorn {
6984faf8d95SLee Schermerhorn 	return NOTIFY_OK;
6994faf8d95SLee Schermerhorn }
70039da08cbSLee Schermerhorn 
70139da08cbSLee Schermerhorn static void init_node_hugetlb_work(int nid) { }
70239da08cbSLee Schermerhorn 
70339da08cbSLee Schermerhorn #endif
704c04fc586SGary Hade 
7059037a993SMichal Hocko int __register_one_node(int nid)
7060fc44159SYasunori Goto {
7079037a993SMichal Hocko 	int error;
7089037a993SMichal Hocko 	int cpu;
7090fc44159SYasunori Goto 
7108732794bSWen Congyang 	node_devices[nid] = kzalloc(sizeof(struct node), GFP_KERNEL);
7118732794bSWen Congyang 	if (!node_devices[nid])
7128732794bSWen Congyang 		return -ENOMEM;
7138732794bSWen Congyang 
714a7be6e5aSDou Liyang 	error = register_node(node_devices[nid], nid);
71576b67ed9SKAMEZAWA Hiroyuki 
71676b67ed9SKAMEZAWA Hiroyuki 	/* link cpu under this node */
71776b67ed9SKAMEZAWA Hiroyuki 	for_each_present_cpu(cpu) {
71876b67ed9SKAMEZAWA Hiroyuki 		if (cpu_to_node(cpu) == nid)
71976b67ed9SKAMEZAWA Hiroyuki 			register_cpu_under_node(cpu, nid);
72076b67ed9SKAMEZAWA Hiroyuki 	}
721c04fc586SGary Hade 
72208d9dbe7SKeith Busch 	INIT_LIST_HEAD(&node_devices[nid]->access_list);
72339da08cbSLee Schermerhorn 	/* initialize work queue for memory hot plug */
72439da08cbSLee Schermerhorn 	init_node_hugetlb_work(nid);
7250fc44159SYasunori Goto 
7260fc44159SYasunori Goto 	return error;
7270fc44159SYasunori Goto }
7280fc44159SYasunori Goto 
7290fc44159SYasunori Goto void unregister_one_node(int nid)
7300fc44159SYasunori Goto {
73192d585efSXishi Qiu 	if (!node_devices[nid])
73292d585efSXishi Qiu 		return;
73392d585efSXishi Qiu 
7348732794bSWen Congyang 	unregister_node(node_devices[nid]);
7358732794bSWen Congyang 	node_devices[nid] = NULL;
7360fc44159SYasunori Goto }
7370fc44159SYasunori Goto 
738bde631a5SLee Schermerhorn /*
739bde631a5SLee Schermerhorn  * node states attributes
740bde631a5SLee Schermerhorn  */
741bde631a5SLee Schermerhorn 
742bde631a5SLee Schermerhorn static ssize_t print_nodes_state(enum node_states state, char *buf)
743bde631a5SLee Schermerhorn {
744bde631a5SLee Schermerhorn 	int n;
745bde631a5SLee Schermerhorn 
746f799b1a7STejun Heo 	n = scnprintf(buf, PAGE_SIZE - 1, "%*pbl",
747f799b1a7STejun Heo 		      nodemask_pr_args(&node_states[state]));
748f6238818SRyota Ozaki 	buf[n++] = '\n';
749f6238818SRyota Ozaki 	buf[n] = '\0';
750bde631a5SLee Schermerhorn 	return n;
751bde631a5SLee Schermerhorn }
752bde631a5SLee Schermerhorn 
753b15f562fSAndi Kleen struct node_attr {
75410fbcf4cSKay Sievers 	struct device_attribute attr;
755b15f562fSAndi Kleen 	enum node_states state;
756b15f562fSAndi Kleen };
757b15f562fSAndi Kleen 
75810fbcf4cSKay Sievers static ssize_t show_node_state(struct device *dev,
75910fbcf4cSKay Sievers 			       struct device_attribute *attr, char *buf)
760bde631a5SLee Schermerhorn {
761b15f562fSAndi Kleen 	struct node_attr *na = container_of(attr, struct node_attr, attr);
762b15f562fSAndi Kleen 	return print_nodes_state(na->state, buf);
763bde631a5SLee Schermerhorn }
764bde631a5SLee Schermerhorn 
765b15f562fSAndi Kleen #define _NODE_ATTR(name, state) \
76610fbcf4cSKay Sievers 	{ __ATTR(name, 0444, show_node_state, NULL), state }
767bde631a5SLee Schermerhorn 
768b15f562fSAndi Kleen static struct node_attr node_state_attr[] = {
769fcf07d22SLai Jiangshan 	[N_POSSIBLE] = _NODE_ATTR(possible, N_POSSIBLE),
770fcf07d22SLai Jiangshan 	[N_ONLINE] = _NODE_ATTR(online, N_ONLINE),
771fcf07d22SLai Jiangshan 	[N_NORMAL_MEMORY] = _NODE_ATTR(has_normal_memory, N_NORMAL_MEMORY),
772bde631a5SLee Schermerhorn #ifdef CONFIG_HIGHMEM
773fcf07d22SLai Jiangshan 	[N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY),
774bde631a5SLee Schermerhorn #endif
77520b2f52bSLai Jiangshan 	[N_MEMORY] = _NODE_ATTR(has_memory, N_MEMORY),
776fcf07d22SLai Jiangshan 	[N_CPU] = _NODE_ATTR(has_cpu, N_CPU),
777bde631a5SLee Schermerhorn };
778bde631a5SLee Schermerhorn 
77910fbcf4cSKay Sievers static struct attribute *node_state_attrs[] = {
780fcf07d22SLai Jiangshan 	&node_state_attr[N_POSSIBLE].attr.attr,
781fcf07d22SLai Jiangshan 	&node_state_attr[N_ONLINE].attr.attr,
782fcf07d22SLai Jiangshan 	&node_state_attr[N_NORMAL_MEMORY].attr.attr,
7833701cde6SAndi Kleen #ifdef CONFIG_HIGHMEM
784fcf07d22SLai Jiangshan 	&node_state_attr[N_HIGH_MEMORY].attr.attr,
7853701cde6SAndi Kleen #endif
78620b2f52bSLai Jiangshan 	&node_state_attr[N_MEMORY].attr.attr,
787fcf07d22SLai Jiangshan 	&node_state_attr[N_CPU].attr.attr,
7883701cde6SAndi Kleen 	NULL
7893701cde6SAndi Kleen };
790bde631a5SLee Schermerhorn 
79110fbcf4cSKay Sievers static struct attribute_group memory_root_attr_group = {
79210fbcf4cSKay Sievers 	.attrs = node_state_attrs,
79310fbcf4cSKay Sievers };
79410fbcf4cSKay Sievers 
79510fbcf4cSKay Sievers static const struct attribute_group *cpu_root_attr_groups[] = {
79610fbcf4cSKay Sievers 	&memory_root_attr_group,
79710fbcf4cSKay Sievers 	NULL,
79810fbcf4cSKay Sievers };
79910fbcf4cSKay Sievers 
8004faf8d95SLee Schermerhorn #define NODE_CALLBACK_PRI	2	/* lower than SLAB */
8014b45099bSKeiichiro Tokunaga static int __init register_node_type(void)
8021da177e4SLinus Torvalds {
803bde631a5SLee Schermerhorn 	int ret;
804bde631a5SLee Schermerhorn 
8053701cde6SAndi Kleen  	BUILD_BUG_ON(ARRAY_SIZE(node_state_attr) != NR_NODE_STATES);
8063701cde6SAndi Kleen  	BUILD_BUG_ON(ARRAY_SIZE(node_state_attrs)-1 != NR_NODE_STATES);
8073701cde6SAndi Kleen 
80810fbcf4cSKay Sievers 	ret = subsys_system_register(&node_subsys, cpu_root_attr_groups);
8094faf8d95SLee Schermerhorn 	if (!ret) {
8106e259e7dSAndrew Morton 		static struct notifier_block node_memory_callback_nb = {
8116e259e7dSAndrew Morton 			.notifier_call = node_memory_callback,
8126e259e7dSAndrew Morton 			.priority = NODE_CALLBACK_PRI,
8136e259e7dSAndrew Morton 		};
8146e259e7dSAndrew Morton 		register_hotmemory_notifier(&node_memory_callback_nb);
8154faf8d95SLee Schermerhorn 	}
816bde631a5SLee Schermerhorn 
817bde631a5SLee Schermerhorn 	/*
818bde631a5SLee Schermerhorn 	 * Note:  we're not going to unregister the node class if we fail
819bde631a5SLee Schermerhorn 	 * to register the node state class attribute files.
820bde631a5SLee Schermerhorn 	 */
821bde631a5SLee Schermerhorn 	return ret;
8221da177e4SLinus Torvalds }
8231da177e4SLinus Torvalds postcore_initcall(register_node_type);
824