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