[PATCH 3/3] postmigration/memory: Associativity & ibm,dynamic-memory-v2

From: Michael Bringmann
Date: Thu Nov 02 2017 - 16:53:36 EST


powerpc/hotplug/memory: Apply changes to the associativity of memory
blocks described by the 'ibm,dynamic-memory-v2' property regarding
the topology of LPARS in Post Migration events. Previous efforts
only recognized whether a block's assignment had changed in the
property. Topology migration requires us to compare the 'aa_index'
values of the old/new properties and 'readd' any block for which the
setting has changed.

Signed-off-by: Michael Bringmann <mwb@xxxxxxxxxxxxxxxxxx>
---
arch/powerpc/include/asm/prom.h | 12 ++
arch/powerpc/platforms/pseries/hotplug-memory.c | 175 +++++++++++++++++++++--
2 files changed, 175 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h
index 825bd59..e16ef0f 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -92,6 +92,18 @@ struct of_drconf_cell {
u32 flags;
};

+/* The of_drconf_cell_v2 struct defines the layout of the LMB array
+ * specified in the device tree property
+ * ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory-v2
+ */
+struct of_drconf_cell_v2 {
+ u32 num_seq_lmbs;
+ u64 base_address;
+ u32 drc_index;
+ u32 aa_index;
+ u32 flags;
+} __attribute__((packed));
+
#define DRCONF_MEM_ASSIGNED 0x00000008
#define DRCONF_MEM_AI_INVALID 0x00000040
#define DRCONF_MEM_RESERVED 0x00000080
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
index 7aba515..aa44011 100644
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
@@ -1153,9 +1153,9 @@ static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
memblock_size);
break;
} else if ((!(be32_to_cpu(old_drmem[i].flags) &
- DRCONF_MEM_ASSIGNED)) &&
- (be32_to_cpu(new_drmem[i].flags) &
- DRCONF_MEM_ASSIGNED)) {
+ DRCONF_MEM_ASSIGNED)) &&
+ (be32_to_cpu(new_drmem[i].flags) &
+ DRCONF_MEM_ASSIGNED)) {
rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr),
memblock_size);
rc = (rc < 0) ? -EINVAL : 0;
@@ -1171,14 +1171,111 @@ static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
return rc;
}

+static inline int pseries_memory_v2_find_drc(u32 drc_index,
+ u64 *base_addr, unsigned long memblock_size,
+ struct of_drconf_cell_v2 **drmem,
+ struct of_drconf_cell_v2 *last_drmem)
+{
+ struct of_drconf_cell_v2 *dm = (*drmem);
+
+ while (dm < last_drmem) {
+ if ((be32_to_cpu(dm->drc_index) <= drc_index) &&
+ (drc_index <= (be32_to_cpu(dm->drc_index)+
+ be32_to_cpu(dm->num_seq_lmbs)-1))) {
+ int offset = drc_index - be32_to_cpu(dm->drc_index);
+ (*base_addr) = be64_to_cpu(dm->base_address) +
+ (offset * memblock_size);
+ break;
+ } else if (drc_index > (be32_to_cpu(dm->drc_index)+
+ be32_to_cpu(dm->num_seq_lmbs)-1)) {
+ dm++;
+ (*drmem) = dm;
+ } else if (be32_to_cpu(dm->drc_index) > drc_index) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int pseries_update_drconf_memory_v2(struct of_reconfig_data *pr)
+{
+ struct of_drconf_cell_v2 *new_drmem, *old_drmem, *last_old_drmem;
+ unsigned long memblock_size;
+ u32 new_entries, old_entries;
+ u64 old_base_addr;
+ __be32 *p;
+ int i, rc = 0;
+
+ if (rtas_hp_event)
+ return 0;
+
+ memblock_size = pseries_memory_block_size();
+ if (!memblock_size)
+ return -EINVAL;
+
+ /* The first int of the property is the number of lmb's
+ * described by the property. This is followed by an array
+ * of of_drconf_cell_v2 entries. Get the number of entries
+ * and skip to the array of of_drconf_cell_v2's.
+ */
+ p = (__be32 *) pr->old_prop->value;
+ if (!p)
+ return -EINVAL;
+ old_entries = be32_to_cpu(*p++);
+ old_drmem = (struct of_drconf_cell_v2 *)p;
+ last_old_drmem = old_drmem +
+ (sizeof(struct of_drconf_cell_v2) * old_entries);
+
+ p = (__be32 *)pr->prop->value;
+ new_entries = be32_to_cpu(*p++);
+ new_drmem = (struct of_drconf_cell_v2 *)p;
+
+ for (i = 0; i < new_entries; i++) {
+ int j;
+ u32 new_drc_index = be32_to_cpu(new_drmem->drc_index);
+
+ for (j = 0; j < new_drmem->num_seq_lmbs; j++) {
+ if (!pseries_memory_v2_find_drc(new_drc_index+j,
+ &old_base_addr,
+ memblock_size,
+ &old_drmem,
+ last_old_drmem)) {
+ if ((be32_to_cpu(old_drmem->flags) &
+ DRCONF_MEM_ASSIGNED) &&
+ (!(be32_to_cpu(new_drmem->flags) &
+ DRCONF_MEM_ASSIGNED))) {
+ rc = pseries_remove_memblock(
+ old_base_addr,
+ memblock_size);
+ } else if ((!(be32_to_cpu(old_drmem->flags) &
+ DRCONF_MEM_ASSIGNED)) &&
+ (be32_to_cpu(new_drmem->flags) &
+ DRCONF_MEM_ASSIGNED)) {
+ rc = memblock_add(
+ old_base_addr, memblock_size);
+ } else if ((be32_to_cpu(old_drmem->aa_index) !=
+ be32_to_cpu(new_drmem->aa_index)) &&
+ (be32_to_cpu(new_drmem->flags) &
+ DRCONF_MEM_ASSIGNED)) {
+ pseries_memory_readd_by_index(
+ new_drc_index+j);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
struct assoc_arrays {
u32 n_arrays;
u32 array_sz;
const __be32 *arrays;
};

-static int pseries_update_ala_memory_aai(int aa_index,
- struct property *dmprop)
+static int pseries_update_ala_memory_aai_v1(int aa_index,
+ struct property *dmprop)
{
struct of_drconf_cell *drmem;
u32 entries;
@@ -1208,11 +1305,47 @@ static int pseries_update_ala_memory_aai(int aa_index,
return 0;
}

+static int pseries_update_ala_memory_aai_v2(int aa_index,
+ struct property *dmprop)
+{
+ struct of_drconf_cell_v2 *drmem;
+ u32 entries;
+ __be32 *p;
+ int i;
+
+ p = (__be32 *) dmprop->value;
+ if (!p)
+ return -EINVAL;
+
+ /* The first int of the property is the number of lmb's
+ * described by the property. This is followed by an array
+ * of of_drconf_cell_v2 entries. Get the number of entries
+ * and skip to the array of of_drconf_cell_v2's.
+ */
+ entries = be32_to_cpu(*p++);
+ drmem = (struct of_drconf_cell_v2 *)p;
+
+ for (i = 0; i < entries; i++) {
+ if ((be32_to_cpu(drmem[i].aa_index) != aa_index) &&
+ (be32_to_cpu(drmem[i].flags) & DRCONF_MEM_ASSIGNED)) {
+ int j;
+ int lim = be32_to_cpu(drmem->num_seq_lmbs);
+ u32 drc_index = be32_to_cpu(drmem->drc_index);
+
+ for (j = 0; j < lim; j++)
+ pseries_memory_readd_by_index(drc_index+j);
+ }
+ }
+
+ return 0;
+}
+
static int pseries_update_ala_memory(struct of_reconfig_data *pr)
{
struct assoc_arrays new_ala, old_ala;
struct device_node *dn;
struct property *dmprop;
+ bool v1 = true;
__be32 *p;
int i, lim;

@@ -1225,8 +1358,13 @@ static int pseries_update_ala_memory(struct of_reconfig_data *pr)

dmprop = of_find_property(dn, "ibm,dynamic-memory", NULL);
if (!dmprop) {
- of_node_put(dn);
- return -ENODEV;
+ v1 = false;
+ dmprop = of_find_property(dn, "ibm,dynamic-memory-v2",
+ NULL);
+ if (!dmprop) {
+ of_node_put(dn);
+ return -ENODEV;
+ }
}

/*
@@ -1268,19 +1406,30 @@ static int pseries_update_ala_memory(struct of_reconfig_data *pr)
new_ala.array_sz))
continue;

- pseries_update_ala_memory_aai(i, dmprop);
+ if (v1)
+ pseries_update_ala_memory_aai_v1(i, dmprop);
+ else
+ pseries_update_ala_memory_aai_v2(i, dmprop);
}

- for (i = lim; i < new_ala.n_arrays; i++)
- pseries_update_ala_memory_aai(i, dmprop);
+ for (i = lim; i < new_ala.n_arrays; i++) {
+ if (v1)
+ pseries_update_ala_memory_aai_v1(i, dmprop);
+ else
+ pseries_update_ala_memory_aai_v2(i, dmprop);
+ }

} else {
/* Update all entries representing these rows;
* as all rows have different sizes, none can
* have equivalent values.
*/
- for (i = 0; i < lim; i++)
- pseries_update_ala_memory_aai(i, dmprop);
+ for (i = 0; i < lim; i++) {
+ if (v1)
+ pseries_update_ala_memory_aai_v1(i, dmprop);
+ else
+ pseries_update_ala_memory_aai_v2(i, dmprop);
+ }
}

of_node_put(dn);
@@ -1303,6 +1452,8 @@ static int pseries_memory_notifier(struct notifier_block *nb,
case OF_RECONFIG_UPDATE_PROPERTY:
if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
err = pseries_update_drconf_memory(rd);
+ if (!strcmp(rd->prop->name, "ibm,dynamic-memory-v2"))
+ err = pseries_update_drconf_memory_v2(rd);
if (!strcmp(rd->prop->name,
"ibm,associativity-lookup-arrays"))
err = pseries_update_ala_memory(rd);