[PATCH] powerpc/papr_scm: Support dynamic enable/disable of performance statistics
Ira Weiny
ira.weiny at intel.com
Tue Sep 29 02:04:43 AEST 2020
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 <vaibhav at 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
>
More information about the Linuxppc-dev
mailing list