Subject: Re: [PATCH v6 3/3] powerpc/pseries: Implement indexed-count hotplug memory remove

From: Michael Roth <[email-masked]>
Date: Mon, 14 Nov 2016 18:58:46 -0600

Quoting Nathan Fontenot (2016-10-18 12:21:06)
> From: Sahil Mehta <[email-masked]>
> 
> Indexed-count remove for memory hotplug guarantees that a contiguous block
> of <count> lmbs beginning at a specified <index> will be unassigned (NOT
> that <count> lmbs will be removed). Because of Qemu's per-DIMM memory
> management, the removal of a contiguous block of memory currently
> requires a series of individual calls. Indexed-count remove reduces
> this series into a single call.
> 
> Signed-off-by: Sahil Mehta <[email-masked]>
> Signed-off-by: Nathan Fontenot <[email-masked]>
> ---
> v2:     -use u32s drc_index and count instead of u32 ic[]
>          in dlpar_memory
> v3:     -add logic to handle invalid drc_index input
> v4:     -none
> v5:     -Update for() loop to start at start_index
> v6:     -none
> 
>  arch/powerpc/platforms/pseries/hotplug-memory.c |   90 +++++++++++++++++++++++
>  1 file changed, 90 insertions(+)
> 
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index badc66d..19ad081 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -558,6 +558,92 @@ static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
>         return rc;
>  }
> 
> +static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index,
> +                                    struct property *prop)
> +{
> +       struct of_drconf_cell *lmbs;
> +       u32 num_lmbs, *p;
> +       int i, rc, start_lmb_found;
> +       int lmbs_available = 0, start_index = 0, end_index;
> +
> +       pr_info("Attempting to hot-remove %u LMB(s) at %x\n",
> +               lmbs_to_remove, drc_index);
> +
> +       if (lmbs_to_remove == 0)
> +               return -EINVAL;
> +
> +       p = prop->value;
> +       num_lmbs = *p++;
> +       lmbs = (struct of_drconf_cell *)p;
> +       start_lmb_found = 0;
> +
> +       /* Navigate to drc_index */
> +       while (start_index < num_lmbs) {
> +               if (lmbs[start_index].drc_index == drc_index) {
> +                       start_lmb_found = 1;
> +                       break;
> +               }
> +
> +               start_index++;
> +       }
> +
> +       if (!start_lmb_found)
> +               return -EINVAL;
> +
> +       end_index = start_index + lmbs_to_remove;
> +
> +       /* Validate that there are enough LMBs to satisfy the request */
> +       for (i = start_index; i < end_index; i++) {
> +               if (lmbs[i].flags & DRCONF_MEM_RESERVED)
> +                       break;
> +
> +               lmbs_available++;
> +       }
> +
> +       if (lmbs_available < lmbs_to_remove)
> +               return -EINVAL;
> +
> +       for (i = start_index; i < end_index; i++) {
> +               if (!(lmbs[i].flags & DRCONF_MEM_ASSIGNED))
> +                       continue;
> +
> +               rc = dlpar_remove_lmb(&lmbs[i]);

dlpar_remove_lmb() currently does both offlining of the memory as well
as releasing the LMB back to the platform, but the specification for
hotplug notifications has the following verbage regarding
indexed-count/count identifiers:

'When using “drc count” or “drc count indexed” as the Hotplug Identifier,
the OS should take steps to verify the entirety of the request can be
satisfied before proceeding with the hotplug / unplug operations. If
only a partial count can be satisfied, the OS should ignore the entirety
of the request. If the OS cannot determine this beforehand, it should
satisfy the hotplug / unplug request for as many of the requested
resources as possible, and attempt to revert to the original OS / DRC
state.'

So doing the dlpar_remove->dlapr_add in case of failure is in line with
the spec, but it should only be done as a last-resort. To me that
suggests that we should be attempting to offline all the LMBs
beforehand, and only after that's successful should we begin attempting
to release LMBs back to the platform. Should we consider introducing
that logic in the patchset? Or maybe as a follow-up?

> +               if (rc)
> +                       break;
> +
> +               lmbs[i].reserved = 1;
> +       }
> +
> +       if (rc) {
> +               pr_err("Memory indexed-count-remove failed, adding any removed LMBs\n");
> +
> +               for (i = start_index; i < end_index; i++) {
> +                       if (!lmbs[i].reserved)
> +                               continue;
> +
> +                       rc = dlpar_add_lmb(&lmbs[i]);
> +                       if (rc)
> +                               pr_err("Failed to add LMB, drc index %x\n",
> +                                      be32_to_cpu(lmbs[i].drc_index));
> +
> +                       lmbs[i].reserved = 0;
> +               }
> +               rc = -EINVAL;
> +       } else {
> +               for (i = start_index; i < end_index; i++) {
> +                       if (!lmbs[i].reserved)
> +                               continue;
> +
> +                       pr_info("Memory at %llx (drc index %x) was hot-removed\n",
> +                               lmbs[i].base_addr, lmbs[i].drc_index);
> +
> +                       lmbs[i].reserved = 0;
> +               }
> +       }
> +
> +       return rc;
> +}
> +
>  #else
>  static inline int pseries_remove_memblock(unsigned long base,
>                                           unsigned int memblock_size)
> @@ -853,6 +939,10 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
>                 } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) {
>                         drc_index = hp_elog->_drc_u.drc_index;
>                         rc = dlpar_memory_remove_by_index(drc_index, prop);
> +               } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) {
> +                       count = hp_elog->_drc_u.indexed_count[0];
> +                       drc_index = hp_elog->_drc_u.indexed_count[1];
> +                       rc = dlpar_memory_remove_by_ic(count, drc_index, prop);
>                 } else {
>                         rc = -EINVAL;
>                 }
>