17b2600f8STejun Heo /* 27b2600f8STejun Heo * ACPI 3.0 based NUMA setup 37b2600f8STejun Heo * Copyright 2004 Andi Kleen, SuSE Labs. 47b2600f8STejun Heo * 57b2600f8STejun Heo * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs. 67b2600f8STejun Heo * 77b2600f8STejun Heo * Called from acpi_numa_init while reading the SRAT and SLIT tables. 87b2600f8STejun Heo * Assumes all memory regions belonging to a single proximity domain 97b2600f8STejun Heo * are in one chunk. Holes between them will be included in the node. 107b2600f8STejun Heo */ 117b2600f8STejun Heo 127b2600f8STejun Heo #include <linux/kernel.h> 137b2600f8STejun Heo #include <linux/acpi.h> 147b2600f8STejun Heo #include <linux/mmzone.h> 157b2600f8STejun Heo #include <linux/bitmap.h> 167b2600f8STejun Heo #include <linux/module.h> 177b2600f8STejun Heo #include <linux/topology.h> 187b2600f8STejun Heo #include <linux/bootmem.h> 197b2600f8STejun Heo #include <linux/memblock.h> 207b2600f8STejun Heo #include <linux/mm.h> 217b2600f8STejun Heo #include <asm/proto.h> 227b2600f8STejun Heo #include <asm/numa.h> 237b2600f8STejun Heo #include <asm/e820.h> 247b2600f8STejun Heo #include <asm/apic.h> 257b2600f8STejun Heo #include <asm/uv/uv.h> 267b2600f8STejun Heo 277b2600f8STejun Heo int acpi_numa __initdata; 287b2600f8STejun Heo 297b2600f8STejun Heo static __init int setup_node(int pxm) 307b2600f8STejun Heo { 317b2600f8STejun Heo return acpi_map_pxm_to_node(pxm); 327b2600f8STejun Heo } 337b2600f8STejun Heo 347b2600f8STejun Heo static __init void bad_srat(void) 357b2600f8STejun Heo { 367b2600f8STejun Heo printk(KERN_ERR "SRAT: SRAT not used.\n"); 377b2600f8STejun Heo acpi_numa = -1; 387b2600f8STejun Heo } 397b2600f8STejun Heo 407b2600f8STejun Heo static __init inline int srat_disabled(void) 417b2600f8STejun Heo { 427b2600f8STejun Heo return acpi_numa < 0; 437b2600f8STejun Heo } 447b2600f8STejun Heo 457b2600f8STejun Heo /* Callback for SLIT parsing */ 467b2600f8STejun Heo void __init acpi_numa_slit_init(struct acpi_table_slit *slit) 477b2600f8STejun Heo { 487b2600f8STejun Heo int i, j; 497b2600f8STejun Heo 507b2600f8STejun Heo for (i = 0; i < slit->locality_count; i++) 517b2600f8STejun Heo for (j = 0; j < slit->locality_count; j++) 527b2600f8STejun Heo numa_set_distance(pxm_to_node(i), pxm_to_node(j), 537b2600f8STejun Heo slit->entry[slit->locality_count * i + j]); 547b2600f8STejun Heo } 557b2600f8STejun Heo 567b2600f8STejun Heo /* Callback for Proximity Domain -> x2APIC mapping */ 577b2600f8STejun Heo void __init 587b2600f8STejun Heo acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) 597b2600f8STejun Heo { 607b2600f8STejun Heo int pxm, node; 617b2600f8STejun Heo int apic_id; 627b2600f8STejun Heo 637b2600f8STejun Heo if (srat_disabled()) 647b2600f8STejun Heo return; 657b2600f8STejun Heo if (pa->header.length < sizeof(struct acpi_srat_x2apic_cpu_affinity)) { 667b2600f8STejun Heo bad_srat(); 677b2600f8STejun Heo return; 687b2600f8STejun Heo } 697b2600f8STejun Heo if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 707b2600f8STejun Heo return; 717b2600f8STejun Heo pxm = pa->proximity_domain; 72a35fd282SYinghai Lu apic_id = pa->apic_id; 73a35fd282SYinghai Lu if (!cpu_has_x2apic && (apic_id >= 0xff)) { 74a35fd282SYinghai Lu printk(KERN_INFO "SRAT: PXM %u -> X2APIC 0x%04x ignored\n", 75a35fd282SYinghai Lu pxm, apic_id); 76a35fd282SYinghai Lu return; 77a35fd282SYinghai Lu } 787b2600f8STejun Heo node = setup_node(pxm); 797b2600f8STejun Heo if (node < 0) { 807b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 817b2600f8STejun Heo bad_srat(); 827b2600f8STejun Heo return; 837b2600f8STejun Heo } 847b2600f8STejun Heo 857b2600f8STejun Heo if (apic_id >= MAX_LOCAL_APIC) { 867b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 877b2600f8STejun Heo return; 887b2600f8STejun Heo } 897b2600f8STejun Heo set_apicid_to_node(apic_id, node); 907b2600f8STejun Heo node_set(node, numa_nodes_parsed); 917b2600f8STejun Heo acpi_numa = 1; 927b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n", 937b2600f8STejun Heo pxm, apic_id, node); 947b2600f8STejun Heo } 957b2600f8STejun Heo 967b2600f8STejun Heo /* Callback for Proximity Domain -> LAPIC mapping */ 977b2600f8STejun Heo void __init 987b2600f8STejun Heo acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) 997b2600f8STejun Heo { 1007b2600f8STejun Heo int pxm, node; 1017b2600f8STejun Heo int apic_id; 1027b2600f8STejun Heo 1037b2600f8STejun Heo if (srat_disabled()) 1047b2600f8STejun Heo return; 1057b2600f8STejun Heo if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { 1067b2600f8STejun Heo bad_srat(); 1077b2600f8STejun Heo return; 1087b2600f8STejun Heo } 1097b2600f8STejun Heo if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 1107b2600f8STejun Heo return; 1117b2600f8STejun Heo pxm = pa->proximity_domain_lo; 1127b2600f8STejun Heo node = setup_node(pxm); 1137b2600f8STejun Heo if (node < 0) { 1147b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 1157b2600f8STejun Heo bad_srat(); 1167b2600f8STejun Heo return; 1177b2600f8STejun Heo } 1187b2600f8STejun Heo 1197b2600f8STejun Heo if (get_uv_system_type() >= UV_X2APIC) 1207b2600f8STejun Heo apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; 1217b2600f8STejun Heo else 1227b2600f8STejun Heo apic_id = pa->apic_id; 1237b2600f8STejun Heo 1247b2600f8STejun Heo if (apic_id >= MAX_LOCAL_APIC) { 1257b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 1267b2600f8STejun Heo return; 1277b2600f8STejun Heo } 1287b2600f8STejun Heo 1297b2600f8STejun Heo set_apicid_to_node(apic_id, node); 1307b2600f8STejun Heo node_set(node, numa_nodes_parsed); 1317b2600f8STejun Heo acpi_numa = 1; 1327b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n", 1337b2600f8STejun Heo pxm, apic_id, node); 1347b2600f8STejun Heo } 1357b2600f8STejun Heo 1367b2600f8STejun Heo #ifdef CONFIG_MEMORY_HOTPLUG 1377b2600f8STejun Heo static inline int save_add_info(void) {return 1;} 1387b2600f8STejun Heo #else 1397b2600f8STejun Heo static inline int save_add_info(void) {return 0;} 1407b2600f8STejun Heo #endif 1417b2600f8STejun Heo 1427b2600f8STejun Heo /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ 1437b2600f8STejun Heo void __init 1447b2600f8STejun Heo acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) 1457b2600f8STejun Heo { 146eca9ad31STejun Heo u64 start, end; 1477b2600f8STejun Heo int node, pxm; 1487b2600f8STejun Heo 1497b2600f8STejun Heo if (srat_disabled()) 1507b2600f8STejun Heo return; 1517b2600f8STejun Heo if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { 1527b2600f8STejun Heo bad_srat(); 1537b2600f8STejun Heo return; 1547b2600f8STejun Heo } 1557b2600f8STejun Heo if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) 1567b2600f8STejun Heo return; 1577b2600f8STejun Heo 1587b2600f8STejun Heo if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) 1597b2600f8STejun Heo return; 1607b2600f8STejun Heo start = ma->base_address; 1617b2600f8STejun Heo end = start + ma->length; 1627b2600f8STejun Heo pxm = ma->proximity_domain; 1637b2600f8STejun Heo node = setup_node(pxm); 1647b2600f8STejun Heo if (node < 0) { 1657b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains.\n"); 1667b2600f8STejun Heo bad_srat(); 1677b2600f8STejun Heo return; 1687b2600f8STejun Heo } 1697b2600f8STejun Heo 1707b2600f8STejun Heo if (numa_add_memblk(node, start, end) < 0) { 1717b2600f8STejun Heo bad_srat(); 1727b2600f8STejun Heo return; 1737b2600f8STejun Heo } 1747b2600f8STejun Heo 175eca9ad31STejun Heo printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm, 1767b2600f8STejun Heo start, end); 1777b2600f8STejun Heo } 1787b2600f8STejun Heo 1797b2600f8STejun Heo void __init acpi_numa_arch_fixup(void) {} 1807b2600f8STejun Heo 1817b2600f8STejun Heo int __init x86_acpi_numa_init(void) 1827b2600f8STejun Heo { 1837b2600f8STejun Heo int ret; 1847b2600f8STejun Heo 1857b2600f8STejun Heo ret = acpi_numa_init(); 1867b2600f8STejun Heo if (ret < 0) 1877b2600f8STejun Heo return ret; 1887b2600f8STejun Heo return srat_disabled() ? -EINVAL : 0; 1897b2600f8STejun Heo } 190