On 10/09/2018 03:37 PM, Michael Bringmann wrote:
> migration/memory: This patch adds code that recognizes changes to
> the associativity of memory blocks described by the device-tree
> properties in order to drive equivalent 'hotplug' operations to
> update local and general kernel data structures to reflect those
> changes.  These differences may include:
> 
> * Evaluate 'ibm,dynamic-memory' properties when processing the
>   updated device-tree properties of the system during Post Migration
>   events (migration_store).  The new functionality looks for changes
>   to the aa_index values for each drc_index/LMB to identify any memory
>   blocks that should be readded.
> 
> * In an LPAR migration scenario, the "ibm,associativity-lookup-arrays"
>   property may change.  In the event that a row of the array differs,
>   locate all assigned memory blocks with that 'aa_index' and 're-add'
>   them to the system memory block data structures.  In the process of
>   the 're-add', the system routines will update the corresponding entry
>   for the memory in the LMB structures and any other relevant kernel
>   data structures.
> 
> A number of previous extensions made to the DRMEM code for scanning
> device-tree properties and creating LMB arrays are used here to
> ensure that the resulting code is simpler and more usable:
> 
> * Use new paired list iterator for the DRMEM LMB info arrays to find
>   differences in old and new versions of properties.
> * Use new iterator for copies of the DRMEM info arrays to evaluate
>   completely new structures.
> * Combine common code for parsing and evaluating memory description
>   properties based on the DRMEM LMB array model to greatly simplify
>   extension from the older property 'ibm,dynamic-memory' to the new
>   property model of 'ibm,dynamic-memory-v2'.
> 
> For support, add a new pseries hotplug action for DLPAR operations,
> PSERIES_HP_ELOG_ACTION_READD_MULTIPLE.  It is a variant of the READD
> operation which performs the action upon multiple instances of the
> resource at one time.  The operation is to be triggered by device-tree
> analysis of updates by RTAS events analyzed by 'migation_store' during
> post-migration processing.  It will be used for memory updates,
> initially.
> 
> Signed-off-by: Michael Bringmann <m...@linux.vnet.ibm.com>
> ---
> Changes in v04:
>   -- Move dlpar_memory_readd_multiple() function definition and use
>      into previous patch along with action constant definition.
>   -- Correct spacing in patch
> Changes in v03:
>   -- Modify the code that parses the memory affinity attributes to
>      mark relevant DRMEM LMB array entries using the internal_flags
>      mechanism instead of generate unique hotplug actions for each
>      memory block to be readded.  The change is intended to both
>      simplify the code, and to require fewer resources on systems
>      with huge amounts of memory.
>   -- Save up notice about any all LMB entries until the end of the
>      'migration_store' operation at which point a single action is
>      queued to scan the entire DRMEM array.
>   -- Add READD_MULTIPLE function for memory that scans the DRMEM
>      array to identify multiple entries that were marked previously.
>      The corresponding memory blocks are to be readded to the system
>      to update relevant data structures outside of the powerpc-
>      specific code.
>   -- Change dlpar_memory_pmt_changes_action to directly queue worker
>      to pseries work queue.
> ---
>  arch/powerpc/platforms/pseries/hotplug-memory.c |  189 
> +++++++++++++++++++----
>  arch/powerpc/platforms/pseries/mobility.c       |    4 
>  arch/powerpc/platforms/pseries/pseries.h        |    4 
>  3 files changed, 163 insertions(+), 34 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c 
> b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index bf2420a..a7ca22e 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -534,8 +534,11 @@ static int dlpar_memory_readd_by_index(u32 drc_index)
>               }
>       }
> 
> -     if (!lmb_found)
> -             rc = -EINVAL;
> +     if (!lmb_found) {
> +             pr_info("Failed to update memory for drc index %lx\n",
> +                     (unsigned long) drc_index);
> +             return -EINVAL;
> +     }
> 
>       if (rc)
>               pr_info("Failed to update memory at %llx\n",
> @@ -1002,13 +1005,43 @@ static int pseries_add_mem_node(struct device_node 
> *np)
>       return (ret < 0) ? -EINVAL : 0;
>  }
> 
> -static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
> +static int pmt_changes = 0;
> +
> +void dlpar_memory_pmt_changes_set(void)
> +{
> +     pmt_changes = 1;
> +}
> +
> +void dlpar_memory_pmt_changes_clear(void)
> +{
> +     pmt_changes = 0;
> +}
> +
> +int dlpar_memory_pmt_changes(void)
> +{
> +     return pmt_changes;
> +}
> +
> +void dlpar_memory_pmt_changes_action(void)
> +{
> +     if (dlpar_memory_pmt_changes()) {
> +             struct pseries_hp_errorlog hp_errlog;
> +
> +             hp_errlog.resource = PSERIES_HP_ELOG_RESOURCE_MEM;
> +             hp_errlog.action = PSERIES_HP_ELOG_ACTION_READD_MULTIPLE;
> +             hp_errlog.id_type = 0;
> +
> +             queue_hotplug_event(&hp_errlog, NULL, NULL);
> +
> +             dlpar_memory_pmt_changes_clear();
> +     }
> +}
> +
> +static int pseries_update_drconf_memory(struct drmem_lmb_info *new_dinfo)
>  {
> -     struct of_drconf_cell_v1 *new_drmem, *old_drmem;
> +     struct drmem_lmb *old_lmb, *new_lmb;
>       unsigned long memblock_size;
> -     u32 entries;
> -     __be32 *p;
> -     int i, rc = -EINVAL;
> +     int rc = 0;
> 
>       if (rtas_hp_event)
>               return 0;
> @@ -1017,42 +1050,122 @@ static int pseries_update_drconf_memory(struct 
> of_reconfig_data *pr)
>       if (!memblock_size)
>               return -EINVAL;
> 
> -     p = (__be32 *) pr->old_prop->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
> -      * entries. Get the number of entries and skip to the array of
> -      * of_drconf_cell's.
> -      */
> -     entries = be32_to_cpu(*p++);
> -     old_drmem = (struct of_drconf_cell_v1 *)p;
> +     /* Arrays should have the same size and DRC indexes */
> +     for_each_pair_dinfo_lmb(drmem_info, old_lmb, new_dinfo, new_lmb) {
> 
> -     p = (__be32 *)pr->prop->value;
> -     p++;
> -     new_drmem = (struct of_drconf_cell_v1 *)p;
> +             if (new_lmb->drc_index != old_lmb->drc_index)
> +                     continue;
> 
> -     for (i = 0; i < entries; i++) {
> -             if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) &&
> -                 (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) 
> {
> +             if ((old_lmb->flags & DRCONF_MEM_ASSIGNED) &&
> +                 (!(new_lmb->flags & DRCONF_MEM_ASSIGNED))) {
>                       rc = pseries_remove_memblock(
> -                             be64_to_cpu(old_drmem[i].base_addr),
> -                                                  memblock_size);
> +                             old_lmb->base_addr, 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)) {
> -                     rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr),
> -                                       memblock_size);
> +             } else if ((!(old_lmb->flags & DRCONF_MEM_ASSIGNED)) &&
> +                        (new_lmb->flags & DRCONF_MEM_ASSIGNED)) {
> +                     rc = memblock_add(old_lmb->base_addr,
> +                                     memblock_size);
>                       rc = (rc < 0) ? -EINVAL : 0;
>                       break;
> +             } else if ((old_lmb->aa_index != new_lmb->aa_index) &&
> +                        (new_lmb->flags & DRCONF_MEM_ASSIGNED)) {
> +                     drmem_mark_lmb_update(old_lmb);
> +                     dlpar_memory_pmt_changes_set();
>               }
>       }
>       return rc;
>  }
> 
> +static void pseries_update_ala_memory_aai(int aa_index)
> +{
> +     struct drmem_lmb *lmb;
> +
> +     /* Readd all LMBs which were previously using the
> +      * specified aa_index value.
> +      */
> +     for_each_drmem_lmb(lmb) {
> +             if ((lmb->aa_index == aa_index) &&
> +                     (lmb->flags & DRCONF_MEM_ASSIGNED)) {
> +                     drmem_mark_lmb_update(lmb);
> +                     dlpar_memory_pmt_changes_set();
> +             }
> +     }
> +}
> +
> +struct assoc_arrays {
> +     u32 n_arrays;
> +     u32 array_sz;
> +     const __be32 *arrays;
> +};

This struct is also defined in arch/powerpc/mm/numa.c. May be a good idea to 
move the
definition to common place.

> +
> +static int pseries_update_ala_memory(struct of_reconfig_data *pr)
> +{
> +     struct assoc_arrays new_ala, old_ala;
> +     __be32 *p;
> +     int i, lim;
> +
> +     if (rtas_hp_event)
> +             return 0;
> +
> +     /*
> +      * The layout of the ibm,associativity-lookup-arrays
> +      * property is a number N indicating the number of
> +      * associativity arrays, followed by a number M
> +      * indicating the size of each associativity array,
> +      * followed by a list of N associativity arrays.
> +      */
> +
> +     p = (__be32 *) pr->old_prop->value;
> +     if (!p)
> +             return -EINVAL;
> +     old_ala.n_arrays = of_read_number(p++, 1);
> +     old_ala.array_sz = of_read_number(p++, 1);
> +     old_ala.arrays = p;
> +
> +     p = (__be32 *) pr->prop->value;
> +     if (!p)
> +             return -EINVAL;
> +     new_ala.n_arrays = of_read_number(p++, 1);
> +     new_ala.array_sz = of_read_number(p++, 1);
> +     new_ala.arrays = p;
> +
> +     lim = (new_ala.n_arrays > old_ala.n_arrays) ? old_ala.n_arrays :
> +                     new_ala.n_arrays;
> +
> +     if (old_ala.array_sz == new_ala.array_sz) {
> +
> +             /* Reset any entries where the old and new rows
> +              * the array have changed.

Small nit, the wording in that comment could be clearer.

-Nathan

> +              */
> +             for (i = 0; i < lim; i++) {
> +                     int index = (i * new_ala.array_sz);
> +
> +                     if (!memcmp(&old_ala.arrays[index],
> +                             &new_ala.arrays[index],
> +                             new_ala.array_sz))
> +                             continue;
> +
> +                     pseries_update_ala_memory_aai(i);
> +             }
> +
> +             /* Reset any entries representing the extra rows.
> +              * There shouldn't be any, but just in case ...
> +              */
> +             for (i = lim; i < new_ala.n_arrays; i++)
> +                     pseries_update_ala_memory_aai(i);
> +
> +     } 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);
> +     }
> +
> +     return 0;
> +}
> +
>  static int pseries_memory_notifier(struct notifier_block *nb,
>                                  unsigned long action, void *data)
>  {
> @@ -1067,8 +1180,16 @@ static int pseries_memory_notifier(struct 
> notifier_block *nb,
>               err = pseries_remove_mem_node(rd->dn);
>               break;
>       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")) {
> +                     struct drmem_lmb_info *dinfo =
> +                             drmem_lmbs_init(rd->prop);
> +                     if (!dinfo)
> +                             return -EINVAL;
> +                     err = pseries_update_drconf_memory(dinfo);
> +                     drmem_lmbs_free(dinfo);
> +             } else if (!strcmp(rd->prop->name,
> +                             "ibm,associativity-lookup-arrays"))
> +                     err = pseries_update_ala_memory(rd);
>               break;
>       }
>       return notifier_from_errno(err);
> diff --git a/arch/powerpc/platforms/pseries/mobility.c 
> b/arch/powerpc/platforms/pseries/mobility.c
> index eba7ef7..c9d3d80 100644
> --- a/arch/powerpc/platforms/pseries/mobility.c
> +++ b/arch/powerpc/platforms/pseries/mobility.c
> @@ -373,6 +373,10 @@ static ssize_t migration_store(struct class *class,
>               return rc;
> 
>       post_mobility_fixup();
> +
> +     /* Apply any necessary changes identified during fixup */
> +     dlpar_memory_pmt_changes_action();
> +
>       return count;
>  }
> 
> diff --git a/arch/powerpc/platforms/pseries/pseries.h 
> b/arch/powerpc/platforms/pseries/pseries.h
> index 72c0b89..3352f90 100644
> --- a/arch/powerpc/platforms/pseries/pseries.h
> +++ b/arch/powerpc/platforms/pseries/pseries.h
> @@ -71,6 +71,10 @@ static inline int dlpar_memory(struct pseries_hp_errorlog 
> *hp_elog)
>       return -EOPNOTSUPP;
>  }
>  #endif
> +void dlpar_memory_pmt_changes_set(void);
> +void dlpar_memory_pmt_changes_clear(void);
> +int dlpar_memory_pmt_changes(void);
> +void dlpar_memory_pmt_changes_action(void);
> 
>  #ifdef CONFIG_HOTPLUG_CPU
>  int dlpar_cpu(struct pseries_hp_errorlog *hp_elog);
> 

Reply via email to