diff options
-rw-r--r-- | arch/powerpc/mm/numa.c | 209 |
1 files changed, 170 insertions, 39 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index dc704da363eb..39328da32edf 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c | |||
@@ -268,6 +268,144 @@ static unsigned long __devinit read_n_cells(int n, const unsigned int **buf) | |||
268 | return result; | 268 | return result; |
269 | } | 269 | } |
270 | 270 | ||
271 | struct of_drconf_cell { | ||
272 | u64 base_addr; | ||
273 | u32 drc_index; | ||
274 | u32 reserved; | ||
275 | u32 aa_index; | ||
276 | u32 flags; | ||
277 | }; | ||
278 | |||
279 | #define DRCONF_MEM_ASSIGNED 0x00000008 | ||
280 | #define DRCONF_MEM_AI_INVALID 0x00000040 | ||
281 | #define DRCONF_MEM_RESERVED 0x00000080 | ||
282 | |||
283 | /* | ||
284 | * Read the next lmb list entry from the ibm,dynamic-memory property | ||
285 | * and return the information in the provided of_drconf_cell structure. | ||
286 | */ | ||
287 | static void read_drconf_cell(struct of_drconf_cell *drmem, const u32 **cellp) | ||
288 | { | ||
289 | const u32 *cp; | ||
290 | |||
291 | drmem->base_addr = read_n_cells(n_mem_addr_cells, cellp); | ||
292 | |||
293 | cp = *cellp; | ||
294 | drmem->drc_index = cp[0]; | ||
295 | drmem->reserved = cp[1]; | ||
296 | drmem->aa_index = cp[2]; | ||
297 | drmem->flags = cp[3]; | ||
298 | |||
299 | *cellp = cp + 4; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Retreive and validate the ibm,dynamic-memory property of the device tree. | ||
304 | * | ||
305 | * The layout of the ibm,dynamic-memory property is a number N of lmb | ||
306 | * list entries followed by N lmb list entries. Each lmb list entry | ||
307 | * contains information as layed out in the of_drconf_cell struct above. | ||
308 | */ | ||
309 | static int of_get_drconf_memory(struct device_node *memory, const u32 **dm) | ||
310 | { | ||
311 | const u32 *prop; | ||
312 | u32 len, entries; | ||
313 | |||
314 | prop = of_get_property(memory, "ibm,dynamic-memory", &len); | ||
315 | if (!prop || len < sizeof(unsigned int)) | ||
316 | return 0; | ||
317 | |||
318 | entries = *prop++; | ||
319 | |||
320 | /* Now that we know the number of entries, revalidate the size | ||
321 | * of the property read in to ensure we have everything | ||
322 | */ | ||
323 | if (len < (entries * (n_mem_addr_cells + 4) + 1) * sizeof(unsigned int)) | ||
324 | return 0; | ||
325 | |||
326 | *dm = prop; | ||
327 | return entries; | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * Retreive and validate the ibm,lmb-size property for drconf memory | ||
332 | * from the device tree. | ||
333 | */ | ||
334 | static u64 of_get_lmb_size(struct device_node *memory) | ||
335 | { | ||
336 | const u32 *prop; | ||
337 | u32 len; | ||
338 | |||
339 | prop = of_get_property(memory, "ibm,lmb-size", &len); | ||
340 | if (!prop || len < sizeof(unsigned int)) | ||
341 | return 0; | ||
342 | |||
343 | return read_n_cells(n_mem_size_cells, &prop); | ||
344 | } | ||
345 | |||
346 | struct assoc_arrays { | ||
347 | u32 n_arrays; | ||
348 | u32 array_sz; | ||
349 | const u32 *arrays; | ||
350 | }; | ||
351 | |||
352 | /* | ||
353 | * Retreive and validate the list of associativity arrays for drconf | ||
354 | * memory from the ibm,associativity-lookup-arrays property of the | ||
355 | * device tree.. | ||
356 | * | ||
357 | * The layout of the ibm,associativity-lookup-arrays property is a number N | ||
358 | * indicating the number of associativity arrays, followed by a number M | ||
359 | * indicating the size of each associativity array, followed by a list | ||
360 | * of N associativity arrays. | ||
361 | */ | ||
362 | static int of_get_assoc_arrays(struct device_node *memory, | ||
363 | struct assoc_arrays *aa) | ||
364 | { | ||
365 | const u32 *prop; | ||
366 | u32 len; | ||
367 | |||
368 | prop = of_get_property(memory, "ibm,associativity-lookup-arrays", &len); | ||
369 | if (!prop || len < 2 * sizeof(unsigned int)) | ||
370 | return -1; | ||
371 | |||
372 | aa->n_arrays = *prop++; | ||
373 | aa->array_sz = *prop++; | ||
374 | |||
375 | /* Now that we know the number of arrrays and size of each array, | ||
376 | * revalidate the size of the property read in. | ||
377 | */ | ||
378 | if (len < (aa->n_arrays * aa->array_sz + 2) * sizeof(unsigned int)) | ||
379 | return -1; | ||
380 | |||
381 | aa->arrays = prop; | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * This is like of_node_to_nid_single() for memory represented in the | ||
387 | * ibm,dynamic-reconfiguration-memory node. | ||
388 | */ | ||
389 | static int of_drconf_to_nid_single(struct of_drconf_cell *drmem, | ||
390 | struct assoc_arrays *aa) | ||
391 | { | ||
392 | int default_nid = 0; | ||
393 | int nid = default_nid; | ||
394 | int index; | ||
395 | |||
396 | if (min_common_depth > 0 && min_common_depth <= aa->array_sz && | ||
397 | !(drmem->flags & DRCONF_MEM_AI_INVALID) && | ||
398 | drmem->aa_index < aa->n_arrays) { | ||
399 | index = drmem->aa_index * aa->array_sz + min_common_depth - 1; | ||
400 | nid = aa->arrays[index]; | ||
401 | |||
402 | if (nid == 0xffff || nid >= MAX_NUMNODES) | ||
403 | nid = default_nid; | ||
404 | } | ||
405 | |||
406 | return nid; | ||
407 | } | ||
408 | |||
271 | /* | 409 | /* |
272 | * Figure out to which domain a cpu belongs and stick it there. | 410 | * Figure out to which domain a cpu belongs and stick it there. |
273 | * Return the id of the domain used. | 411 | * Return the id of the domain used. |
@@ -355,57 +493,50 @@ static unsigned long __init numa_enforce_memory_limit(unsigned long start, | |||
355 | */ | 493 | */ |
356 | static void __init parse_drconf_memory(struct device_node *memory) | 494 | static void __init parse_drconf_memory(struct device_node *memory) |
357 | { | 495 | { |
358 | const unsigned int *lm, *dm, *aa; | 496 | const u32 *dm; |
359 | unsigned int ls, ld, la; | 497 | unsigned int n, rc; |
360 | unsigned int n, aam, aalen; | 498 | unsigned long lmb_size, size; |
361 | unsigned long lmb_size, size, start; | 499 | int nid; |
362 | int nid, default_nid = 0; | 500 | struct assoc_arrays aa; |
363 | unsigned int ai, flags; | 501 | |
364 | 502 | n = of_get_drconf_memory(memory, &dm); | |
365 | lm = of_get_property(memory, "ibm,lmb-size", &ls); | 503 | if (!n) |
366 | dm = of_get_property(memory, "ibm,dynamic-memory", &ld); | ||
367 | aa = of_get_property(memory, "ibm,associativity-lookup-arrays", &la); | ||
368 | if (!lm || !dm || !aa || | ||
369 | ls < sizeof(unsigned int) || ld < sizeof(unsigned int) || | ||
370 | la < 2 * sizeof(unsigned int)) | ||
371 | return; | 504 | return; |
372 | 505 | ||
373 | lmb_size = read_n_cells(n_mem_size_cells, &lm); | 506 | lmb_size = of_get_lmb_size(memory); |
374 | n = *dm++; /* number of LMBs */ | 507 | if (!lmb_size) |
375 | aam = *aa++; /* number of associativity lists */ | 508 | return; |
376 | aalen = *aa++; /* length of each associativity list */ | 509 | |
377 | if (ld < (n * (n_mem_addr_cells + 4) + 1) * sizeof(unsigned int) || | 510 | rc = of_get_assoc_arrays(memory, &aa); |
378 | la < (aam * aalen + 2) * sizeof(unsigned int)) | 511 | if (rc) |
379 | return; | 512 | return; |
380 | 513 | ||
381 | for (; n != 0; --n) { | 514 | for (; n != 0; --n) { |
382 | start = read_n_cells(n_mem_addr_cells, &dm); | 515 | struct of_drconf_cell drmem; |
383 | ai = dm[2]; | 516 | |
384 | flags = dm[3]; | 517 | read_drconf_cell(&drmem, &dm); |
385 | dm += 4; | 518 | |
386 | /* 0x80 == reserved, 0x8 = assigned to us */ | 519 | /* skip this block if the reserved bit is set in flags (0x80) |
387 | if ((flags & 0x80) || !(flags & 0x8)) | 520 | or if the block is not assigned to this partition (0x8) */ |
521 | if ((drmem.flags & DRCONF_MEM_RESERVED) | ||
522 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) | ||
388 | continue; | 523 | continue; |
389 | nid = default_nid; | ||
390 | /* flags & 0x40 means associativity index is invalid */ | ||
391 | if (min_common_depth > 0 && min_common_depth <= aalen && | ||
392 | (flags & 0x40) == 0 && ai < aam) { | ||
393 | /* this is like of_node_to_nid_single */ | ||
394 | nid = aa[ai * aalen + min_common_depth - 1]; | ||
395 | if (nid == 0xffff || nid >= MAX_NUMNODES) | ||
396 | nid = default_nid; | ||
397 | } | ||
398 | 524 | ||
399 | fake_numa_create_new_node(((start + lmb_size) >> PAGE_SHIFT), | 525 | nid = of_drconf_to_nid_single(&drmem, &aa); |
400 | &nid); | 526 | |
527 | fake_numa_create_new_node( | ||
528 | ((drmem.base_addr + lmb_size) >> PAGE_SHIFT), | ||
529 | &nid); | ||
530 | |||
401 | node_set_online(nid); | 531 | node_set_online(nid); |
402 | 532 | ||
403 | size = numa_enforce_memory_limit(start, lmb_size); | 533 | size = numa_enforce_memory_limit(drmem.base_addr, lmb_size); |
404 | if (!size) | 534 | if (!size) |
405 | continue; | 535 | continue; |
406 | 536 | ||
407 | add_active_range(nid, start >> PAGE_SHIFT, | 537 | add_active_range(nid, drmem.base_addr >> PAGE_SHIFT, |
408 | (start >> PAGE_SHIFT) + (size >> PAGE_SHIFT)); | 538 | (drmem.base_addr >> PAGE_SHIFT) |
539 | + (size >> PAGE_SHIFT)); | ||
409 | } | 540 | } |
410 | } | 541 | } |
411 | 542 | ||