@@ -429,3 +429,25 @@ Description:
attribute is only visible for devices supporting the
capability. The retrieved errors are logged as kernel
trace events with the label 'cxl_poison'.
+
+
+What: /sys/bus/cxl/devices/memX/inject_poison
+Date: March, 2023
+KernelVersion: v6.4
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (WO) When a Device Physical Address (DPA) is written to this
+ attribute, the memdev driver sends an inject poison command to
+ the device for the specified address. The DPA must be 64-byte
+ aligned and the length of the injected poison is 64-bytes. If
+ successful, the device returns poison when the address is
+ accessed through the CXL.mem bus. 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.
+ It is not an error to inject poison into an address that
+ already has poison present and no error is returned. The
+ inject_poison attribute is only visible for devices supporting
+ the capability. Kconfig option CXL_POISON_INJECT must be on
+ to enable this option. The default is off.
@@ -139,4 +139,15 @@ config CXL_REGION_INVALIDATION_TEST
If unsure, or if this kernel is meant for production environments,
say N.
+config CXL_POISON_INJECT
+ bool "CXL: Support CXL Memory Device Poison Inject"
+ depends on CXL_MEM
+ help
+ Selecting this option creates the sysfs attributes inject_poison
+ and clear_poison for CXL memory devices supporting the capability.
+ This option is intended for debug scenarios only and is disabled
+ by default. See Documentation/ABI/testing/sysfs-bus-cxl.
+
+ If unsure, say N.
+
endif
@@ -168,6 +168,92 @@ static ssize_t trigger_poison_list_store(struct device *dev,
}
static DEVICE_ATTR_WO(trigger_poison_list);
+static int cxl_dpa_mapped(struct device *dev, void *data)
+{
+ struct cxl_endpoint_decoder *cxled;
+ u64 *dpa = data;
+
+ if (!is_endpoint_decoder(dev))
+ return 0;
+
+ cxled = to_cxl_endpoint_decoder(dev);
+ if (!cxled->dpa_res || !resource_size(cxled->dpa_res))
+ return 0;
+
+ if (*dpa <= cxled->dpa_res->end && *dpa >= cxled->dpa_res->start) {
+ dev_dbg(dev, "dpa:0x%llx mapped in region:%s\n",
+ *dpa, dev_name(&cxled->cxld.region->dev));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa)
+{
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ struct cxl_port *port;
+ int rc;
+
+ 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;
+ }
+ port = dev_get_drvdata(&cxlmd->dev);
+ if (port && is_cxl_endpoint(port) && port->commit_end != -1) {
+ rc = device_for_each_child(&port->dev, &dpa, cxl_dpa_mapped);
+ if (rc)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t inject_poison_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
+ struct cxl_mbox_inject_poison inject;
+ struct cxl_mbox_cmd mbox_cmd;
+ u64 dpa;
+ int rc;
+
+ rc = kstrtou64(buf, 0, &dpa);
+ if (rc)
+ return rc;
+
+ down_read(&cxl_dpa_rwsem);
+ rc = cxl_validate_poison_dpa(cxlmd, dpa);
+ if (rc) {
+ up_read(&cxl_dpa_rwsem);
+ return rc;
+ }
+
+ inject = (struct cxl_mbox_inject_poison) {
+ .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(cxlmd->cxlds, &mbox_cmd);
+
+ up_read(&cxl_dpa_rwsem);
+ return rc ? rc : len;
+}
+static DEVICE_ATTR_WO(inject_poison);
+
static struct attribute *cxl_memdev_attributes[] = {
&dev_attr_serial.attr,
&dev_attr_firmware_version.attr,
@@ -175,6 +261,7 @@ static struct attribute *cxl_memdev_attributes[] = {
&dev_attr_label_storage_size.attr,
&dev_attr_numa_node.attr,
&dev_attr_trigger_poison_list.attr,
+ &dev_attr_inject_poison.attr,
NULL,
};
@@ -201,6 +288,16 @@ static umode_t cxl_memdev_visible(struct kobject *kobj, struct attribute *a,
to_cxl_memdev(dev)->cxlds->enabled_cmds))
return 0;
}
+ if (a == &dev_attr_inject_poison.attr) {
+ struct device *dev = kobj_to_dev(kobj);
+
+ if (!IS_ENABLED(CONFIG_CXL_POISON_INJECT))
+ return 0;
+
+ if (!test_bit(CXL_MEM_COMMAND_ID_INJECT_POISON,
+ to_cxl_memdev(dev)->cxlds->enabled_cmds))
+ return 0;
+ }
return a->mode;
}
@@ -602,6 +602,11 @@ struct cxl_mbox_poison_payload_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