Commit 75b7c05e authored by Shivaprasad G Bhat's avatar Shivaprasad G Bhat Committed by Michael Ellerman

powerpc/papr_scm: Implement support for H_SCM_FLUSH hcall

Add support for ND_REGION_ASYNC capability if the device tree
indicates 'ibm,hcall-flush-required' property in the NVDIMM node.
Flush is done by issuing H_SCM_FLUSH hcall to the hypervisor.

If the flush request failed, the hypervisor is expected to
to reflect the problem in the subsequent nvdimm H_SCM_HEALTH call.

This patch prevents mmap of namespaces with MAP_SYNC flag if the
nvdimm requires an explicit flush[1].

References:
[1] https://github.com/avocado-framework-tests/avocado-misc-tests/blob/master/memory/ndctl.py.data/map_sync.cSigned-off-by: default avatarShivaprasad G Bhat <sbhat@linux.ibm.com>
Reviewed-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
[mpe: Use unsigned long / long instead of uint64_t/int64_t]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/161703936121.36.7260632399582101498.stgit@e1fbed493c87
parent af072b1a
...@@ -275,6 +275,20 @@ Health Bitmap Flags: ...@@ -275,6 +275,20 @@ Health Bitmap Flags:
Given a DRC Index collect the performance statistics for NVDIMM and copy them Given a DRC Index collect the performance statistics for NVDIMM and copy them
to the resultBuffer. to the resultBuffer.
**H_SCM_FLUSH**
| Input: *drcIndex, continue-token*
| Out: *continue-token*
| Return Value: *H_SUCCESS, H_Parameter, H_P2, H_BUSY*
Given a DRC Index Flush the data to backend NVDIMM device.
The hcall returns H_BUSY when the flush takes longer time and the hcall needs
to be issued multiple times in order to be completely serviced. The
*continue-token* from the output to be passed in the argument list of
subsequent hcalls to the hypervisor until the hcall is completely serviced
at which point H_SUCCESS or other error is returned by the hypervisor.
References References
========== ==========
.. [1] "Power Architecture Platform Reference" .. [1] "Power Architecture Platform Reference"
......
...@@ -315,7 +315,8 @@ ...@@ -315,7 +315,8 @@
#define H_SCM_HEALTH 0x400 #define H_SCM_HEALTH 0x400
#define H_SCM_PERFORMANCE_STATS 0x418 #define H_SCM_PERFORMANCE_STATS 0x418
#define H_RPT_INVALIDATE 0x448 #define H_RPT_INVALIDATE 0x448
#define MAX_HCALL_OPCODE H_RPT_INVALIDATE #define H_SCM_FLUSH 0x44C
#define MAX_HCALL_OPCODE H_SCM_FLUSH
/* Scope args for H_SCM_UNBIND_ALL */ /* Scope args for H_SCM_UNBIND_ALL */
#define H_UNBIND_SCOPE_ALL (0x1) #define H_UNBIND_SCOPE_ALL (0x1)
......
...@@ -93,6 +93,7 @@ struct papr_scm_priv { ...@@ -93,6 +93,7 @@ struct papr_scm_priv {
uint64_t block_size; uint64_t block_size;
int metadata_size; int metadata_size;
bool is_volatile; bool is_volatile;
bool hcall_flush_required;
uint64_t bound_addr; uint64_t bound_addr;
...@@ -117,6 +118,38 @@ struct papr_scm_priv { ...@@ -117,6 +118,38 @@ struct papr_scm_priv {
size_t stat_buffer_len; size_t stat_buffer_len;
}; };
static int papr_scm_pmem_flush(struct nd_region *nd_region,
struct bio *bio __maybe_unused)
{
struct papr_scm_priv *p = nd_region_provider_data(nd_region);
unsigned long ret_buf[PLPAR_HCALL_BUFSIZE], token = 0;
long rc;
dev_dbg(&p->pdev->dev, "flush drc 0x%x", p->drc_index);
do {
rc = plpar_hcall(H_SCM_FLUSH, ret_buf, p->drc_index, token);
token = ret_buf[0];
/* Check if we are stalled for some time */
if (H_IS_LONG_BUSY(rc)) {
msleep(get_longbusy_msecs(rc));
rc = H_BUSY;
} else if (rc == H_BUSY) {
cond_resched();
}
} while (rc == H_BUSY);
if (rc) {
dev_err(&p->pdev->dev, "flush error: %lld", rc);
rc = -EIO;
} else {
dev_dbg(&p->pdev->dev, "flush drc 0x%x complete", p->drc_index);
}
return rc;
}
static LIST_HEAD(papr_nd_regions); static LIST_HEAD(papr_nd_regions);
static DEFINE_MUTEX(papr_ndr_lock); static DEFINE_MUTEX(papr_ndr_lock);
...@@ -943,6 +976,11 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) ...@@ -943,6 +976,11 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p)
ndr_desc.num_mappings = 1; ndr_desc.num_mappings = 1;
ndr_desc.nd_set = &p->nd_set; ndr_desc.nd_set = &p->nd_set;
if (p->hcall_flush_required) {
set_bit(ND_REGION_ASYNC, &ndr_desc.flags);
ndr_desc.flush = papr_scm_pmem_flush;
}
if (p->is_volatile) if (p->is_volatile)
p->region = nvdimm_volatile_region_create(p->bus, &ndr_desc); p->region = nvdimm_volatile_region_create(p->bus, &ndr_desc);
else { else {
...@@ -1088,6 +1126,7 @@ static int papr_scm_probe(struct platform_device *pdev) ...@@ -1088,6 +1126,7 @@ static int papr_scm_probe(struct platform_device *pdev)
p->block_size = block_size; p->block_size = block_size;
p->blocks = blocks; p->blocks = blocks;
p->is_volatile = !of_property_read_bool(dn, "ibm,cache-flush-required"); p->is_volatile = !of_property_read_bool(dn, "ibm,cache-flush-required");
p->hcall_flush_required = of_property_read_bool(dn, "ibm,hcall-flush-required");
/* We just need to ensure that set cookies are unique across */ /* We just need to ensure that set cookies are unique across */
uuid_parse(uuid_str, (uuid_t *) uuid); uuid_parse(uuid_str, (uuid_t *) uuid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment