diff --git a/xen/arch/x86/srat.c b/xen/arch/x86/srat.c index 8ffe43bdfe..3d02520a5a 100644 --- a/xen/arch/x86/srat.c +++ b/xen/arch/x86/srat.c @@ -42,6 +42,12 @@ static struct node node_memblk_range[NR_NODE_MEMBLKS]; static nodeid_t memblk_nodeid[NR_NODE_MEMBLKS]; static __initdata DECLARE_BITMAP(memblk_hotplug, NR_NODE_MEMBLKS); +enum conflicts { + NO_CONFLICT, + OVERLAP, + INTERLEAVE, +}; + static inline bool node_found(unsigned idx, unsigned pxm) { return ((pxm2node[idx].pxm == pxm) && @@ -119,20 +125,45 @@ int valid_numa_range(paddr_t start, paddr_t end, nodeid_t node) return 0; } -static __init int conflicting_memblks(paddr_t start, paddr_t end) +static +enum conflicts __init conflicting_memblks(nodeid_t nid, paddr_t start, + paddr_t end, paddr_t nd_start, + paddr_t nd_end, unsigned int *mblkid) { - int i; + unsigned int i; + /* + * Scan all recorded nodes' memory blocks to check conflicts: + * Overlap or interleave. + */ for (i = 0; i < num_node_memblks; i++) { struct node *nd = &node_memblk_range[i]; + + *mblkid = i; + + /* Skip 0 bytes node memory block. */ if (nd->start == nd->end) continue; + /* + * Use memblk range to check memblk overlaps, include the + * self-overlap case. As nd's range is non-empty, the special + * case "nd->end == end && nd->start == start" also can be covered. + */ if (nd->end > start && nd->start < end) - return i; - if (nd->end == end && nd->start == start) - return i; + return OVERLAP; + + /* + * Use node memory range to check whether new range contains + * memory from other nodes - interleave check. We just need + * to check full contains situation. Because overlaps have + * been checked above. + */ + if (nid != memblk_nodeid[i] && + nd->start >= nd_start && nd->end <= nd_end) + return INTERLEAVE; } - return -1; + + return NO_CONFLICT; } static __init void cutoff_node(int i, paddr_t start, paddr_t end) @@ -275,10 +306,12 @@ acpi_numa_processor_affinity_init(const struct acpi_srat_cpu_affinity *pa) void __init acpi_numa_memory_affinity_init(const struct acpi_srat_mem_affinity *ma) { + struct node *nd; + paddr_t nd_start, nd_end; paddr_t start, end; unsigned pxm; nodeid_t node; - int i; + unsigned int i; if (srat_disabled()) return; @@ -310,44 +343,74 @@ acpi_numa_memory_affinity_init(const struct acpi_srat_mem_affinity *ma) bad_srat(); return; } - /* It is fine to add this area to the nodes data it will be used later*/ - i = conflicting_memblks(start, end); - if (i < 0) - /* everything fine */; - else if (memblk_nodeid[i] == node) { - bool mismatch = !(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) != - !test_bit(i, memblk_hotplug); - printk("%sSRAT: PXM %u (%"PRIpaddr"-%"PRIpaddr") overlaps with itself (%"PRIpaddr"-%"PRIpaddr")\n", - mismatch ? KERN_ERR : KERN_WARNING, pxm, start, end, - node_memblk_range[i].start, node_memblk_range[i].end); - if (mismatch) { - bad_srat(); - return; + /* + * For the node that already has some memory blocks, we will + * expand the node memory range temporarily to check memory + * interleaves with other nodes. We will not use this node + * temp memory range to check overlaps, because it will mask + * the overlaps in same node. + * + * Node with 0 bytes memory doesn't need this expandsion. + */ + nd_start = start; + nd_end = end; + nd = &nodes[node]; + if (nd->start != nd->end) { + if (nd_start > nd->start) + nd_start = nd->start; + + if (nd_end < nd->end) + nd_end = nd->end; + } + + /* It is fine to add this area to the nodes data it will be used later*/ + switch (conflicting_memblks(node, start, end, nd_start, nd_end, &i)) { + case OVERLAP: + if (memblk_nodeid[i] == node) { + bool mismatch = !(ma->flags & + ACPI_SRAT_MEM_HOT_PLUGGABLE) != + !test_bit(i, memblk_hotplug); + + printk("%sSRAT: PXM %u [%"PRIpaddr", %"PRIpaddr"] overlaps with itself [%"PRIpaddr", %"PRIpaddr"]\n", + mismatch ? KERN_ERR : KERN_WARNING, pxm, start, + end - 1, node_memblk_range[i].start, + node_memblk_range[i].end - 1); + if (mismatch) { + bad_srat(); + return; + } + break; } - } else { + printk(KERN_ERR - "SRAT: PXM %u (%"PRIpaddr"-%"PRIpaddr") overlaps with PXM %u (%"PRIpaddr"-%"PRIpaddr")\n", - pxm, start, end, node_to_pxm(memblk_nodeid[i]), - node_memblk_range[i].start, node_memblk_range[i].end); + "SRAT: PXM %u [%"PRIpaddr", %"PRIpaddr"] overlaps with PXM %u [%"PRIpaddr", %"PRIpaddr"]\n", + pxm, start, end - 1, node_to_pxm(memblk_nodeid[i]), + node_memblk_range[i].start, + node_memblk_range[i].end - 1); bad_srat(); return; - } - if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)) { - struct node *nd = &nodes[node]; - if (!node_test_and_set(node, memory_nodes_parsed)) { - nd->start = start; - nd->end = end; - } else { - if (start < nd->start) - nd->start = start; - if (nd->end < end) - nd->end = end; - } + case INTERLEAVE: + printk(KERN_ERR + "SRAT: PXM %u: [%"PRIpaddr", %"PRIpaddr"] interleaves with PXM %u memblk [%"PRIpaddr", %"PRIpaddr"]\n", + pxm, nd_start, nd_end - 1, node_to_pxm(memblk_nodeid[i]), + node_memblk_range[i].start, node_memblk_range[i].end - 1); + bad_srat(); + return; + + case NO_CONFLICT: + break; } - printk(KERN_INFO "SRAT: Node %u PXM %u %"PRIpaddr"-%"PRIpaddr"%s\n", - node, pxm, start, end, + + if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)) { + node_set(node, memory_nodes_parsed); + nd->start = nd_start; + nd->end = nd_end; + } + + printk(KERN_INFO "SRAT: Node %u PXM %u [%"PRIpaddr", %"PRIpaddr"]%s\n", + node, pxm, start, end - 1, ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE ? " (hotplug)" : ""); node_memblk_range[num_node_memblks].start = start; @@ -396,7 +459,7 @@ static int __init nodes_cover_memory(void) if (start < end) { printk(KERN_ERR "SRAT: No PXM for e820 range: " - "%"PRIpaddr" - %"PRIpaddr"\n", start, end); + "[%"PRIpaddr", %"PRIpaddr"]\n", start, end - 1); return 0; } }