2005-04-16 15:20:36 -07:00
/*
* ACPI 3.0 based NUMA setup
* Copyright 2004 Andi Kleen , SuSE Labs .
*
* Reads the ACPI SRAT table to figure out what memory belongs to which CPUs .
*
* Called from acpi_numa_init while reading the SRAT and SLIT tables .
* Assumes all memory regions belonging to a single proximity domain
* are in one chunk . Holes between them will be included in the node .
*/
# include <linux/kernel.h>
# include <linux/acpi.h>
# include <linux/mmzone.h>
# include <linux/bitmap.h>
# include <linux/module.h>
# include <linux/topology.h>
2006-04-07 19:49:18 +02:00
# include <linux/bootmem.h>
# include <linux/mm.h>
2005-04-16 15:20:36 -07:00
# include <asm/proto.h>
# include <asm/numa.h>
2006-01-11 22:44:39 +01:00
# include <asm/e820.h>
2008-03-28 14:12:08 -05:00
# include <asm/genapic.h>
2005-04-16 15:20:36 -07:00
2006-09-26 10:52:33 +02:00
int acpi_numa __initdata ;
2005-04-16 15:20:36 -07:00
static struct acpi_table_slit * acpi_slit ;
static nodemask_t nodes_parsed __initdata ;
2006-03-25 16:29:12 +01:00
static struct bootnode nodes [ MAX_NUMNODES ] __initdata ;
2006-09-30 23:27:06 -07:00
static struct bootnode nodes_add [ MAX_NUMNODES ] ;
2006-04-07 19:49:18 +02:00
static int found_add_area __initdata ;
2006-05-15 18:19:44 +02:00
int hotadd_percent __initdata = 0 ;
2005-04-16 15:20:36 -07:00
2008-03-25 10:14:35 -07:00
static int num_node_memblks __initdata ;
static struct bootnode node_memblk_range [ NR_NODE_MEMBLKS ] __initdata ;
static int memblk_nodeid [ NR_NODE_MEMBLKS ] __initdata ;
2006-02-03 21:51:17 +01:00
/* Too small nodes confuse the VM badly. Usually they result
from BIOS bugs . */
# define NODE_MIN_SIZE (4*1024*1024)
2005-04-16 15:20:36 -07:00
static __init int setup_node ( int pxm )
{
2006-06-23 02:03:19 -07:00
return acpi_map_pxm_to_node ( pxm ) ;
2005-04-16 15:20:36 -07:00
}
2008-03-25 10:14:35 -07:00
static __init int conflicting_memblks ( unsigned long start , unsigned long end )
2005-04-16 15:20:36 -07:00
{
int i ;
2008-03-25 10:14:35 -07:00
for ( i = 0 ; i < num_node_memblks ; i + + ) {
struct bootnode * nd = & node_memblk_range [ i ] ;
2005-04-16 15:20:36 -07:00
if ( nd - > start = = nd - > end )
continue ;
if ( nd - > end > start & & nd - > start < end )
2008-03-25 10:14:35 -07:00
return memblk_nodeid [ i ] ;
2005-04-16 15:20:36 -07:00
if ( nd - > end = = end & & nd - > start = = start )
2008-03-25 10:14:35 -07:00
return memblk_nodeid [ i ] ;
2005-04-16 15:20:36 -07:00
}
return - 1 ;
}
static __init void cutoff_node ( int i , unsigned long start , unsigned long end )
{
2006-03-25 16:29:12 +01:00
struct bootnode * nd = & nodes [ i ] ;
2006-04-07 19:49:18 +02:00
if ( found_add_area )
return ;
2005-04-16 15:20:36 -07:00
if ( nd - > start < start ) {
nd - > start = start ;
if ( nd - > end < nd - > start )
nd - > start = nd - > end ;
}
if ( nd - > end > end ) {
nd - > end = end ;
if ( nd - > start > nd - > end )
nd - > start = nd - > end ;
}
}
static __init void bad_srat ( void )
{
2005-09-12 18:49:25 +02:00
int i ;
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " SRAT: SRAT not used. \n " ) ;
acpi_numa = - 1 ;
2006-05-15 18:19:44 +02:00
found_add_area = 0 ;
2005-09-12 18:49:25 +02:00
for ( i = 0 ; i < MAX_LOCAL_APIC ; i + + )
apicid_to_node [ i ] = NUMA_NO_NODE ;
2006-04-07 19:49:18 +02:00
for ( i = 0 ; i < MAX_NUMNODES ; i + + )
nodes_add [ i ] . start = nodes [ i ] . end = 0 ;
2006-09-27 01:49:52 -07:00
remove_all_active_ranges ( ) ;
2005-04-16 15:20:36 -07:00
}
static __init inline int srat_disabled ( void )
{
return numa_off | | acpi_numa < 0 ;
}
/* Callback for SLIT parsing */
void __init acpi_numa_slit_init ( struct acpi_table_slit * slit )
{
acpi_slit = slit ;
}
/* Callback for Proximity Domain -> LAPIC mapping */
void __init
2007-02-02 19:48:22 +03:00
acpi_numa_processor_affinity_init ( struct acpi_srat_cpu_affinity * pa )
2005-04-16 15:20:36 -07:00
{
int pxm , node ;
2008-01-30 13:33:10 +01:00
int apic_id ;
2006-02-03 21:51:26 +01:00
if ( srat_disabled ( ) )
return ;
2007-02-02 19:48:22 +03:00
if ( pa - > header . length ! = sizeof ( struct acpi_srat_cpu_affinity ) ) {
2006-05-15 18:19:44 +02:00
bad_srat ( ) ;
2006-02-03 21:51:26 +01:00
return ;
}
2007-02-02 19:48:22 +03:00
if ( ( pa - > flags & ACPI_SRAT_CPU_ENABLED ) = = 0 )
2005-04-16 15:20:36 -07:00
return ;
2007-02-02 19:48:22 +03:00
pxm = pa - > proximity_domain_lo ;
2005-04-16 15:20:36 -07:00
node = setup_node ( pxm ) ;
if ( node < 0 ) {
printk ( KERN_ERR " SRAT: Too many proximity domains %x \n " , pxm ) ;
bad_srat ( ) ;
return ;
}
2008-02-16 23:00:22 -08:00
2008-03-28 14:12:08 -05:00
if ( is_uv_system ( ) )
apic_id = ( pa - > apic_id < < 8 ) | pa - > local_sapic_eid ;
else
apic_id = pa - > apic_id ;
2008-01-30 13:33:10 +01:00
apicid_to_node [ apic_id ] = node ;
2005-04-16 15:20:36 -07:00
acpi_numa = 1 ;
2005-09-12 18:49:24 +02:00
printk ( KERN_INFO " SRAT: PXM %u -> APIC %u -> Node %u \n " ,
2008-01-30 13:33:10 +01:00
pxm , apic_id , node ) ;
2005-04-16 15:20:36 -07:00
}
2008-04-23 13:20:56 +02:00
static int update_end_of_memory ( unsigned long end ) { return - 1 ; }
2006-09-30 23:27:05 -07:00
static int hotadd_enough_memory ( struct bootnode * nd ) { return 1 ; }
# ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
static inline int save_add_info ( void ) { return 1 ; }
# else
static inline int save_add_info ( void ) { return 0 ; }
# endif
2006-04-07 19:49:18 +02:00
/*
2006-09-30 23:27:05 -07:00
* Update nodes_add and decide if to include add are in the zone .
2008-01-30 13:31:42 +01:00
* Both SPARSE and RESERVE need nodes_add information .
2007-10-20 01:25:36 +02:00
* This code supports one contiguous hot add area per node .
2006-04-07 19:49:18 +02:00
*/
2008-02-17 13:22:58 +01:00
static int __init
reserve_hotadd ( int node , unsigned long start , unsigned long end )
2006-04-07 19:49:18 +02:00
{
unsigned long s_pfn = start > > PAGE_SHIFT ;
unsigned long e_pfn = end > > PAGE_SHIFT ;
2006-09-30 23:27:05 -07:00
int ret = 0 , changed = 0 ;
2006-04-07 19:49:18 +02:00
struct bootnode * nd = & nodes_add [ node ] ;
/* I had some trouble with strange memory hotadd regions breaking
the boot . Be very strict here and reject anything unexpected .
If you want working memory hotadd write correct SRATs .
The node size check is a basic sanity check to guard against
mistakes */
if ( ( signed long ) ( end - start ) < NODE_MIN_SIZE ) {
printk ( KERN_ERR " SRAT: Hotplug area too small \n " ) ;
return - 1 ;
}
/* This check might be a bit too strict, but I'm keeping it for now. */
2006-09-27 01:49:52 -07:00
if ( absent_pages_in_range ( s_pfn , e_pfn ) ! = e_pfn - s_pfn ) {
2006-09-27 01:49:58 -07:00
printk ( KERN_ERR
" SRAT: Hotplug area %lu -> %lu has existing memory \n " ,
s_pfn , e_pfn ) ;
2006-04-07 19:49:18 +02:00
return - 1 ;
}
if ( ! hotadd_enough_memory ( & nodes_add [ node ] ) ) {
printk ( KERN_ERR " SRAT: Hotplug area too large \n " ) ;
return - 1 ;
}
/* Looks good */
if ( nd - > start = = nd - > end ) {
2007-02-02 19:48:22 +03:00
nd - > start = start ;
nd - > end = end ;
2006-04-07 19:49:18 +02:00
changed = 1 ;
2007-02-02 19:48:22 +03:00
} else {
if ( nd - > start = = end ) {
nd - > start = start ;
2006-04-07 19:49:18 +02:00
changed = 1 ;
}
2007-02-02 19:48:22 +03:00
if ( nd - > end = = start ) {
nd - > end = end ;
2006-04-07 19:49:18 +02:00
changed = 1 ;
}
if ( ! changed )
printk ( KERN_ERR " SRAT: Hotplug zone not continuous. Partly ignored \n " ) ;
2007-02-02 19:48:22 +03:00
}
2006-04-07 19:49:18 +02:00
2006-09-30 23:27:05 -07:00
ret = update_end_of_memory ( nd - > end ) ;
2006-04-07 19:49:18 +02:00
if ( changed )
printk ( KERN_INFO " SRAT: hot plug zone found %Lx - %Lx \n " , nd - > start , nd - > end ) ;
2006-09-30 23:27:05 -07:00
return ret ;
2006-04-07 19:49:18 +02:00
}
2005-04-16 15:20:36 -07:00
/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
void __init
2007-02-02 19:48:22 +03:00
acpi_numa_memory_affinity_init ( struct acpi_srat_mem_affinity * ma )
2005-04-16 15:20:36 -07:00
{
2006-04-07 19:49:18 +02:00
struct bootnode * nd , oldnode ;
2005-04-16 15:20:36 -07:00
unsigned long start , end ;
int node , pxm ;
int i ;
2006-02-03 21:51:26 +01:00
if ( srat_disabled ( ) )
2005-04-16 15:20:36 -07:00
return ;
2007-02-02 19:48:22 +03:00
if ( ma - > header . length ! = sizeof ( struct acpi_srat_mem_affinity ) ) {
2006-02-03 21:51:26 +01:00
bad_srat ( ) ;
return ;
}
2007-02-02 19:48:22 +03:00
if ( ( ma - > flags & ACPI_SRAT_MEM_ENABLED ) = = 0 )
2006-02-03 21:51:26 +01:00
return ;
2007-02-02 19:48:22 +03:00
if ( ( ma - > flags & ACPI_SRAT_MEM_HOT_PLUGGABLE ) & & ! save_add_info ( ) )
2006-04-07 19:49:18 +02:00
return ;
2007-02-02 19:48:22 +03:00
start = ma - > base_address ;
end = start + ma - > length ;
2005-04-16 15:20:36 -07:00
pxm = ma - > proximity_domain ;
node = setup_node ( pxm ) ;
if ( node < 0 ) {
printk ( KERN_ERR " SRAT: Too many proximity domains. \n " ) ;
bad_srat ( ) ;
return ;
}
2008-03-25 10:14:35 -07:00
i = conflicting_memblks ( start , end ) ;
2005-09-12 18:49:24 +02:00
if ( i = = node ) {
printk ( KERN_WARNING
" SRAT: Warning: PXM %d (%lx-%lx) overlaps with itself (%Lx-%Lx) \n " ,
pxm , start , end , nodes [ i ] . start , nodes [ i ] . end ) ;
} else if ( i > = 0 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR
2005-09-12 18:49:24 +02:00
" SRAT: PXM %d (%lx-%lx) overlaps with PXM %d (%Lx-%Lx) \n " ,
pxm , start , end , node_to_pxm ( i ) ,
nodes [ i ] . start , nodes [ i ] . end ) ;
2005-04-16 15:20:36 -07:00
bad_srat ( ) ;
return ;
}
nd = & nodes [ node ] ;
2006-04-07 19:49:18 +02:00
oldnode = * nd ;
2005-04-16 15:20:36 -07:00
if ( ! node_test_and_set ( node , nodes_parsed ) ) {
nd - > start = start ;
nd - > end = end ;
} else {
if ( start < nd - > start )
nd - > start = start ;
if ( nd - > end < end )
nd - > end = end ;
}
2006-04-07 19:49:18 +02:00
2008-03-25 10:14:35 -07:00
printk ( KERN_INFO " SRAT: Node %u PXM %u %lx-%lx \n " , node , pxm ,
start , end ) ;
e820_register_active_regions ( node , start > > PAGE_SHIFT ,
end > > PAGE_SHIFT ) ;
2006-09-27 01:49:59 -07:00
push_node_boundaries ( node , nd - > start > > PAGE_SHIFT ,
nd - > end > > PAGE_SHIFT ) ;
2006-04-07 19:49:18 +02:00
2007-02-02 19:48:22 +03:00
if ( ( ma - > flags & ACPI_SRAT_MEM_HOT_PLUGGABLE ) & &
( reserve_hotadd ( node , start , end ) < 0 ) ) {
2006-04-07 19:49:18 +02:00
/* Ignore hotadd region. Undo damage */
printk ( KERN_NOTICE " SRAT: Hotplug region ignored \n " ) ;
* nd = oldnode ;
if ( ( nd - > start | nd - > end ) = = 0 )
node_clear ( node , nodes_parsed ) ;
}
2008-03-25 10:14:35 -07:00
node_memblk_range [ num_node_memblks ] . start = start ;
node_memblk_range [ num_node_memblks ] . end = end ;
memblk_nodeid [ num_node_memblks ] = node ;
num_node_memblks + + ;
2005-04-16 15:20:36 -07:00
}
2006-01-11 22:44:39 +01:00
/* Sanity check to catch more bad SRATs (they are amazingly common).
Make sure the PXMs cover all memory . */
2007-07-21 17:10:32 +02:00
static int __init nodes_cover_memory ( const struct bootnode * nodes )
2006-01-11 22:44:39 +01:00
{
int i ;
unsigned long pxmram , e820ram ;
pxmram = 0 ;
for_each_node_mask ( i , nodes_parsed ) {
unsigned long s = nodes [ i ] . start > > PAGE_SHIFT ;
unsigned long e = nodes [ i ] . end > > PAGE_SHIFT ;
pxmram + = e - s ;
2006-09-27 01:49:52 -07:00
pxmram - = absent_pages_in_range ( s , e ) ;
2006-04-07 19:49:18 +02:00
if ( ( long ) pxmram < 0 )
pxmram = 0 ;
2006-01-11 22:44:39 +01:00
}
2006-09-27 01:49:52 -07:00
e820ram = end_pfn - absent_pages_in_range ( 0 , end_pfn ) ;
2006-02-16 23:42:13 +01:00
/* We seem to lose 3 pages somewhere. Allow a bit of slack. */
if ( ( long ) ( e820ram - pxmram ) > = 1 * 1024 * 1024 ) {
2006-01-11 22:44:39 +01:00
printk ( KERN_ERR
" SRAT: PXMs only cover %luMB of your %luMB e820 RAM. Not used. \n " ,
( pxmram < < PAGE_SHIFT ) > > 20 ,
( e820ram < < PAGE_SHIFT ) > > 20 ) ;
return 0 ;
}
return 1 ;
}
2008-01-30 13:33:37 +01:00
static void __init unparse_node ( int node )
2006-02-03 21:51:17 +01:00
{
int i ;
node_clear ( node , nodes_parsed ) ;
for ( i = 0 ; i < MAX_LOCAL_APIC ; i + + ) {
if ( apicid_to_node [ i ] = = node )
apicid_to_node [ i ] = NUMA_NO_NODE ;
}
}
2005-04-16 15:20:36 -07:00
void __init acpi_numa_arch_fixup ( void ) { }
/* Use the information discovered above to actually set up the nodes. */
int __init acpi_scan_nodes ( unsigned long start , unsigned long end )
{
int i ;
2006-01-11 22:44:39 +01:00
2007-07-21 17:09:56 +02:00
if ( acpi_numa < = 0 )
return - 1 ;
2005-09-12 18:49:25 +02:00
/* First clean up the node list */
2006-02-03 21:51:17 +01:00
for ( i = 0 ; i < MAX_NUMNODES ; i + + ) {
2007-02-02 19:48:22 +03:00
cutoff_node ( i , start , end ) ;
2008-01-30 13:33:14 +01:00
/*
* don ' t confuse VM with a node that doesn ' t have the
* minimum memory .
*/
if ( nodes [ i ] . end & &
( nodes [ i ] . end - nodes [ i ] . start ) < NODE_MIN_SIZE ) {
2006-02-03 21:51:17 +01:00
unparse_node ( i ) ;
2006-05-30 22:47:57 +02:00
node_set_offline ( i ) ;
}
2005-09-12 18:49:25 +02:00
}
2007-07-21 17:10:32 +02:00
if ( ! nodes_cover_memory ( nodes ) ) {
2006-01-11 22:44:39 +01:00
bad_srat ( ) ;
return - 1 ;
}
2008-03-25 10:14:35 -07:00
memnode_shift = compute_hash_shift ( node_memblk_range , num_node_memblks ,
memblk_nodeid ) ;
2005-04-16 15:20:36 -07:00
if ( memnode_shift < 0 ) {
printk ( KERN_ERR
" SRAT: No NUMA node hash function found. Contact maintainer \n " ) ;
bad_srat ( ) ;
return - 1 ;
}
2005-09-12 18:49:25 +02:00
2007-05-02 19:27:20 +02:00
node_possible_map = nodes_parsed ;
2005-09-12 18:49:25 +02:00
/* Finally register nodes */
2007-05-02 19:27:20 +02:00
for_each_node_mask ( i , node_possible_map )
2005-04-16 15:20:36 -07:00
setup_node_bootmem ( i , nodes [ i ] . start , nodes [ i ] . end ) ;
2006-04-07 19:49:21 +02:00
/* Try again in case setup_node_bootmem missed one due
to missing bootmem */
2007-05-02 19:27:20 +02:00
for_each_node_mask ( i , node_possible_map )
2006-04-07 19:49:21 +02:00
if ( ! node_online ( i ) )
setup_node_bootmem ( i , nodes [ i ] . start , nodes [ i ] . end ) ;
2007-02-02 19:48:22 +03:00
for ( i = 0 ; i < NR_CPUS ; i + + ) {
2008-01-30 13:33:21 +01:00
int node = early_cpu_to_node ( i ) ;
2008-01-30 13:33:21 +01:00
if ( node = = NUMA_NO_NODE )
2005-04-16 15:20:36 -07:00
continue ;
2008-01-30 13:33:21 +01:00
if ( ! node_isset ( node , node_possible_map ) )
2005-11-05 17:25:53 +01:00
numa_set_node ( i , NUMA_NO_NODE ) ;
2005-04-16 15:20:36 -07:00
}
numa_init_array ( ) ;
return 0 ;
}
2007-07-21 17:10:32 +02:00
# ifdef CONFIG_NUMA_EMU
2008-01-30 13:33:10 +01:00
static int fake_node_to_pxm_map [ MAX_NUMNODES ] __initdata = {
[ 0 . . . MAX_NUMNODES - 1 ] = PXM_INVAL
} ;
2008-01-30 13:33:21 +01:00
static s16 fake_apicid_to_node [ MAX_LOCAL_APIC ] __initdata = {
2008-01-30 13:33:10 +01:00
[ 0 . . . MAX_LOCAL_APIC - 1 ] = NUMA_NO_NODE
} ;
2007-07-21 17:10:32 +02:00
static int __init find_node_by_addr ( unsigned long addr )
{
int ret = NUMA_NO_NODE ;
int i ;
for_each_node_mask ( i , nodes_parsed ) {
/*
* Find the real node that this emulated node appears on . For
* the sake of simplicity , we only use a real node ' s starting
* address to determine which emulated node it appears on .
*/
if ( addr > = nodes [ i ] . start & & addr < nodes [ i ] . end ) {
ret = i ;
break ;
}
}
2008-01-30 13:33:35 +01:00
return ret ;
2007-07-21 17:10:32 +02:00
}
/*
* In NUMA emulation , we need to setup proximity domain ( _PXM ) to node ID
* mappings that respect the real ACPI topology but reflect our emulated
* environment . For each emulated node , we find which real node it appears on
* and create PXM to NID mappings for those fake nodes which mirror that
* locality . SLIT will now represent the correct distances between emulated
* nodes as a result of the real topology .
*/
void __init acpi_fake_nodes ( const struct bootnode * fake_nodes , int num_nodes )
{
2007-07-21 17:10:33 +02:00
int i , j ;
2007-07-21 17:10:32 +02:00
printk ( KERN_INFO " Faking PXM affinity for fake nodes on real "
" topology. \n " ) ;
for ( i = 0 ; i < num_nodes ; i + + ) {
int nid , pxm ;
nid = find_node_by_addr ( fake_nodes [ i ] . start ) ;
if ( nid = = NUMA_NO_NODE )
continue ;
pxm = node_to_pxm ( nid ) ;
if ( pxm = = PXM_INVAL )
continue ;
fake_node_to_pxm_map [ i ] = pxm ;
2007-07-21 17:10:33 +02:00
/*
* For each apicid_to_node mapping that exists for this real
* node , it must now point to the fake node ID .
*/
for ( j = 0 ; j < MAX_LOCAL_APIC ; j + + )
if ( apicid_to_node [ j ] = = nid )
fake_apicid_to_node [ j ] = i ;
2007-07-21 17:10:32 +02:00
}
for ( i = 0 ; i < num_nodes ; i + + )
__acpi_map_pxm_to_node ( fake_node_to_pxm_map [ i ] , i ) ;
2007-07-21 17:10:33 +02:00
memcpy ( apicid_to_node , fake_apicid_to_node , sizeof ( apicid_to_node ) ) ;
2007-07-21 17:10:32 +02:00
nodes_clear ( nodes_parsed ) ;
for ( i = 0 ; i < num_nodes ; i + + )
if ( fake_nodes [ i ] . start ! = fake_nodes [ i ] . end )
node_set ( i , nodes_parsed ) ;
WARN_ON ( ! nodes_cover_memory ( fake_nodes ) ) ;
}
static int null_slit_node_compare ( int a , int b )
{
return node_to_pxm ( a ) = = node_to_pxm ( b ) ;
}
# else
static int null_slit_node_compare ( int a , int b )
{
return a = = b ;
}
# endif /* CONFIG_NUMA_EMU */
2006-04-07 19:49:18 +02:00
void __init srat_reserve_add_area ( int nodeid )
{
if ( found_add_area & & nodes_add [ nodeid ] . end ) {
u64 total_mb ;
printk ( KERN_INFO " SRAT: Reserving hot-add memory space "
" for node %d at %Lx-%Lx \n " ,
nodeid , nodes_add [ nodeid ] . start , nodes_add [ nodeid ] . end ) ;
total_mb = ( nodes_add [ nodeid ] . end - nodes_add [ nodeid ] . start )
> > PAGE_SHIFT ;
total_mb * = sizeof ( struct page ) ;
total_mb > > = 20 ;
printk ( KERN_INFO " SRAT: This will cost you %Lu MB of "
" pre-allocated memory. \n " , ( unsigned long long ) total_mb ) ;
reserve_bootmem_node ( NODE_DATA ( nodeid ) , nodes_add [ nodeid ] . start ,
2008-02-07 00:15:17 -08:00
nodes_add [ nodeid ] . end - nodes_add [ nodeid ] . start ,
BOOTMEM_DEFAULT ) ;
2006-04-07 19:49:18 +02:00
}
}
2005-04-16 15:20:36 -07:00
int __node_distance ( int a , int b )
{
int index ;
if ( ! acpi_slit )
2007-07-21 17:10:32 +02:00
return null_slit_node_compare ( a , b ) ? LOCAL_DISTANCE :
REMOTE_DISTANCE ;
2007-02-02 19:48:22 +03:00
index = acpi_slit - > locality_count * node_to_pxm ( a ) ;
2005-04-16 15:20:36 -07:00
return acpi_slit - > entry [ index + node_to_pxm ( b ) ] ;
}
EXPORT_SYMBOL ( __node_distance ) ;
2006-09-30 23:27:06 -07:00
int memory_add_physaddr_to_nid ( u64 start )
{
int i , ret = 0 ;
for_each_node ( i )
if ( nodes_add [ i ] . start < = start & & nodes_add [ i ] . end > start )
ret = i ;
return ret ;
}
2006-09-30 23:27:07 -07:00
EXPORT_SYMBOL_GPL ( memory_add_physaddr_to_nid ) ;