powerpc/pseries: Update LMB associativity index during DLPAR add/remove

The associativity array index specified for a LMB in the device tree,
/ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory, needs to be updated
prior to DLPAR adding a LMB and after DLPAR removing a LMB.

Without doing this step in the DLPAR add process a LMB could be configured
with the incorrect affinity. For a LMB that was not present at boot the
affinity index is set to 0xffffffff, which defaults to adding the LMB to
the first online node since the index is not a valid value. Or, the
affinity index could contain a stale value if the LMB was present at boot
but later DLPAR removed and is being DLPAR added back to the system.

This patch adds a step in the DLPAR add flow to look up the associativity
index for a LMB prior to adding a LMB and setting the associativity to
0xffffffff when a LMB is removed.

This patch also modifies the DLPAR add/remove flow to no longer do a single
update of the device tree property after all of the requested DLPAR
operations are complete and now does a property update during the add
or remove of each LMB.

Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
Nathan Fontenot 2016-02-10 11:12:13 -06:00 committed by Michael Ellerman
parent 4a4bdfea7c
commit bdf5fc6338

View File

@ -116,6 +116,155 @@ static struct property *dlpar_clone_drconf_property(struct device_node *dn)
return new_prop;
}
static void dlpar_update_drconf_property(struct device_node *dn,
struct property *prop)
{
struct of_drconf_cell *lmbs;
u32 num_lmbs, *p;
int i;
/* Convert the property back to BE */
p = prop->value;
num_lmbs = *p;
*p = cpu_to_be32(*p);
p++;
lmbs = (struct of_drconf_cell *)p;
for (i = 0; i < num_lmbs; i++) {
lmbs[i].base_addr = cpu_to_be64(lmbs[i].base_addr);
lmbs[i].drc_index = cpu_to_be32(lmbs[i].drc_index);
lmbs[i].flags = cpu_to_be32(lmbs[i].flags);
}
rtas_hp_event = true;
of_update_property(dn, prop);
rtas_hp_event = false;
}
static int dlpar_update_device_tree_lmb(struct of_drconf_cell *lmb)
{
struct device_node *dn;
struct property *prop;
struct of_drconf_cell *lmbs;
u32 *p, num_lmbs;
int i;
dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
if (!dn)
return -ENODEV;
prop = dlpar_clone_drconf_property(dn);
if (!prop) {
of_node_put(dn);
return -ENODEV;
}
p = prop->value;
num_lmbs = *p++;
lmbs = (struct of_drconf_cell *)p;
for (i = 0; i < num_lmbs; i++) {
if (lmbs[i].drc_index == lmb->drc_index) {
lmbs[i].flags = lmb->flags;
lmbs[i].aa_index = lmb->aa_index;
dlpar_update_drconf_property(dn, prop);
break;
}
}
of_node_put(dn);
return 0;
}
static u32 lookup_lmb_associativity_index(struct of_drconf_cell *lmb)
{
struct device_node *parent, *lmb_node, *dr_node;
const u32 *lmb_assoc;
const u32 *assoc_arrays;
u32 aa_index;
int aa_arrays, aa_array_entries, aa_array_sz;
int i;
parent = of_find_node_by_path("/");
if (!parent)
return -ENODEV;
lmb_node = dlpar_configure_connector(cpu_to_be32(lmb->drc_index),
parent);
of_node_put(parent);
if (!lmb_node)
return -EINVAL;
lmb_assoc = of_get_property(lmb_node, "ibm,associativity", NULL);
if (!lmb_assoc) {
dlpar_free_cc_nodes(lmb_node);
return -ENODEV;
}
dr_node = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
if (!dr_node) {
dlpar_free_cc_nodes(lmb_node);
return -ENODEV;
}
assoc_arrays = of_get_property(dr_node,
"ibm,associativity-lookup-arrays",
NULL);
of_node_put(dr_node);
if (!assoc_arrays) {
dlpar_free_cc_nodes(lmb_node);
return -ENODEV;
}
/* The ibm,associativity-lookup-arrays property is defined to be
* a 32-bit value specifying the number of associativity arrays
* followed by a 32-bitvalue specifying the number of entries per
* array, followed by the associativity arrays.
*/
aa_arrays = be32_to_cpu(assoc_arrays[0]);
aa_array_entries = be32_to_cpu(assoc_arrays[1]);
aa_array_sz = aa_array_entries * sizeof(u32);
aa_index = -1;
for (i = 0; i < aa_arrays; i++) {
int indx = (i * aa_array_entries) + 2;
if (memcmp(&assoc_arrays[indx], &lmb_assoc[1], aa_array_sz))
continue;
aa_index = i;
break;
}
dlpar_free_cc_nodes(lmb_node);
return aa_index;
}
static int dlpar_add_device_tree_lmb(struct of_drconf_cell *lmb)
{
int aa_index;
lmb->flags |= DRCONF_MEM_ASSIGNED;
aa_index = lookup_lmb_associativity_index(lmb);
if (aa_index < 0) {
pr_err("Couldn't find associativity index for drc index %x\n",
lmb->drc_index);
return aa_index;
}
lmb->aa_index = aa_index;
return dlpar_update_device_tree_lmb(lmb);
}
static int dlpar_remove_device_tree_lmb(struct of_drconf_cell *lmb)
{
lmb->flags &= ~DRCONF_MEM_ASSIGNED;
lmb->aa_index = 0xffffffff;
return dlpar_update_device_tree_lmb(lmb);
}
static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb)
{
unsigned long section_nr;
@ -243,8 +392,8 @@ static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
memblock_remove(lmb->base_addr, block_sz);
dlpar_release_drc(lmb->drc_index);
dlpar_remove_device_tree_lmb(lmb);
lmb->flags &= ~DRCONF_MEM_ASSIGNED;
return 0;
}
@ -435,9 +584,19 @@ static int dlpar_add_lmb(struct of_drconf_cell *lmb)
if (rc)
return rc;
rc = dlpar_add_lmb_memory(lmb);
if (rc)
rc = dlpar_add_device_tree_lmb(lmb);
if (rc) {
pr_err("Couldn't update device tree for drc index %x\n",
lmb->drc_index);
dlpar_release_drc(lmb->drc_index);
return rc;
}
rc = dlpar_add_lmb_memory(lmb);
if (rc) {
dlpar_remove_device_tree_lmb(lmb);
dlpar_release_drc(lmb->drc_index);
}
return rc;
}
@ -542,31 +701,6 @@ static int dlpar_memory_add_by_index(u32 drc_index, struct property *prop)
return rc;
}
static void dlpar_update_drconf_property(struct device_node *dn,
struct property *prop)
{
struct of_drconf_cell *lmbs;
u32 num_lmbs, *p;
int i;
/* Convert the property back to BE */
p = prop->value;
num_lmbs = *p;
*p = cpu_to_be32(*p);
p++;
lmbs = (struct of_drconf_cell *)p;
for (i = 0; i < num_lmbs; i++) {
lmbs[i].base_addr = cpu_to_be64(lmbs[i].base_addr);
lmbs[i].drc_index = cpu_to_be32(lmbs[i].drc_index);
lmbs[i].flags = cpu_to_be32(lmbs[i].flags);
}
rtas_hp_event = true;
of_update_property(dn, prop);
rtas_hp_event = false;
}
int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
{
struct device_node *dn;
@ -614,10 +748,7 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
break;
}
if (rc)
dlpar_free_drconf_property(prop);
else
dlpar_update_drconf_property(dn, prop);
dlpar_free_drconf_property(prop);
dlpar_memory_out:
of_node_put(dn);