diff options
Diffstat (limited to 'arch/powerpc/mm/numa.c')
-rw-r--r-- | arch/powerpc/mm/numa.c | 310 |
1 files changed, 253 insertions, 57 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index dc704da363eb..cf4bffba6f7c 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); | 504 | return; |
367 | aa = of_get_property(memory, "ibm,associativity-lookup-arrays", &la); | 505 | |
368 | if (!lm || !dm || !aa || | 506 | lmb_size = of_get_lmb_size(memory); |
369 | ls < sizeof(unsigned int) || ld < sizeof(unsigned int) || | 507 | if (!lmb_size) |
370 | la < 2 * sizeof(unsigned int)) | ||
371 | return; | 508 | return; |
372 | 509 | ||
373 | lmb_size = read_n_cells(n_mem_size_cells, &lm); | 510 | rc = of_get_assoc_arrays(memory, &aa); |
374 | n = *dm++; /* number of LMBs */ | 511 | if (rc) |
375 | aam = *aa++; /* number of associativity lists */ | ||
376 | aalen = *aa++; /* length of each associativity list */ | ||
377 | if (ld < (n * (n_mem_addr_cells + 4) + 1) * sizeof(unsigned int) || | ||
378 | la < (aam * aalen + 2) * sizeof(unsigned int)) | ||
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 | ||
@@ -770,6 +901,79 @@ early_param("numa", early_numa); | |||
770 | 901 | ||
771 | #ifdef CONFIG_MEMORY_HOTPLUG | 902 | #ifdef CONFIG_MEMORY_HOTPLUG |
772 | /* | 903 | /* |
904 | * Validate the node associated with the memory section we are | ||
905 | * trying to add. | ||
906 | */ | ||
907 | int valid_hot_add_scn(int *nid, unsigned long start, u32 lmb_size, | ||
908 | unsigned long scn_addr) | ||
909 | { | ||
910 | nodemask_t nodes; | ||
911 | |||
912 | if (*nid < 0 || !node_online(*nid)) | ||
913 | *nid = any_online_node(NODE_MASK_ALL); | ||
914 | |||
915 | if ((scn_addr >= start) && (scn_addr < (start + lmb_size))) { | ||
916 | nodes_setall(nodes); | ||
917 | while (NODE_DATA(*nid)->node_spanned_pages == 0) { | ||
918 | node_clear(*nid, nodes); | ||
919 | *nid = any_online_node(nodes); | ||
920 | } | ||
921 | |||
922 | return 1; | ||
923 | } | ||
924 | |||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * Find the node associated with a hot added memory section represented | ||
930 | * by the ibm,dynamic-reconfiguration-memory node. | ||
931 | */ | ||
932 | static int hot_add_drconf_scn_to_nid(struct device_node *memory, | ||
933 | unsigned long scn_addr) | ||
934 | { | ||
935 | const u32 *dm; | ||
936 | unsigned int n, rc; | ||
937 | unsigned long lmb_size; | ||
938 | int default_nid = any_online_node(NODE_MASK_ALL); | ||
939 | int nid; | ||
940 | struct assoc_arrays aa; | ||
941 | |||
942 | n = of_get_drconf_memory(memory, &dm); | ||
943 | if (!n) | ||
944 | return default_nid;; | ||
945 | |||
946 | lmb_size = of_get_lmb_size(memory); | ||
947 | if (!lmb_size) | ||
948 | return default_nid; | ||
949 | |||
950 | rc = of_get_assoc_arrays(memory, &aa); | ||
951 | if (rc) | ||
952 | return default_nid; | ||
953 | |||
954 | for (; n != 0; --n) { | ||
955 | struct of_drconf_cell drmem; | ||
956 | |||
957 | read_drconf_cell(&drmem, &dm); | ||
958 | |||
959 | /* skip this block if it is reserved or not assigned to | ||
960 | * this partition */ | ||
961 | if ((drmem.flags & DRCONF_MEM_RESERVED) | ||
962 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) | ||
963 | continue; | ||
964 | |||
965 | nid = of_drconf_to_nid_single(&drmem, &aa); | ||
966 | |||
967 | if (valid_hot_add_scn(&nid, drmem.base_addr, lmb_size, | ||
968 | scn_addr)) | ||
969 | return nid; | ||
970 | } | ||
971 | |||
972 | BUG(); /* section address should be found above */ | ||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | /* | ||
773 | * Find the node associated with a hot added memory section. Section | 977 | * Find the node associated with a hot added memory section. Section |
774 | * corresponds to a SPARSEMEM section, not an LMB. It is assumed that | 978 | * corresponds to a SPARSEMEM section, not an LMB. It is assumed that |
775 | * sections are fully contained within a single LMB. | 979 | * sections are fully contained within a single LMB. |
@@ -777,12 +981,17 @@ early_param("numa", early_numa); | |||
777 | int hot_add_scn_to_nid(unsigned long scn_addr) | 981 | int hot_add_scn_to_nid(unsigned long scn_addr) |
778 | { | 982 | { |
779 | struct device_node *memory = NULL; | 983 | struct device_node *memory = NULL; |
780 | nodemask_t nodes; | ||
781 | int default_nid = any_online_node(NODE_MASK_ALL); | ||
782 | int nid; | 984 | int nid; |
783 | 985 | ||
784 | if (!numa_enabled || (min_common_depth < 0)) | 986 | if (!numa_enabled || (min_common_depth < 0)) |
785 | return default_nid; | 987 | return any_online_node(NODE_MASK_ALL); |
988 | |||
989 | memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | ||
990 | if (memory) { | ||
991 | nid = hot_add_drconf_scn_to_nid(memory, scn_addr); | ||
992 | of_node_put(memory); | ||
993 | return nid; | ||
994 | } | ||
786 | 995 | ||
787 | while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { | 996 | while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { |
788 | unsigned long start, size; | 997 | unsigned long start, size; |
@@ -801,13 +1010,9 @@ ha_new_range: | |||
801 | size = read_n_cells(n_mem_size_cells, &memcell_buf); | 1010 | size = read_n_cells(n_mem_size_cells, &memcell_buf); |
802 | nid = of_node_to_nid_single(memory); | 1011 | nid = of_node_to_nid_single(memory); |
803 | 1012 | ||
804 | /* Domains not present at boot default to 0 */ | 1013 | if (valid_hot_add_scn(&nid, start, size, scn_addr)) { |
805 | if (nid < 0 || !node_online(nid)) | ||
806 | nid = default_nid; | ||
807 | |||
808 | if ((scn_addr >= start) && (scn_addr < (start + size))) { | ||
809 | of_node_put(memory); | 1014 | of_node_put(memory); |
810 | goto got_nid; | 1015 | return nid; |
811 | } | 1016 | } |
812 | 1017 | ||
813 | if (--ranges) /* process all ranges in cell */ | 1018 | if (--ranges) /* process all ranges in cell */ |
@@ -815,14 +1020,5 @@ ha_new_range: | |||
815 | } | 1020 | } |
816 | BUG(); /* section address should be found above */ | 1021 | BUG(); /* section address should be found above */ |
817 | return 0; | 1022 | return 0; |
818 | |||
819 | /* Temporary code to ensure that returned node is not empty */ | ||
820 | got_nid: | ||
821 | nodes_setall(nodes); | ||
822 | while (NODE_DATA(nid)->node_spanned_pages == 0) { | ||
823 | node_clear(nid, nodes); | ||
824 | nid = any_online_node(nodes); | ||
825 | } | ||
826 | return nid; | ||
827 | } | 1023 | } |
828 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 1024 | #endif /* CONFIG_MEMORY_HOTPLUG */ |