aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm/numa.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/mm/numa.c')
-rw-r--r--arch/powerpc/mm/numa.c122
1 files changed, 89 insertions, 33 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 80d110635d24..f78f19e0a2a4 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -42,6 +42,12 @@ EXPORT_SYMBOL(node_data);
42 42
43static int min_common_depth; 43static int min_common_depth;
44static int n_mem_addr_cells, n_mem_size_cells; 44static int n_mem_addr_cells, n_mem_size_cells;
45static int form1_affinity;
46
47#define MAX_DISTANCE_REF_POINTS 4
48static int distance_ref_points_depth;
49static const unsigned int *distance_ref_points;
50static 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
213int __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
232static 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
228out: 271out:
229 return nid; 272 return nid;
230} 273}
@@ -251,26 +294,10 @@ int of_node_to_nid(struct device_node *device)
251} 294}
252EXPORT_SYMBOL_GPL(of_node_to_nid); 295EXPORT_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 */
268static int __init find_min_common_depth(void) 297static 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
368err:
369 of_node_put(rtas_root);
370 return -1;
315} 371}
316 372
317static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells) 373static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells)