@@ -9,6 +9,9 @@
#include "core.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/cxl.h>
+
static bool cxl_raw_allow_all;
/**
@@ -750,6 +753,7 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
{
/* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
struct cxl_mbox_identify id;
+ __le32 val = 0;
int rc;
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
@@ -769,6 +773,9 @@ int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
cxlds->lsa_size = le32_to_cpu(id.lsa_size);
memcpy(cxlds->firmware_version, id.fw_revision, sizeof(id.fw_revision));
+ memcpy(&val, id.poison_list_max_mer, 3);
+ cxlds->poison_max = min_t(u32, le32_to_cpu(val), CXL_POISON_LIST_MAX);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
@@ -833,6 +840,67 @@ int cxl_mem_create_range_info(struct cxl_dev_state *cxlds)
}
EXPORT_SYMBOL_NS_GPL(cxl_mem_create_range_info, CXL);
+int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
+ const char *region_name)
+{
+ struct cxl_dev_state *cxlds = cxlmd->cxlds;
+ struct cxl_mbox_poison_payload_out *po;
+ struct cxl_mbox_poison_payload_in pi;
+ int nr_records = 0;
+ int rc, i;
+
+ po = kvmalloc(cxlds->payload_size, GFP_KERNEL);
+ if (!po)
+ return -ENOMEM;
+
+ pi.offset = cpu_to_le64(offset);
+ pi.length = cpu_to_le64(len);
+
+ rc = mutex_lock_interruptible(&cxlds->poison_list_mutex);
+ if (rc)
+ goto out;
+
+ do {
+ u64 overflow_t = 0;
+
+ rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_POISON, &pi,
+ sizeof(pi), po, cxlds->payload_size);
+ if (rc)
+ break;
+
+ if (po->flags & CXL_POISON_FLAG_OVERFLOW)
+ overflow_t = le64_to_cpu(po->overflow_timestamp);
+
+ for (i = 0; i < le16_to_cpu(po->count); i++) {
+ u32 len = le32_to_cpu(po->record[i].length) *
+ CXL_POISON_LEN_MULT;
+ u64 addr = le64_to_cpu(po->record[i].address);
+ u8 source = addr & CXL_POISON_SOURCE_MASK;
+ u64 dpa = addr & CXL_POISON_START_MASK;
+ u64 hpa = 0;
+
+ trace_cxl_poison(current->pid, region_name,
+ dev_name(&cxlmd->dev),
+ dev_name(cxlds->dev), hpa, dpa, len,
+ source, po->flags, overflow_t);
+ }
+
+ /* Protect against an uncleared _FLAG_MORE */
+ nr_records = nr_records + le16_to_cpu(po->count);
+ if (nr_records >= cxlds->poison_max) {
+ dev_dbg(&cxlmd->dev, "Max Error Records reached: %d\n",
+ nr_records);
+ break;
+ }
+ } while (po->flags & CXL_POISON_FLAG_MORE);
+
+ mutex_unlock(&cxlds->poison_list_mutex);
+out:
+ kvfree(po);
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(cxl_mem_get_poison, CXL);
+
struct cxl_dev_state *cxl_dev_state_create(struct device *dev)
{
struct cxl_dev_state *cxlds;
@@ -844,6 +912,7 @@ struct cxl_dev_state *cxl_dev_state_create(struct device *dev)
}
mutex_init(&cxlds->mbox_mutex);
+ mutex_init(&cxlds->poison_list_mutex);
cxlds->dev = dev;
return cxlds;
@@ -192,6 +192,8 @@ struct cxl_endpoint_dvsec_info {
* (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register)
* @lsa_size: Size of Label Storage Area
* (CXL 2.0 8.2.9.5.1.1 Identify Memory Device)
+ * @poison_max: maximum media error records held in device cache
+ * @poison_list_mutex: Mutex to synchronize poison list retrieval
* @mbox_mutex: Mutex to synchronize mailbox access.
* @firmware_version: Firmware version for the memory device.
* @enabled_cmds: Hardware commands found enabled in CEL.
@@ -223,6 +225,8 @@ struct cxl_dev_state {
size_t payload_size;
size_t lsa_size;
+ u32 poison_max;
+ struct mutex poison_list_mutex; /* Protect reads of poison list */
struct mutex mbox_mutex; /* Protects device mailbox and firmware */
char firmware_version[0x10];
DECLARE_BITMAP(enabled_cmds, CXL_MEM_COMMAND_ID_MAX);
@@ -344,6 +348,42 @@ struct cxl_mbox_set_partition_info {
#define CXL_SET_PARTITION_IMMEDIATE_FLAG BIT(0)
+struct cxl_mbox_poison_payload_in {
+ __le64 offset;
+ __le64 length;
+} __packed;
+
+struct cxl_mbox_poison_payload_out {
+ u8 flags;
+ u8 rsvd1;
+ __le64 overflow_timestamp;
+ __le16 count;
+ u8 rsvd2[0x14];
+ struct cxl_poison_record {
+ __le64 address;
+ __le32 length;
+ __le32 rsvd;
+ } __packed record[];
+} __packed;
+
+/* CXL 8.2.9.5.4.1 Get Poison List payload out flags */
+#define CXL_POISON_FLAG_MORE BIT(0)
+#define CXL_POISON_FLAG_OVERFLOW BIT(1)
+#define CXL_POISON_FLAG_SCANNING BIT(2)
+
+/*
+ * CXL 8.2.9.5.4.1 Get Poison List address field encodes both the
+ * starting address of poison, and the source of the poison.
+ */
+#define CXL_POISON_START_MASK GENMASK_ULL(63, 6)
+#define CXL_POISON_SOURCE_MASK GENMASK(2, 0)
+
+/* CXL 8.2.9.5.4.1 Table 188: Length is in units of 64 bytes */
+#define CXL_POISON_LEN_MULT 64
+
+/* Kernel maximum for a cache of media poison errors */
+#define CXL_POISON_LIST_MAX 1024
+
/**
* struct cxl_mem_command - Driver representation of a memory device command
* @info: Command information as it exists for the UAPI
@@ -378,6 +418,8 @@ int cxl_mem_create_range_info(struct cxl_dev_state *cxlds);
struct cxl_dev_state *cxl_dev_state_create(struct device *dev);
void set_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
void clear_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
+int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
+ const char *region_name);
#ifdef CONFIG_CXL_SUSPEND
void cxl_mem_active_inc(void);
void cxl_mem_active_dec(void);