mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
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:
parent
4a4bdfea7c
commit
bdf5fc6338
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user