/* * Generic VM initialization for x86-64 NUMA setups. * Copyright 2002,2003 Andi Kleen, SuSE Labs. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Dprintk #define Dprintk(x...) #endif struct pglist_data *node_data[MAXNODE]; bootmem_data_t plat_node_bdata[MAX_NUMNODES]; int memnode_shift; u8 memnodemap[NODEMAPSIZE]; unsigned char cpu_to_node[NR_CPUS]; cpumask_t node_to_cpumask[MAXNODE]; static int numa_off __initdata; unsigned long nodes_present; int __init compute_hash_shift(struct node *nodes) { int i; int shift = 24; u64 addr; /* When in doubt use brute force. */ while (shift < 48) { memset(memnodemap,0xff,sizeof(*memnodemap) * NODEMAPSIZE); for (i = 0; i < numnodes; i++) { if (nodes[i].start == nodes[i].end) continue; for (addr = nodes[i].start; addr < nodes[i].end; addr += (1UL << shift)) { if (memnodemap[addr >> shift] != 0xff && memnodemap[addr >> shift] != i) { printk(KERN_INFO "node %d shift %d addr %Lx conflict %d\n", i, shift, addr, memnodemap[addr>>shift]); goto next; } memnodemap[addr >> shift] = i; } } return shift; next: shift++; } memset(memnodemap,0,sizeof(*memnodemap) * NODEMAPSIZE); return -1; } /* Initialize bootmem allocator for a node */ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long end) { unsigned long start_pfn, end_pfn, bootmap_pages, bootmap_size, bootmap_start; unsigned long nodedata_phys; const int pgdat_size = round_up(sizeof(pg_data_t), PAGE_SIZE); start = round_up(start, ZONE_ALIGN); printk("Bootmem setup node %d %016lx-%016lx\n", nodeid, start, end); start_pfn = start >> PAGE_SHIFT; end_pfn = end >> PAGE_SHIFT; nodedata_phys = find_e820_area(start, end, pgdat_size); if (nodedata_phys == -1L) panic("Cannot find memory pgdat in node %d\n", nodeid); Dprintk("nodedata_phys %lx\n", nodedata_phys); node_data[nodeid] = phys_to_virt(nodedata_phys); memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t)); NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid]; NODE_DATA(nodeid)->node_start_pfn = start_pfn; NODE_DATA(nodeid)->node_spanned_pages = end_pfn - start_pfn; /* Find a place for the bootmem map */ bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); bootmap_start = round_up(nodedata_phys + pgdat_size, PAGE_SIZE); bootmap_start = find_e820_area(bootmap_start, end, bootmap_pages<> PAGE_SHIFT, start_pfn, end_pfn); e820_bootmem_free(NODE_DATA(nodeid), start, end); reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, bootmap_pages< numnodes) numnodes = nodeid + 1; node_set_online(nodeid); } /* Initialize final allocator for a zone */ void __init setup_node_zones(int nodeid) { unsigned long start_pfn, end_pfn; unsigned long zones[MAX_NR_ZONES]; unsigned long dma_end_pfn; memset(zones, 0, sizeof(unsigned long) * MAX_NR_ZONES); start_pfn = node_start_pfn(nodeid); end_pfn = node_end_pfn(nodeid); printk(KERN_INFO "setting up node %d %lx-%lx\n", nodeid, start_pfn, end_pfn); /* All nodes > 0 have a zero length zone DMA */ dma_end_pfn = __pa(MAX_DMA_ADDRESS) >> PAGE_SHIFT; if (start_pfn < dma_end_pfn) { zones[ZONE_DMA] = dma_end_pfn - start_pfn; zones[ZONE_NORMAL] = end_pfn - dma_end_pfn; } else { zones[ZONE_NORMAL] = end_pfn - start_pfn; } free_area_init_node(nodeid, NODE_DATA(nodeid), NULL, zones, start_pfn, NULL); } void __init numa_init_array(void) { int rr, i; /* There are unfortunately some poorly designed mainboards around that only connect memory to a single CPU. This breaks the 1:1 cpu->node mapping. To avoid this fill in the mapping for all possible CPUs, as the number of CPUs is not known yet. We round robin the existing nodes. */ rr = 0; for (i = 0; i < MAXNODE; i++) { if (node_online(i)) continue; rr = find_next_bit(node_online_map, MAX_NUMNODES, rr); if (rr == MAX_NUMNODES) rr = find_first_bit(node_online_map, MAX_NUMNODES); node_data[i] = node_data[rr]; cpu_to_node[i] = rr; rr++; } set_bit(0, &node_to_cpumask[cpu_to_node(0)]); } void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) { int i; #ifdef CONFIG_K8_NUMA if (!numa_off && !k8_scan_nodes(start_pfn<