Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/pseries: PAPR persistent memory support

This patch implements support for discovering storage class memory
devices at boot and for handling hotplug of new regions via RTAS
hotplug events.

Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
[mpe: Fix CONFIG_MEMORY_HOTPLUG=n build]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Oliver O'Halloran and committed by
Michael Ellerman
4c5d87db 422123cc

+193 -4
+3 -1
arch/powerpc/include/asm/firmware.h
··· 53 53 #define FW_FEATURE_DRMEM_V2 ASM_CONST(0x0000000400000000) 54 54 #define FW_FEATURE_DRC_INFO ASM_CONST(0x0000000800000000) 55 55 #define FW_FEATURE_BLOCK_REMOVE ASM_CONST(0x0000001000000000) 56 + #define FW_FEATURE_PAPR_SCM ASM_CONST(0x0000002000000000) 56 57 57 58 #ifndef __ASSEMBLY__ 58 59 ··· 71 70 FW_FEATURE_SET_MODE | FW_FEATURE_BEST_ENERGY | 72 71 FW_FEATURE_TYPE1_AFFINITY | FW_FEATURE_PRRN | 73 72 FW_FEATURE_HPT_RESIZE | FW_FEATURE_DRMEM_V2 | 74 - FW_FEATURE_DRC_INFO | FW_FEATURE_BLOCK_REMOVE, 73 + FW_FEATURE_DRC_INFO | FW_FEATURE_BLOCK_REMOVE | 74 + FW_FEATURE_PAPR_SCM, 75 75 FW_FEATURE_PSERIES_ALWAYS = 0, 76 76 FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL, 77 77 FW_FEATURE_POWERNV_ALWAYS = 0,
+9 -1
arch/powerpc/include/asm/hvcall.h
··· 296 296 #define H_INT_ESB 0x3C8 297 297 #define H_INT_SYNC 0x3CC 298 298 #define H_INT_RESET 0x3D0 299 - #define MAX_HCALL_OPCODE H_INT_RESET 299 + #define H_SCM_READ_METADATA 0x3E4 300 + #define H_SCM_WRITE_METADATA 0x3E8 301 + #define H_SCM_BIND_MEM 0x3EC 302 + #define H_SCM_UNBIND_MEM 0x3F0 303 + #define H_SCM_QUERY_BLOCK_MEM_BINDING 0x3F4 304 + #define H_SCM_QUERY_LOGICAL_MEM_BINDING 0x3F8 305 + #define H_SCM_MEM_QUERY 0x3FC 306 + #define H_SCM_BLOCK_CLEAR 0x400 307 + #define MAX_HCALL_OPCODE H_SCM_BLOCK_CLEAR 300 308 301 309 /* H_VIOCTL functions */ 302 310 #define H_GET_VIOA_DUMP_SIZE 0x01
+2
arch/powerpc/include/asm/rtas.h
··· 125 125 #define RTAS_TYPE_INFO 0xE2 126 126 #define RTAS_TYPE_DEALLOC 0xE3 127 127 #define RTAS_TYPE_DUMP 0xE4 128 + #define RTAS_TYPE_HOTPLUG 0xE5 128 129 /* I don't add PowerMGM events right now, this is a different topic */ 129 130 #define RTAS_TYPE_PMGM_POWER_SW_ON 0x60 130 131 #define RTAS_TYPE_PMGM_POWER_SW_OFF 0x61 ··· 330 329 #define PSERIES_HP_ELOG_RESOURCE_MEM 2 331 330 #define PSERIES_HP_ELOG_RESOURCE_SLOT 3 332 331 #define PSERIES_HP_ELOG_RESOURCE_PHB 4 332 + #define PSERIES_HP_ELOG_RESOURCE_PMEM 6 333 333 334 334 #define PSERIES_HP_ELOG_ACTION_ADD 1 335 335 #define PSERIES_HP_ELOG_ACTION_REMOVE 2
+2
arch/powerpc/kernel/rtasd.c
··· 91 91 return "Dump Notification Event"; 92 92 case RTAS_TYPE_PRRN: 93 93 return "Platform Resource Reassignment Event"; 94 + case RTAS_TYPE_HOTPLUG: 95 + return "Hotplug Event"; 94 96 } 95 97 96 98 return rtas_type[0];
+1 -1
arch/powerpc/platforms/pseries/Makefile
··· 13 13 obj-$(CONFIG_PSERIES_ENERGY) += pseries_energy.o 14 14 15 15 obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o 16 - obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o 16 + obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o pmem.o 17 17 18 18 obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o 19 19 obj-$(CONFIG_HVCS) += hvcserver.o
+4
arch/powerpc/platforms/pseries/dlpar.c
··· 355 355 case PSERIES_HP_ELOG_RESOURCE_CPU: 356 356 rc = dlpar_cpu(hp_elog); 357 357 break; 358 + case PSERIES_HP_ELOG_RESOURCE_PMEM: 359 + rc = dlpar_hp_pmem(hp_elog); 360 + break; 361 + 358 362 default: 359 363 pr_warn_ratelimited("Invalid resource (%d) specified\n", 360 364 hp_elog->resource);
+1
arch/powerpc/platforms/pseries/firmware.c
··· 66 66 {FW_FEATURE_BEST_ENERGY, "hcall-best-energy-1*"}, 67 67 {FW_FEATURE_HPT_RESIZE, "hcall-hpt-resize"}, 68 68 {FW_FEATURE_BLOCK_REMOVE, "hcall-block-remove"}, 69 + {FW_FEATURE_PAPR_SCM, "hcall-scm"}, 69 70 }; 70 71 71 72 /* Build up the firmware features bitmask using the contents of
+164
arch/powerpc/platforms/pseries/pmem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Handles hot and cold plug of persistent memory regions on pseries. 5 + */ 6 + 7 + #define pr_fmt(fmt) "pseries-pmem: " fmt 8 + 9 + #include <linux/kernel.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/delay.h> 12 + #include <linux/sched.h> /* for idle_task_exit */ 13 + #include <linux/sched/hotplug.h> 14 + #include <linux/cpu.h> 15 + #include <linux/of.h> 16 + #include <linux/of_platform.h> 17 + #include <linux/slab.h> 18 + #include <asm/prom.h> 19 + #include <asm/rtas.h> 20 + #include <asm/firmware.h> 21 + #include <asm/machdep.h> 22 + #include <asm/vdso_datapage.h> 23 + #include <asm/plpar_wrappers.h> 24 + #include <asm/topology.h> 25 + 26 + #include "pseries.h" 27 + #include "offline_states.h" 28 + 29 + static struct device_node *pmem_node; 30 + 31 + static ssize_t pmem_drc_add_node(u32 drc_index) 32 + { 33 + struct device_node *dn; 34 + int rc; 35 + 36 + pr_debug("Attempting to add pmem node, drc index: %x\n", drc_index); 37 + 38 + rc = dlpar_acquire_drc(drc_index); 39 + if (rc) { 40 + pr_err("Failed to acquire DRC, rc: %d, drc index: %x\n", 41 + rc, drc_index); 42 + return -EINVAL; 43 + } 44 + 45 + dn = dlpar_configure_connector(cpu_to_be32(drc_index), pmem_node); 46 + if (!dn) { 47 + pr_err("configure-connector failed for drc %x\n", drc_index); 48 + dlpar_release_drc(drc_index); 49 + return -EINVAL; 50 + } 51 + 52 + /* NB: The of reconfig notifier creates platform device from the node */ 53 + rc = dlpar_attach_node(dn, pmem_node); 54 + if (rc) { 55 + pr_err("Failed to attach node %s, rc: %d, drc index: %x\n", 56 + dn->name, rc, drc_index); 57 + 58 + if (dlpar_release_drc(drc_index)) 59 + dlpar_free_cc_nodes(dn); 60 + 61 + return rc; 62 + } 63 + 64 + pr_info("Successfully added %pOF, drc index: %x\n", dn, drc_index); 65 + 66 + return 0; 67 + } 68 + 69 + static ssize_t pmem_drc_remove_node(u32 drc_index) 70 + { 71 + struct device_node *dn; 72 + uint32_t index; 73 + int rc; 74 + 75 + for_each_child_of_node(pmem_node, dn) { 76 + if (of_property_read_u32(dn, "ibm,my-drc-index", &index)) 77 + continue; 78 + if (index == drc_index) 79 + break; 80 + } 81 + 82 + if (!dn) { 83 + pr_err("Attempting to remove unused DRC index %x\n", drc_index); 84 + return -ENODEV; 85 + } 86 + 87 + pr_debug("Attempting to remove %pOF, drc index: %x\n", dn, drc_index); 88 + 89 + /* * NB: tears down the ibm,pmemory device as a side-effect */ 90 + rc = dlpar_detach_node(dn); 91 + if (rc) 92 + return rc; 93 + 94 + rc = dlpar_release_drc(drc_index); 95 + if (rc) { 96 + pr_err("Failed to release drc (%x) for CPU %s, rc: %d\n", 97 + drc_index, dn->name, rc); 98 + dlpar_attach_node(dn, pmem_node); 99 + return rc; 100 + } 101 + 102 + pr_info("Successfully removed PMEM with drc index: %x\n", drc_index); 103 + 104 + return 0; 105 + } 106 + 107 + int dlpar_hp_pmem(struct pseries_hp_errorlog *hp_elog) 108 + { 109 + u32 count, drc_index; 110 + int rc; 111 + 112 + /* slim chance, but we might get a hotplug event while booting */ 113 + if (!pmem_node) 114 + pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory"); 115 + if (!pmem_node) { 116 + pr_err("Hotplug event for a pmem device, but none exists\n"); 117 + return -ENODEV; 118 + } 119 + 120 + if (hp_elog->id_type != PSERIES_HP_ELOG_ID_DRC_INDEX) { 121 + pr_err("Unsupported hotplug event type %d\n", 122 + hp_elog->id_type); 123 + return -EINVAL; 124 + } 125 + 126 + count = hp_elog->_drc_u.drc_count; 127 + drc_index = hp_elog->_drc_u.drc_index; 128 + 129 + lock_device_hotplug(); 130 + 131 + if (hp_elog->action == PSERIES_HP_ELOG_ACTION_ADD) { 132 + rc = pmem_drc_add_node(drc_index); 133 + } else if (hp_elog->action == PSERIES_HP_ELOG_ACTION_REMOVE) { 134 + rc = pmem_drc_remove_node(drc_index); 135 + } else { 136 + pr_err("Unsupported hotplug action (%d)\n", hp_elog->action); 137 + rc = -EINVAL; 138 + } 139 + 140 + unlock_device_hotplug(); 141 + return rc; 142 + } 143 + 144 + const struct of_device_id drc_pmem_match[] = { 145 + { .type = "ibm,persistent-memory", }, 146 + {} 147 + }; 148 + 149 + static int pseries_pmem_init(void) 150 + { 151 + pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory"); 152 + if (!pmem_node) 153 + return 0; 154 + 155 + /* 156 + * The generic OF bus probe/populate handles creating platform devices 157 + * from the child (ibm,pmemory) nodes. The generic code registers an of 158 + * reconfig notifier to handle the hot-add/remove cases too. 159 + */ 160 + of_platform_bus_probe(pmem_node, drc_pmem_match, NULL); 161 + 162 + return 0; 163 + } 164 + machine_arch_initcall(pseries, pseries_pmem_init);
+5
arch/powerpc/platforms/pseries/pseries.h
··· 65 65 66 66 #ifdef CONFIG_MEMORY_HOTPLUG 67 67 int dlpar_memory(struct pseries_hp_errorlog *hp_elog); 68 + int dlpar_hp_pmem(struct pseries_hp_errorlog *hp_elog); 68 69 #else 69 70 static inline int dlpar_memory(struct pseries_hp_errorlog *hp_elog) 71 + { 72 + return -EOPNOTSUPP; 73 + } 74 + static inline int dlpar_hp_pmem(struct pseries_hp_errorlog *hp_elog) 70 75 { 71 76 return -EOPNOTSUPP; 72 77 }
+2 -1
arch/powerpc/platforms/pseries/ras.c
··· 333 333 * hotplug events on the ras_log_buf to be handled by rtas_errd. 334 334 */ 335 335 if (hp_elog->resource == PSERIES_HP_ELOG_RESOURCE_MEM || 336 - hp_elog->resource == PSERIES_HP_ELOG_RESOURCE_CPU) 336 + hp_elog->resource == PSERIES_HP_ELOG_RESOURCE_CPU || 337 + hp_elog->resource == PSERIES_HP_ELOG_RESOURCE_PMEM) 337 338 queue_hotplug_event(hp_elog); 338 339 else 339 340 log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, 0);