diff options
| author | Vaibhav Jain <[email protected]> | 2020-07-31 12:11:52 +0530 | 
|---|---|---|
| committer | Michael Ellerman <[email protected]> | 2020-07-31 22:55:27 +1000 | 
| commit | 2d02bf835e5731de632c8a13567905fa7c0da01c (patch) | |
| tree | dcf4f379102e0f71d3c4b18b5300fb64850a601b | |
| parent | d947fb4c965cdb7242f3f91124ea16079c49fa8b (diff) | |
powerpc/papr_scm: Fetch nvdimm performance stats from PHYP
Update papr_scm.c to query dimm performance statistics from PHYP via
H_SCM_PERFORMANCE_STATS hcall and export them to user-space as PAPR
specific NVDIMM attribute 'perf_stats' in sysfs. The patch also
provide a sysfs ABI documentation for the stats being reported and
their meanings.
During NVDIMM probe time in papr_scm_nvdimm_init() a special variant
of H_SCM_PERFORMANCE_STATS hcall is issued to check if collection of
performance statistics is supported or not. If successful then a PHYP
returns a maximum possible buffer length needed to read all
performance stats. This returned value is stored in a per-nvdimm
attribute 'stat_buffer_len'.
The layout of request buffer for reading NVDIMM performance stats from
PHYP is defined in 'struct papr_scm_perf_stats' and 'struct
papr_scm_perf_stat'. These structs are used in newly introduced
drc_pmem_query_stats() that issues the H_SCM_PERFORMANCE_STATS hcall.
The sysfs access function perf_stats_show() uses value
'stat_buffer_len' to allocate a buffer large enough to hold all
possible NVDIMM performance stats and passes it to
drc_pmem_query_stats() to populate. Finally statistics reported in the
buffer are formatted into the sysfs access function output buffer.
Signed-off-by: Vaibhav Jain <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
| -rw-r--r-- | Documentation/ABI/testing/sysfs-bus-papr-pmem | 27 | ||||
| -rw-r--r-- | arch/powerpc/platforms/pseries/papr_scm.c | 150 | 
2 files changed, 177 insertions, 0 deletions
| diff --git a/Documentation/ABI/testing/sysfs-bus-papr-pmem b/Documentation/ABI/testing/sysfs-bus-papr-pmem index 5b10d036a8d4..c1a67275c43f 100644 --- a/Documentation/ABI/testing/sysfs-bus-papr-pmem +++ b/Documentation/ABI/testing/sysfs-bus-papr-pmem @@ -25,3 +25,30 @@ Description:  				  NVDIMM have been scrubbed.  		* "locked"	: Indicating that NVDIMM contents cant  				  be modified until next power cycle. + +What:		/sys/bus/nd/devices/nmemX/papr/perf_stats +Date:		May, 2020 +KernelVersion:	v5.9 +Contact:	linuxppc-dev <[email protected]>, [email protected], +Description: +		(RO) Report various performance stats related to papr-scm NVDIMM +		device.  Each stat is reported on a new line with each line +		composed of a stat-identifier followed by it value. Below are +		currently known dimm performance stats which are reported: + +		* "CtlResCt" : Controller Reset Count +		* "CtlResTm" : Controller Reset Elapsed Time +		* "PonSecs " : Power-on Seconds +		* "MemLife " : Life Remaining +		* "CritRscU" : Critical Resource Utilization +		* "HostLCnt" : Host Load Count +		* "HostSCnt" : Host Store Count +		* "HostSDur" : Host Store Duration +		* "HostLDur" : Host Load Duration +		* "MedRCnt " : Media Read Count +		* "MedWCnt " : Media Write Count +		* "MedRDur " : Media Read Duration +		* "MedWDur " : Media Write Duration +		* "CchRHCnt" : Cache Read Hit Count +		* "CchWHCnt" : Cache Write Hit Count +		* "FastWCnt" : Fast Write Count
\ No newline at end of file diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 3d1235a76ba9..f37f3f70007d 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -64,6 +64,26 @@  				    PAPR_PMEM_HEALTH_FATAL |	\  				    PAPR_PMEM_HEALTH_UNHEALTHY) +#define PAPR_SCM_PERF_STATS_EYECATCHER __stringify(SCMSTATS) +#define PAPR_SCM_PERF_STATS_VERSION 0x1 + +/* Struct holding a single performance metric */ +struct papr_scm_perf_stat { +	u8 stat_id[8]; +	__be64 stat_val; +} __packed; + +/* Struct exchanged between kernel and PHYP for fetching drc perf stats */ +struct papr_scm_perf_stats { +	u8 eye_catcher[8]; +	/* Should be PAPR_SCM_PERF_STATS_VERSION */ +	__be32 stats_version; +	/* Number of stats following */ +	__be32 num_statistics; +	/* zero or more performance matrics */ +	struct papr_scm_perf_stat scm_statistic[]; +} __packed; +  /* private struct associated with each region */  struct papr_scm_priv {  	struct platform_device *pdev; @@ -92,6 +112,9 @@ 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); @@ -201,6 +224,79 @@ err_out:  }  /* + * Query the Dimm performance stats from PHYP and copy them (if returned) to + * provided struct papr_scm_perf_stats instance 'stats' that can hold atleast + * (num_stats + header) bytes. + * - 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. + * - 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 long ret[PLPAR_HCALL_BUFSIZE]; +	size_t size; +	s64 rc; + +	/* Setup the out buffer */ +	if (buff_stats) { +		memcpy(buff_stats->eye_catcher, +		       PAPR_SCM_PERF_STATS_EYECATCHER, 8); +		buff_stats->stats_version = +			cpu_to_be32(PAPR_SCM_PERF_STATS_VERSION); +		buff_stats->num_statistics = +			cpu_to_be32(num_stats); + +		/* +		 * Calculate the buffer size based on num-stats provided +		 * or use the prefetched max buffer length +		 */ +		if (num_stats) +			/* Calculate size from the num_stats */ +			size = sizeof(struct papr_scm_perf_stats) + +				num_stats * sizeof(struct papr_scm_perf_stat); +		else +			size = p->stat_buffer_len; +	} else { +		/* In case of no out buffer ignore the size */ +		size = 0; +	} + +	/* 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, +			 size); + +	/* Check if the error was due to an unknown stat-id */ +	if (rc == H_PARTIAL) { +		dev_err(&p->pdev->dev, +			"Unknown performance stats, Err:0x%016lX\n", ret[0]); +		return -ENOENT; +	} else if (rc != H_SUCCESS) { +		dev_err(&p->pdev->dev, +			"Failed to query performance stats, Err:%lld\n", rc); +		return -EIO; + +	} else if (!size) { +		/* Handle case where stat buffer size was requested */ +		dev_dbg(&p->pdev->dev, +			"Performance stats size %ld\n", ret[0]); +		return ret[0]; +	} + +	/* Successfully fetched the requested stats from phyp */ +	dev_dbg(&p->pdev->dev, +		"Performance stats returned %d stats\n", +		be32_to_cpu(buff_stats->num_statistics)); +	return 0; +} + +/*   * Issue hcall to retrieve dimm health info and populate papr_scm_priv with the   * health information.   */ @@ -637,6 +733,48 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc,  	return 0;  } +static ssize_t perf_stats_show(struct device *dev, +			       struct device_attribute *attr, char *buf) +{ +	int index, rc; +	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; + +	/* Allocate the buffer for phyp where stats are written */ +	stats = kzalloc(p->stat_buffer_len, GFP_KERNEL); +	if (!stats) +		return -ENOMEM; + +	/* Ask phyp to return all dimm perf stats */ +	rc = drc_pmem_query_stats(p, stats, 0); +	if (rc) +		goto free_stats; +	/* +	 * Go through the returned output buffer and print stats and +	 * values. Since stat_id is essentially a char string of +	 * 8 bytes, simply use the string format specifier to print it. +	 */ +	seq_buf_init(&s, buf, PAGE_SIZE); +	for (index = 0, stat = stats->scm_statistic; +	     index < be32_to_cpu(stats->num_statistics); +	     ++index, ++stat) { +		seq_buf_printf(&s, "%.8s = 0x%016llX\n", +			       stat->stat_id, +			       be64_to_cpu(stat->stat_val)); +	} + +free_stats: +	kfree(stats); +	return rc ? rc : seq_buf_used(&s); +} +DEVICE_ATTR_RO(perf_stats); +  static ssize_t flags_show(struct device *dev,  			  struct device_attribute *attr, char *buf)  { @@ -682,6 +820,7 @@ DEVICE_ATTR_RO(flags);  /* papr_scm specific dimm attributes */  static struct attribute *papr_nd_attributes[] = {  	&dev_attr_flags.attr, +	&dev_attr_perf_stats.attr,  	NULL,  }; @@ -702,6 +841,7 @@ 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; @@ -769,6 +909,16 @@ 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); |