root/arch/powerpc/platforms/pseries/pmem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pmem_drc_add_node
  2. pmem_drc_remove_node
  3. dlpar_hp_pmem
  4. pseries_pmem_init

   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 %pOF, rc: %d, drc index: %x\n",
  56                         dn, 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 %pOFn, rc: %d\n",
  97                         drc_index, dn, 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 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         drc_index = hp_elog->_drc_u.drc_index;
 127 
 128         lock_device_hotplug();
 129 
 130         if (hp_elog->action == PSERIES_HP_ELOG_ACTION_ADD) {
 131                 rc = pmem_drc_add_node(drc_index);
 132         } else if (hp_elog->action == PSERIES_HP_ELOG_ACTION_REMOVE) {
 133                 rc = pmem_drc_remove_node(drc_index);
 134         } else {
 135                 pr_err("Unsupported hotplug action (%d)\n", hp_elog->action);
 136                 rc = -EINVAL;
 137         }
 138 
 139         unlock_device_hotplug();
 140         return rc;
 141 }
 142 
 143 const struct of_device_id drc_pmem_match[] = {
 144         { .type = "ibm,persistent-memory", },
 145         {}
 146 };
 147 
 148 static int pseries_pmem_init(void)
 149 {
 150         pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory");
 151         if (!pmem_node)
 152                 return 0;
 153 
 154         /*
 155          * The generic OF bus probe/populate handles creating platform devices
 156          * from the child (ibm,pmemory) nodes. The generic code registers an of
 157          * reconfig notifier to handle the hot-add/remove cases too.
 158          */
 159         of_platform_bus_probe(pmem_node, drc_pmem_match, NULL);
 160 
 161         return 0;
 162 }
 163 machine_arch_initcall(pseries, pseries_pmem_init);

/* [<][>][^][v][top][bottom][index][help] */