On Mon, Sep 14, 2020 at 02:51:15AM +0530, Vaibhav Jain wrote:
> Collection of performance statistics of an NVDIMM can be dynamically
> enabled/disabled from the Hypervisor Management Console even when the
> guest lpar is running. The current implementation however will check if
> the performance statistics collection is supported during NVDIMM probe
> and if yes will assume that to be the case afterwards.
> 
> Hence we update papr_scm to remove this assumption from the code by
> eliminating the 'stat_buffer_len' member from 'struct papr_scm_priv'
> that was used to cache the max buffer size needed to fetch NVDIMM
> performance stats from PHYP. With that struct member gone, various
> functions that depended on it are updated. Specifically
> perf_stats_show() is updated to query the PHYP first for the size of
> buffer needed to hold all performance statistics instead of relying on
> 'stat_buffer_len'
> 
> Signed-off-by: Vaibhav Jain <vaib...@linux.ibm.com>
> ---
>  arch/powerpc/platforms/pseries/papr_scm.c | 53 +++++++++++------------
>  1 file changed, 25 insertions(+), 28 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/papr_scm.c 
> b/arch/powerpc/platforms/pseries/papr_scm.c
> index 27268370dee00..6697e1c3b9ebe 100644
> --- a/arch/powerpc/platforms/pseries/papr_scm.c
> +++ b/arch/powerpc/platforms/pseries/papr_scm.c
> @@ -112,9 +112,6 @@ struct papr_scm_priv {
>  
>       /* Health information for the dimm */
>       u64 health_bitmap;
> -
> -     /* length of the stat buffer as expected by phyp */
> -     size_t stat_buffer_len;
>  };
>  
>  static LIST_HEAD(papr_nd_regions);
> @@ -230,14 +227,15 @@ static int drc_pmem_query_n_bind(struct papr_scm_priv 
> *p)
>   * - If buff_stats == NULL the return value is the size in byes of the buffer
>   * needed to hold all supported performance-statistics.
>   * - If buff_stats != NULL and num_stats == 0 then we copy all known
> - * performance-statistics to 'buff_stat' and expect to be large enough to
> - * hold them.
> + * performance-statistics to 'buff_stat' and expect it to be large enough to
> + * hold them. The 'buff_size' args contains the size of the 'buff_stats'
>   * - if buff_stats != NULL and num_stats > 0 then copy the requested
>   * performance-statistics to buff_stats.
>   */
>  static ssize_t drc_pmem_query_stats(struct papr_scm_priv *p,
>                                   struct papr_scm_perf_stats *buff_stats,
> -                                 unsigned int num_stats)
> +                                 unsigned int num_stats,
> +                                 size_t buff_size)
>  {
>       unsigned long ret[PLPAR_HCALL_BUFSIZE];
>       size_t size;
> @@ -261,12 +259,18 @@ static ssize_t drc_pmem_query_stats(struct 
> papr_scm_priv *p,
>                       size = sizeof(struct papr_scm_perf_stats) +
>                               num_stats * sizeof(struct papr_scm_perf_stat);
>               else
> -                     size = p->stat_buffer_len;
> +                     size = buff_size;
>       } else {
>               /* In case of no out buffer ignore the size */
>               size = 0;
>       }
>  
> +     /* verify that the buffer size needed is sufficient */
> +     if (size > buff_size) {
> +             __WARN();
> +             return -EINVAL;
> +     }
> +
>       /* Do the HCALL asking PHYP for info */
>       rc = plpar_hcall(H_SCM_PERFORMANCE_STATS, ret, p->drc_index,
>                        buff_stats ? virt_to_phys(buff_stats) : 0,
> @@ -277,6 +281,10 @@ static ssize_t drc_pmem_query_stats(struct papr_scm_priv 
> *p,
>               dev_err(&p->pdev->dev,
>                       "Unknown performance stats, Err:0x%016lX\n", ret[0]);
>               return -ENOENT;
> +     } else if (rc == H_AUTHORITY) {
> +             dev_dbg(&p->pdev->dev,
> +                     "Performance stats in-accessible\n");
> +             return -EPERM;
>       } else if (rc != H_SUCCESS) {
>               dev_err(&p->pdev->dev,
>                       "Failed to query performance stats, Err:%lld\n", rc);
> @@ -526,10 +534,6 @@ static int papr_pdsm_fuel_gauge(struct papr_scm_priv *p,
>       struct papr_scm_perf_stat *stat;
>       struct papr_scm_perf_stats *stats;
>  
> -     /* Silently fail if fetching performance metrics isn't  supported */
> -     if (!p->stat_buffer_len)
> -             return 0;
> -
>       /* Allocate request buffer enough to hold single performance stat */
>       size = sizeof(struct papr_scm_perf_stats) +
>               sizeof(struct papr_scm_perf_stat);
> @@ -543,9 +547,11 @@ static int papr_pdsm_fuel_gauge(struct papr_scm_priv *p,
>       stat->stat_val = 0;
>  
>       /* Fetch the fuel gauge and populate it in payload */
> -     rc = drc_pmem_query_stats(p, stats, 1);
> +     rc = drc_pmem_query_stats(p, stats, 1, size);
>       if (rc < 0) {
>               dev_dbg(&p->pdev->dev, "Err(%d) fetching fuel gauge\n", rc);
> +             /* Silently fail if unable to fetch performance metric */
> +             rc = 0;
>               goto free_stats;
>       }
>  
> @@ -786,23 +792,25 @@ static ssize_t perf_stats_show(struct device *dev,
>                              struct device_attribute *attr, char *buf)
>  {
>       int index;
> -     ssize_t rc;
> +     ssize_t rc, buff_len;
>       struct seq_buf s;
>       struct papr_scm_perf_stat *stat;
>       struct papr_scm_perf_stats *stats;
>       struct nvdimm *dimm = to_nvdimm(dev);
>       struct papr_scm_priv *p = nvdimm_provider_data(dimm);
>  
> -     if (!p->stat_buffer_len)
> -             return -ENOENT;
> +     /* fetch the length of buffer needed to get all stats */
> +     buff_len = drc_pmem_query_stats(p, NULL, 0, 0);
> +     if (buff_len <= 0)
> +             return buff_len;

Generally I can't find anything wrong with this patch technically but the
architecture of drc_pmem_query_stats() seems overly complicated.

IOW, I feel like you are overloading drc_pmem_query_stats() in an odd way which
makes it and the callers code confusing.  Why can't you have a separate
function which returns the max buffer length and separate out the logic within
drc_pmem_query_stats() to make it clear how to call plpar_hcall() to get this
information?

Ira

>  
>       /* Allocate the buffer for phyp where stats are written */
> -     stats = kzalloc(p->stat_buffer_len, GFP_KERNEL);
> +     stats = kzalloc(buff_len, GFP_KERNEL);
>       if (!stats)
>               return -ENOMEM;
>  
>       /* Ask phyp to return all dimm perf stats */
> -     rc = drc_pmem_query_stats(p, stats, 0);
> +     rc = drc_pmem_query_stats(p, stats, 0, buff_len);
>       if (rc)
>               goto free_stats;
>       /*
> @@ -891,7 +899,6 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
>       struct nd_region_desc ndr_desc;
>       unsigned long dimm_flags;
>       int target_nid, online_nid;
> -     ssize_t stat_size;
>  
>       p->bus_desc.ndctl = papr_scm_ndctl;
>       p->bus_desc.module = THIS_MODULE;
> @@ -962,16 +969,6 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
>       list_add_tail(&p->region_list, &papr_nd_regions);
>       mutex_unlock(&papr_ndr_lock);
>  
> -     /* Try retriving the stat buffer and see if its supported */
> -     stat_size = drc_pmem_query_stats(p, NULL, 0);
> -     if (stat_size > 0) {
> -             p->stat_buffer_len = stat_size;
> -             dev_dbg(&p->pdev->dev, "Max perf-stat size %lu-bytes\n",
> -                     p->stat_buffer_len);
> -     } else {
> -             dev_info(&p->pdev->dev, "Dimm performance stats unavailable\n");
> -     }
> -
>       return 0;
>  
>  err: nvdimm_bus_unregister(p->bus);
> -- 
> 2.26.2
> 

Reply via email to