diff options
author | Sahil Mehta <sahilmehta17@gmail.com> | 2017-02-15 13:45:56 -0500 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2017-02-17 01:57:30 -0500 |
commit | 333f7b76865bec24c66710cf352f892d69e3ba0a (patch) | |
tree | 0fab9fb29b3b62927c1113598e327b5a0069d67a | |
parent | a90e883d8b8fb07ddea4d9618ce58bf72bce7f00 (diff) |
powerpc/pseries: Implement indexed-count hotplug memory add
Indexed-count add for memory hotplug guarantees that a contiguous block
of <count> lmbs beginning at a specified <drc index> will be assigned,
any LMBs in this range that are not already assigned will be DLPAR added.
Because of Qemu's per-DIMM memory management, the addition of a contiguous
block of memory currently requires a series of individual calls to add
each LMB in the block. Indexed-count add reduces this series of calls to
a single call for the entire block.
Signed-off-by: Sahil Mehta <sahilmehta17@gmail.com>
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r-- | arch/powerpc/include/asm/rtas.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/dlpar.c | 38 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 119 |
3 files changed, 147 insertions, 12 deletions
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 076b89247ab5..ec9dd79398ee 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h | |||
@@ -307,6 +307,7 @@ struct pseries_hp_errorlog { | |||
307 | union { | 307 | union { |
308 | __be32 drc_index; | 308 | __be32 drc_index; |
309 | __be32 drc_count; | 309 | __be32 drc_count; |
310 | struct { __be32 count, index; } ic; | ||
310 | char drc_name[1]; | 311 | char drc_name[1]; |
311 | } _drc_u; | 312 | } _drc_u; |
312 | }; | 313 | }; |
@@ -323,6 +324,7 @@ struct pseries_hp_errorlog { | |||
323 | #define PSERIES_HP_ELOG_ID_DRC_NAME 1 | 324 | #define PSERIES_HP_ELOG_ID_DRC_NAME 1 |
324 | #define PSERIES_HP_ELOG_ID_DRC_INDEX 2 | 325 | #define PSERIES_HP_ELOG_ID_DRC_INDEX 2 |
325 | #define PSERIES_HP_ELOG_ID_DRC_COUNT 3 | 326 | #define PSERIES_HP_ELOG_ID_DRC_COUNT 3 |
327 | #define PSERIES_HP_ELOG_ID_DRC_IC 4 | ||
326 | 328 | ||
327 | struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log, | 329 | struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log, |
328 | uint16_t section_id); | 330 | uint16_t section_id); |
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index d3a81e746fc4..193e052fa0dd 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c | |||
@@ -354,11 +354,17 @@ static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) | |||
354 | switch (hp_elog->id_type) { | 354 | switch (hp_elog->id_type) { |
355 | case PSERIES_HP_ELOG_ID_DRC_COUNT: | 355 | case PSERIES_HP_ELOG_ID_DRC_COUNT: |
356 | hp_elog->_drc_u.drc_count = | 356 | hp_elog->_drc_u.drc_count = |
357 | be32_to_cpu(hp_elog->_drc_u.drc_count); | 357 | be32_to_cpu(hp_elog->_drc_u.drc_count); |
358 | break; | 358 | break; |
359 | case PSERIES_HP_ELOG_ID_DRC_INDEX: | 359 | case PSERIES_HP_ELOG_ID_DRC_INDEX: |
360 | hp_elog->_drc_u.drc_index = | 360 | hp_elog->_drc_u.drc_index = |
361 | be32_to_cpu(hp_elog->_drc_u.drc_index); | 361 | be32_to_cpu(hp_elog->_drc_u.drc_index); |
362 | break; | ||
363 | case PSERIES_HP_ELOG_ID_DRC_IC: | ||
364 | hp_elog->_drc_u.ic.count = | ||
365 | be32_to_cpu(hp_elog->_drc_u.ic.count); | ||
366 | hp_elog->_drc_u.ic.index = | ||
367 | be32_to_cpu(hp_elog->_drc_u.ic.index); | ||
362 | } | 368 | } |
363 | 369 | ||
364 | switch (hp_elog->resource) { | 370 | switch (hp_elog->resource) { |
@@ -467,7 +473,33 @@ static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) | |||
467 | if (!arg) | 473 | if (!arg) |
468 | return -EINVAL; | 474 | return -EINVAL; |
469 | 475 | ||
470 | if (sysfs_streq(arg, "index")) { | 476 | if (sysfs_streq(arg, "indexed-count")) { |
477 | hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; | ||
478 | arg = strsep(cmd, " "); | ||
479 | if (!arg) { | ||
480 | pr_err("No DRC count specified.\n"); | ||
481 | return -EINVAL; | ||
482 | } | ||
483 | |||
484 | if (kstrtou32(arg, 0, &count)) { | ||
485 | pr_err("Invalid DRC count specified.\n"); | ||
486 | return -EINVAL; | ||
487 | } | ||
488 | |||
489 | arg = strsep(cmd, " "); | ||
490 | if (!arg) { | ||
491 | pr_err("No DRC Index specified.\n"); | ||
492 | return -EINVAL; | ||
493 | } | ||
494 | |||
495 | if (kstrtou32(arg, 0, &index)) { | ||
496 | pr_err("Invalid DRC Index specified.\n"); | ||
497 | return -EINVAL; | ||
498 | } | ||
499 | |||
500 | hp_elog->_drc_u.ic.count = cpu_to_be32(count); | ||
501 | hp_elog->_drc_u.ic.index = cpu_to_be32(index); | ||
502 | } else if (sysfs_streq(arg, "index")) { | ||
471 | hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; | 503 | hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; |
472 | arg = strsep(cmd, " "); | 504 | arg = strsep(cmd, " "); |
473 | if (!arg) { | 505 | if (!arg) { |
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index b3b92814ce87..ad8b3a606a8e 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c | |||
@@ -779,6 +779,97 @@ static int dlpar_memory_add_by_index(u32 drc_index, struct property *prop) | |||
779 | return rc; | 779 | return rc; |
780 | } | 780 | } |
781 | 781 | ||
782 | static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index, | ||
783 | struct property *prop) | ||
784 | { | ||
785 | struct of_drconf_cell *lmbs; | ||
786 | u32 num_lmbs, *p; | ||
787 | int i, rc, start_lmb_found; | ||
788 | int lmbs_available = 0, start_index = 0, end_index; | ||
789 | |||
790 | pr_info("Attempting to hot-add %u LMB(s) at index %x\n", | ||
791 | lmbs_to_add, drc_index); | ||
792 | |||
793 | if (lmbs_to_add == 0) | ||
794 | return -EINVAL; | ||
795 | |||
796 | p = prop->value; | ||
797 | num_lmbs = *p++; | ||
798 | lmbs = (struct of_drconf_cell *)p; | ||
799 | start_lmb_found = 0; | ||
800 | |||
801 | /* Navigate to drc_index */ | ||
802 | while (start_index < num_lmbs) { | ||
803 | if (lmbs[start_index].drc_index == drc_index) { | ||
804 | start_lmb_found = 1; | ||
805 | break; | ||
806 | } | ||
807 | |||
808 | start_index++; | ||
809 | } | ||
810 | |||
811 | if (!start_lmb_found) | ||
812 | return -EINVAL; | ||
813 | |||
814 | end_index = start_index + lmbs_to_add; | ||
815 | |||
816 | /* Validate that the LMBs in this range are not reserved */ | ||
817 | for (i = start_index; i < end_index; i++) { | ||
818 | if (lmbs[i].flags & DRCONF_MEM_RESERVED) | ||
819 | break; | ||
820 | |||
821 | lmbs_available++; | ||
822 | } | ||
823 | |||
824 | if (lmbs_available < lmbs_to_add) | ||
825 | return -EINVAL; | ||
826 | |||
827 | for (i = start_index; i < end_index; i++) { | ||
828 | if (lmbs[i].flags & DRCONF_MEM_ASSIGNED) | ||
829 | continue; | ||
830 | |||
831 | rc = dlpar_acquire_drc(lmbs[i].drc_index); | ||
832 | if (rc) | ||
833 | break; | ||
834 | |||
835 | rc = dlpar_add_lmb(&lmbs[i]); | ||
836 | if (rc) { | ||
837 | dlpar_release_drc(lmbs[i].drc_index); | ||
838 | break; | ||
839 | } | ||
840 | |||
841 | lmbs[i].reserved = 1; | ||
842 | } | ||
843 | |||
844 | if (rc) { | ||
845 | pr_err("Memory indexed-count-add failed, removing any added LMBs\n"); | ||
846 | |||
847 | for (i = start_index; i < end_index; i++) { | ||
848 | if (!lmbs[i].reserved) | ||
849 | continue; | ||
850 | |||
851 | rc = dlpar_remove_lmb(&lmbs[i]); | ||
852 | if (rc) | ||
853 | pr_err("Failed to remove LMB, drc index %x\n", | ||
854 | be32_to_cpu(lmbs[i].drc_index)); | ||
855 | else | ||
856 | dlpar_release_drc(lmbs[i].drc_index); | ||
857 | } | ||
858 | rc = -EINVAL; | ||
859 | } else { | ||
860 | for (i = start_index; i < end_index; i++) { | ||
861 | if (!lmbs[i].reserved) | ||
862 | continue; | ||
863 | |||
864 | pr_info("Memory at %llx (drc index %x) was hot-added\n", | ||
865 | lmbs[i].base_addr, lmbs[i].drc_index); | ||
866 | lmbs[i].reserved = 0; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | return rc; | ||
871 | } | ||
872 | |||
782 | int dlpar_memory(struct pseries_hp_errorlog *hp_elog) | 873 | int dlpar_memory(struct pseries_hp_errorlog *hp_elog) |
783 | { | 874 | { |
784 | struct device_node *dn; | 875 | struct device_node *dn; |
@@ -786,9 +877,6 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) | |||
786 | u32 count, drc_index; | 877 | u32 count, drc_index; |
787 | int rc; | 878 | int rc; |
788 | 879 | ||
789 | count = hp_elog->_drc_u.drc_count; | ||
790 | drc_index = hp_elog->_drc_u.drc_index; | ||
791 | |||
792 | lock_device_hotplug(); | 880 | lock_device_hotplug(); |
793 | 881 | ||
794 | dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | 882 | dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); |
@@ -805,22 +893,35 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) | |||
805 | 893 | ||
806 | switch (hp_elog->action) { | 894 | switch (hp_elog->action) { |
807 | case PSERIES_HP_ELOG_ACTION_ADD: | 895 | case PSERIES_HP_ELOG_ACTION_ADD: |
808 | if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) | 896 | if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
897 | count = hp_elog->_drc_u.drc_count; | ||
809 | rc = dlpar_memory_add_by_count(count, prop); | 898 | rc = dlpar_memory_add_by_count(count, prop); |
810 | else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) | 899 | } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
900 | drc_index = hp_elog->_drc_u.drc_index; | ||
811 | rc = dlpar_memory_add_by_index(drc_index, prop); | 901 | rc = dlpar_memory_add_by_index(drc_index, prop); |
812 | else | 902 | } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { |
903 | count = hp_elog->_drc_u.ic.count; | ||
904 | drc_index = hp_elog->_drc_u.ic.index; | ||
905 | rc = dlpar_memory_add_by_ic(count, drc_index, prop); | ||
906 | } else { | ||
813 | rc = -EINVAL; | 907 | rc = -EINVAL; |
908 | } | ||
909 | |||
814 | break; | 910 | break; |
815 | case PSERIES_HP_ELOG_ACTION_REMOVE: | 911 | case PSERIES_HP_ELOG_ACTION_REMOVE: |
816 | if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) | 912 | if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
913 | count = hp_elog->_drc_u.drc_count; | ||
817 | rc = dlpar_memory_remove_by_count(count, prop); | 914 | rc = dlpar_memory_remove_by_count(count, prop); |
818 | else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) | 915 | } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
916 | drc_index = hp_elog->_drc_u.drc_index; | ||
819 | rc = dlpar_memory_remove_by_index(drc_index, prop); | 917 | rc = dlpar_memory_remove_by_index(drc_index, prop); |
820 | else | 918 | } else { |
821 | rc = -EINVAL; | 919 | rc = -EINVAL; |
920 | } | ||
921 | |||
822 | break; | 922 | break; |
823 | case PSERIES_HP_ELOG_ACTION_READD: | 923 | case PSERIES_HP_ELOG_ACTION_READD: |
924 | drc_index = hp_elog->_drc_u.drc_index; | ||
824 | rc = dlpar_memory_readd_by_index(drc_index, prop); | 925 | rc = dlpar_memory_readd_by_index(drc_index, prop); |
825 | break; | 926 | break; |
826 | default: | 927 | default: |