diff options
Diffstat (limited to 'arch/powerpc/mm/numa.c')
| -rw-r--r-- | arch/powerpc/mm/numa.c | 122 |
1 files changed, 89 insertions, 33 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index aa731af720c0..002878ccf90b 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c | |||
| @@ -42,6 +42,12 @@ EXPORT_SYMBOL(node_data); | |||
| 42 | 42 | ||
| 43 | static int min_common_depth; | 43 | static int min_common_depth; |
| 44 | static int n_mem_addr_cells, n_mem_size_cells; | 44 | static int n_mem_addr_cells, n_mem_size_cells; |
| 45 | static int form1_affinity; | ||
| 46 | |||
| 47 | #define MAX_DISTANCE_REF_POINTS 4 | ||
| 48 | static int distance_ref_points_depth; | ||
| 49 | static const unsigned int *distance_ref_points; | ||
| 50 | static int distance_lookup_table[MAX_NUMNODES][MAX_DISTANCE_REF_POINTS]; | ||
| 45 | 51 | ||
| 46 | /* | 52 | /* |
| 47 | * Allocate node_to_cpumask_map based on number of available nodes | 53 | * Allocate node_to_cpumask_map based on number of available nodes |
| @@ -204,6 +210,39 @@ static const u32 *of_get_usable_memory(struct device_node *memory) | |||
| 204 | return prop; | 210 | return prop; |
| 205 | } | 211 | } |
| 206 | 212 | ||
| 213 | int __node_distance(int a, int b) | ||
| 214 | { | ||
| 215 | int i; | ||
| 216 | int distance = LOCAL_DISTANCE; | ||
| 217 | |||
| 218 | if (!form1_affinity) | ||
| 219 | return distance; | ||
| 220 | |||
| 221 | for (i = 0; i < distance_ref_points_depth; i++) { | ||
| 222 | if (distance_lookup_table[a][i] == distance_lookup_table[b][i]) | ||
| 223 | break; | ||
| 224 | |||
| 225 | /* Double the distance for each NUMA level */ | ||
| 226 | distance *= 2; | ||
| 227 | } | ||
| 228 | |||
| 229 | return distance; | ||
| 230 | } | ||
| 231 | |||
| 232 | static void initialize_distance_lookup_table(int nid, | ||
| 233 | const unsigned int *associativity) | ||
| 234 | { | ||
| 235 | int i; | ||
| 236 | |||
| 237 | if (!form1_affinity) | ||
| 238 | return; | ||
| 239 | |||
| 240 | for (i = 0; i < distance_ref_points_depth; i++) { | ||
| 241 | distance_lookup_table[nid][i] = | ||
| 242 | associativity[distance_ref_points[i]]; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 207 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa | 246 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa |
| 208 | * info is found. | 247 | * info is found. |
| 209 | */ | 248 | */ |
| @@ -225,6 +264,10 @@ static int of_node_to_nid_single(struct device_node *device) | |||
| 225 | /* POWER4 LPAR uses 0xffff as invalid node */ | 264 | /* POWER4 LPAR uses 0xffff as invalid node */ |
| 226 | if (nid == 0xffff || nid >= MAX_NUMNODES) | 265 | if (nid == 0xffff || nid >= MAX_NUMNODES) |
| 227 | nid = -1; | 266 | nid = -1; |
| 267 | |||
| 268 | if (nid > 0 && tmp[0] >= distance_ref_points_depth) | ||
| 269 | initialize_distance_lookup_table(nid, tmp); | ||
| 270 | |||
| 228 | out: | 271 | out: |
| 229 | return nid; | 272 | return nid; |
| 230 | } | 273 | } |
| @@ -251,26 +294,10 @@ int of_node_to_nid(struct device_node *device) | |||
| 251 | } | 294 | } |
| 252 | EXPORT_SYMBOL_GPL(of_node_to_nid); | 295 | EXPORT_SYMBOL_GPL(of_node_to_nid); |
| 253 | 296 | ||
| 254 | /* | ||
| 255 | * In theory, the "ibm,associativity" property may contain multiple | ||
| 256 | * associativity lists because a resource may be multiply connected | ||
| 257 | * into the machine. This resource then has different associativity | ||
| 258 | * characteristics relative to its multiple connections. We ignore | ||
| 259 | * this for now. We also assume that all cpu and memory sets have | ||
| 260 | * their distances represented at a common level. This won't be | ||
| 261 | * true for hierarchical NUMA. | ||
| 262 | * | ||
| 263 | * In any case the ibm,associativity-reference-points should give | ||
| 264 | * the correct depth for a normal NUMA system. | ||
| 265 | * | ||
| 266 | * - Dave Hansen <haveblue@us.ibm.com> | ||
| 267 | */ | ||
| 268 | static int __init find_min_common_depth(void) | 297 | static int __init find_min_common_depth(void) |
| 269 | { | 298 | { |
| 270 | int depth, index; | 299 | int depth; |
| 271 | const unsigned int *ref_points; | ||
| 272 | struct device_node *rtas_root; | 300 | struct device_node *rtas_root; |
| 273 | unsigned int len; | ||
| 274 | struct device_node *chosen; | 301 | struct device_node *chosen; |
| 275 | const char *vec5; | 302 | const char *vec5; |
| 276 | 303 | ||
| @@ -280,18 +307,28 @@ static int __init find_min_common_depth(void) | |||
| 280 | return -1; | 307 | return -1; |
| 281 | 308 | ||
| 282 | /* | 309 | /* |
| 283 | * this property is 2 32-bit integers, each representing a level of | 310 | * This property is a set of 32-bit integers, each representing |
| 284 | * depth in the associativity nodes. The first is for an SMP | 311 | * an index into the ibm,associativity nodes. |
| 285 | * configuration (should be all 0's) and the second is for a normal | 312 | * |
| 286 | * NUMA configuration. | 313 | * With form 0 affinity the first integer is for an SMP configuration |
| 314 | * (should be all 0's) and the second is for a normal NUMA | ||
| 315 | * configuration. We have only one level of NUMA. | ||
| 316 | * | ||
| 317 | * With form 1 affinity the first integer is the most significant | ||
| 318 | * NUMA boundary and the following are progressively less significant | ||
| 319 | * boundaries. There can be more than one level of NUMA. | ||
| 287 | */ | 320 | */ |
| 288 | index = 1; | 321 | distance_ref_points = of_get_property(rtas_root, |
| 289 | ref_points = of_get_property(rtas_root, | 322 | "ibm,associativity-reference-points", |
| 290 | "ibm,associativity-reference-points", &len); | 323 | &distance_ref_points_depth); |
| 324 | |||
| 325 | if (!distance_ref_points) { | ||
| 326 | dbg("NUMA: ibm,associativity-reference-points not found.\n"); | ||
| 327 | goto err; | ||
| 328 | } | ||
| 329 | |||
| 330 | distance_ref_points_depth /= sizeof(int); | ||
| 291 | 331 | ||
| 292 | /* | ||
| 293 | * For form 1 affinity information we want the first field | ||
| 294 | */ | ||
| 295 | #define VEC5_AFFINITY_BYTE 5 | 332 | #define VEC5_AFFINITY_BYTE 5 |
| 296 | #define VEC5_AFFINITY 0x80 | 333 | #define VEC5_AFFINITY 0x80 |
| 297 | chosen = of_find_node_by_path("/chosen"); | 334 | chosen = of_find_node_by_path("/chosen"); |
| @@ -299,19 +336,38 @@ static int __init find_min_common_depth(void) | |||
| 299 | vec5 = of_get_property(chosen, "ibm,architecture-vec-5", NULL); | 336 | vec5 = of_get_property(chosen, "ibm,architecture-vec-5", NULL); |
| 300 | if (vec5 && (vec5[VEC5_AFFINITY_BYTE] & VEC5_AFFINITY)) { | 337 | if (vec5 && (vec5[VEC5_AFFINITY_BYTE] & VEC5_AFFINITY)) { |
| 301 | dbg("Using form 1 affinity\n"); | 338 | dbg("Using form 1 affinity\n"); |
| 302 | index = 0; | 339 | form1_affinity = 1; |
| 303 | } | 340 | } |
| 304 | } | 341 | } |
| 305 | 342 | ||
| 306 | if ((len >= 2 * sizeof(unsigned int)) && ref_points) { | 343 | if (form1_affinity) { |
| 307 | depth = ref_points[index]; | 344 | depth = distance_ref_points[0]; |
| 308 | } else { | 345 | } else { |
| 309 | dbg("NUMA: ibm,associativity-reference-points not found.\n"); | 346 | if (distance_ref_points_depth < 2) { |
| 310 | depth = -1; | 347 | printk(KERN_WARNING "NUMA: " |
| 348 | "short ibm,associativity-reference-points\n"); | ||
| 349 | goto err; | ||
| 350 | } | ||
| 351 | |||
| 352 | depth = distance_ref_points[1]; | ||
| 311 | } | 353 | } |
| 312 | of_node_put(rtas_root); | ||
| 313 | 354 | ||
| 355 | /* | ||
| 356 | * Warn and cap if the hardware supports more than | ||
| 357 | * MAX_DISTANCE_REF_POINTS domains. | ||
| 358 | */ | ||
| 359 | if (distance_ref_points_depth > MAX_DISTANCE_REF_POINTS) { | ||
| 360 | printk(KERN_WARNING "NUMA: distance array capped at " | ||
| 361 | "%d entries\n", MAX_DISTANCE_REF_POINTS); | ||
| 362 | distance_ref_points_depth = MAX_DISTANCE_REF_POINTS; | ||
| 363 | } | ||
| 364 | |||
| 365 | of_node_put(rtas_root); | ||
| 314 | return depth; | 366 | return depth; |
| 367 | |||
| 368 | err: | ||
| 369 | of_node_put(rtas_root); | ||
| 370 | return -1; | ||
| 315 | } | 371 | } |
| 316 | 372 | ||
| 317 | static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells) | 373 | static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells) |
