Message ID | 241c64115e6bd2effed9c7a20b08b3908dd7be8f.1681874357.git.alison.schofield@intel.com |
---|---|
State | Accepted |
Commit | d2fbc48658022f48625064ae192baff52057987d |
Headers | show |
Series | cxl: CXL Inject & Clear Poison | expand |
alison.schofield@ wrote: > From: Alison Schofield <alison.schofield@intel.com> > > CXL devices optionally support the INJECT POISON mailbox command. Add > memdev driver support for the mailbox command. > > Per the CXL Specification (3.0 8.2.9.8.4.2), after receiving a valid > inject poison request, the device will return poison when the address > is accessed through the CXL.mem driver. Injecting poison adds the address > to the device's Poison List and the error source is set to Injected. > In addition, the device adds a poison creation event to its internal > Informational Event log, updates the Event Status register, and if > configured, interrupts the host. > > Also, per the CXL Specification, it is not an error to inject poison > into an address that already has poison present and no error is > returned from the device. > > If the address is not contained in the device's dpa resource, or is > not 64 byte aligned, return -EINVAL without issuing the mbox command. > > Poison injection is intended for debug only and will be exposed to > userspace through debugfs. Restrict compilation to CONFIG_DEBUG_FS. > > Signed-off-by: Alison Schofield <alison.schofield@intel.com> > Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> > Reviewed-by: Dave Jiang <dave.jiang@intel.com> > --- > drivers/cxl/core/memdev.c | 56 +++++++++++++++++++++++++++++++++++++++ > drivers/cxl/cxlmem.h | 6 +++++ > 2 files changed, 62 insertions(+) > > diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c > index 8db7f220f182..474f7c2f4f6e 100644 > --- a/drivers/cxl/core/memdev.c > +++ b/drivers/cxl/core/memdev.c > @@ -160,6 +160,62 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd) > } > EXPORT_SYMBOL_NS_GPL(cxl_trigger_poison_list, CXL); > > +static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa) > +{ > + struct cxl_dev_state *cxlds = cxlmd->cxlds; > + > + if (!IS_ENABLED(CONFIG_DEBUG_FS)) > + return 0; This does no harm, but it looks unnecessary given that it is only called by debugfs entry points which already compile away in the CONFIG_DEBUG_FS=n case. It also takes no action, so why block it if someone besides a debugfs entry point wants to use it? I'll leave it for now. > + > + if (!resource_size(&cxlds->dpa_res)) { > + dev_dbg(cxlds->dev, "device has no dpa resource\n"); > + return -EINVAL; > + } > + if (dpa < cxlds->dpa_res.start || dpa > cxlds->dpa_res.end) { > + dev_dbg(cxlds->dev, "dpa:0x%llx not in resource:%pR\n", > + dpa, &cxlds->dpa_res); > + return -EINVAL; > + } > + if (!IS_ALIGNED(dpa, 64)) { > + dev_dbg(cxlds->dev, "dpa:0x%llx is not 64-byte aligned\n", dpa); > + return -EINVAL; > + } > + > + return 0; > +} > + > +int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) > +{ > + struct cxl_dev_state *cxlds = cxlmd->cxlds; > + struct cxl_mbox_inject_poison inject; > + struct cxl_mbox_cmd mbox_cmd; > + int rc; > + > + if (!IS_ENABLED(CONFIG_DEBUG_FS)) > + return 0; > + > + rc = down_read_interruptible(&cxl_dpa_rwsem); > + if (rc) > + return rc; > + > + rc = cxl_validate_poison_dpa(cxlmd, dpa); > + if (rc) > + goto out; > + > + inject.address = cpu_to_le64(dpa); > + mbox_cmd = (struct cxl_mbox_cmd) { > + .opcode = CXL_MBOX_OP_INJECT_POISON, > + .size_in = sizeof(inject), > + .payload_in = &inject, > + }; > + rc = cxl_internal_send_cmd(cxlds, &mbox_cmd); > +out: > + up_read(&cxl_dpa_rwsem); > + > + return rc; > +} > +EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, CXL); > + > static struct attribute *cxl_memdev_attributes[] = { > &dev_attr_serial.attr, > &dev_attr_firmware_version.attr, > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h > index 68b9db545aae..8a3ea9cabf1f 100644 > --- a/drivers/cxl/cxlmem.h > +++ b/drivers/cxl/cxlmem.h > @@ -615,6 +615,11 @@ struct cxl_mbox_poison_out { > #define CXL_POISON_SOURCE_INJECTED 3 > #define CXL_POISON_SOURCE_VENDOR 7 > > +/* Inject & Clear Poison CXL 3.0 Spec 8.2.9.8.4.2/3 */ > +struct cxl_mbox_inject_poison { > + __le64 address; > +}; > + > /** > * struct cxl_mem_command - Driver representation of a memory device command > * @info: Command information as it exists for the UAPI > @@ -689,6 +694,7 @@ int cxl_poison_state_init(struct cxl_dev_state *cxlds); > int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, > struct cxl_region *cxlr); > int cxl_trigger_poison_list(struct cxl_memdev *cxlmd); > +int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa); > > #ifdef CONFIG_CXL_SUSPEND > void cxl_mem_active_inc(void); > -- > 2.37.3 >
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 8db7f220f182..474f7c2f4f6e 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -160,6 +160,62 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd) } EXPORT_SYMBOL_NS_GPL(cxl_trigger_poison_list, CXL); +static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa) +{ + struct cxl_dev_state *cxlds = cxlmd->cxlds; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + if (!resource_size(&cxlds->dpa_res)) { + dev_dbg(cxlds->dev, "device has no dpa resource\n"); + return -EINVAL; + } + if (dpa < cxlds->dpa_res.start || dpa > cxlds->dpa_res.end) { + dev_dbg(cxlds->dev, "dpa:0x%llx not in resource:%pR\n", + dpa, &cxlds->dpa_res); + return -EINVAL; + } + if (!IS_ALIGNED(dpa, 64)) { + dev_dbg(cxlds->dev, "dpa:0x%llx is not 64-byte aligned\n", dpa); + return -EINVAL; + } + + return 0; +} + +int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa) +{ + struct cxl_dev_state *cxlds = cxlmd->cxlds; + struct cxl_mbox_inject_poison inject; + struct cxl_mbox_cmd mbox_cmd; + int rc; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + rc = down_read_interruptible(&cxl_dpa_rwsem); + if (rc) + return rc; + + rc = cxl_validate_poison_dpa(cxlmd, dpa); + if (rc) + goto out; + + inject.address = cpu_to_le64(dpa); + mbox_cmd = (struct cxl_mbox_cmd) { + .opcode = CXL_MBOX_OP_INJECT_POISON, + .size_in = sizeof(inject), + .payload_in = &inject, + }; + rc = cxl_internal_send_cmd(cxlds, &mbox_cmd); +out: + up_read(&cxl_dpa_rwsem); + + return rc; +} +EXPORT_SYMBOL_NS_GPL(cxl_inject_poison, CXL); + static struct attribute *cxl_memdev_attributes[] = { &dev_attr_serial.attr, &dev_attr_firmware_version.attr, diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 68b9db545aae..8a3ea9cabf1f 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -615,6 +615,11 @@ struct cxl_mbox_poison_out { #define CXL_POISON_SOURCE_INJECTED 3 #define CXL_POISON_SOURCE_VENDOR 7 +/* Inject & Clear Poison CXL 3.0 Spec 8.2.9.8.4.2/3 */ +struct cxl_mbox_inject_poison { + __le64 address; +}; + /** * struct cxl_mem_command - Driver representation of a memory device command * @info: Command information as it exists for the UAPI @@ -689,6 +694,7 @@ int cxl_poison_state_init(struct cxl_dev_state *cxlds); int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, struct cxl_region *cxlr); int cxl_trigger_poison_list(struct cxl_memdev *cxlmd); +int cxl_inject_poison(struct cxl_memdev *cxlmd, u64 dpa); #ifdef CONFIG_CXL_SUSPEND void cxl_mem_active_inc(void);