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; 727b2600f8STejun Heo node = setup_node(pxm); 737b2600f8STejun Heo if (node < 0) { 747b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 757b2600f8STejun Heo bad_srat(); 767b2600f8STejun Heo return; 777b2600f8STejun Heo } 787b2600f8STejun Heo 797b2600f8STejun Heo apic_id = pa->apic_id; 807b2600f8STejun Heo if (apic_id >= MAX_LOCAL_APIC) { 817b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 827b2600f8STejun Heo return; 837b2600f8STejun Heo } 847b2600f8STejun Heo set_apicid_to_node(apic_id, node); 857b2600f8STejun Heo node_set(node, numa_nodes_parsed); 867b2600f8STejun Heo acpi_numa = 1; 877b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n", 887b2600f8STejun Heo pxm, apic_id, node); 897b2600f8STejun Heo } 907b2600f8STejun Heo 917b2600f8STejun Heo /* Callback for Proximity Domain -> LAPIC mapping */ 927b2600f8STejun Heo void __init 937b2600f8STejun Heo acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) 947b2600f8STejun Heo { 957b2600f8STejun Heo int pxm, node; 967b2600f8STejun Heo int apic_id; 977b2600f8STejun Heo 987b2600f8STejun Heo if (srat_disabled()) 997b2600f8STejun Heo return; 1007b2600f8STejun Heo if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) { 1017b2600f8STejun Heo bad_srat(); 1027b2600f8STejun Heo return; 1037b2600f8STejun Heo } 1047b2600f8STejun Heo if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0) 1057b2600f8STejun Heo return; 1067b2600f8STejun Heo pxm = pa->proximity_domain_lo; 107cd298f60SKurt Garloff if (acpi_srat_revision >= 2) 108cd298f60SKurt Garloff pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; 1097b2600f8STejun Heo node = setup_node(pxm); 1107b2600f8STejun Heo if (node < 0) { 1117b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); 1127b2600f8STejun Heo bad_srat(); 1137b2600f8STejun Heo return; 1147b2600f8STejun Heo } 1157b2600f8STejun Heo 1167b2600f8STejun Heo if (get_uv_system_type() >= UV_X2APIC) 1177b2600f8STejun Heo apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; 1187b2600f8STejun Heo else 1197b2600f8STejun Heo apic_id = pa->apic_id; 1207b2600f8STejun Heo 1217b2600f8STejun Heo if (apic_id >= MAX_LOCAL_APIC) { 1227b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u skipped apicid that is too big\n", pxm, apic_id, node); 1237b2600f8STejun Heo return; 1247b2600f8STejun Heo } 1257b2600f8STejun Heo 1267b2600f8STejun Heo set_apicid_to_node(apic_id, node); 1277b2600f8STejun Heo node_set(node, numa_nodes_parsed); 1287b2600f8STejun Heo acpi_numa = 1; 1297b2600f8STejun Heo printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n", 1307b2600f8STejun Heo pxm, apic_id, node); 1317b2600f8STejun Heo } 1327b2600f8STejun Heo 1337b2600f8STejun Heo #ifdef CONFIG_MEMORY_HOTPLUG 1347b2600f8STejun Heo static inline int save_add_info(void) {return 1;} 1357b2600f8STejun Heo #else 1367b2600f8STejun Heo static inline int save_add_info(void) {return 0;} 1377b2600f8STejun Heo #endif 1387b2600f8STejun Heo 1397b2600f8STejun Heo /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ 1407b2600f8STejun Heo void __init 1417b2600f8STejun Heo acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) 1427b2600f8STejun Heo { 143eca9ad31STejun Heo u64 start, end; 1447b2600f8STejun Heo int node, pxm; 1457b2600f8STejun Heo 1467b2600f8STejun Heo if (srat_disabled()) 1477b2600f8STejun Heo return; 1487b2600f8STejun Heo if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { 1497b2600f8STejun Heo bad_srat(); 1507b2600f8STejun Heo return; 1517b2600f8STejun Heo } 1527b2600f8STejun Heo if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) 1537b2600f8STejun Heo return; 1547b2600f8STejun Heo 1557b2600f8STejun Heo if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) 1567b2600f8STejun Heo return; 1577b2600f8STejun Heo start = ma->base_address; 1587b2600f8STejun Heo end = start + ma->length; 1597b2600f8STejun Heo pxm = ma->proximity_domain; 160cd298f60SKurt Garloff if (acpi_srat_revision <= 1) 161cd298f60SKurt Garloff pxm &= 0xff; 1627b2600f8STejun Heo node = setup_node(pxm); 1637b2600f8STejun Heo if (node < 0) { 1647b2600f8STejun Heo printk(KERN_ERR "SRAT: Too many proximity domains.\n"); 1657b2600f8STejun Heo bad_srat(); 1667b2600f8STejun Heo return; 1677b2600f8STejun Heo } 1687b2600f8STejun Heo 1697b2600f8STejun Heo if (numa_add_memblk(node, start, end) < 0) { 1707b2600f8STejun Heo bad_srat(); 1717b2600f8STejun Heo return; 1727b2600f8STejun Heo } 1737b2600f8STejun Heo 174eca9ad31STejun Heo printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm, 1757b2600f8STejun Heo start, end); 1767b2600f8STejun Heo } 1777b2600f8STejun Heo 1787b2600f8STejun Heo void __init acpi_numa_arch_fixup(void) {} 1797b2600f8STejun Heo 1807b2600f8STejun Heo int __init x86_acpi_numa_init(void) 1817b2600f8STejun Heo { 1827b2600f8STejun Heo int ret; 1837b2600f8STejun Heo 1847b2600f8STejun Heo ret = acpi_numa_init(); 1857b2600f8STejun Heo if (ret < 0) 1867b2600f8STejun Heo return ret; 1877b2600f8STejun Heo return srat_disabled() ? -EINVAL : 0; 1887b2600f8STejun Heo } 189