2025-05-21 13:47:41 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/*
|
|
|
|
* CXL EDAC memory feature driver.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2024-2025 HiSilicon Limited.
|
|
|
|
*
|
|
|
|
* - Supports functions to configure EDAC features of the
|
|
|
|
* CXL memory devices.
|
|
|
|
* - Registers with the EDAC device subsystem driver to expose
|
|
|
|
* the features sysfs attributes to the user for configuring
|
|
|
|
* CXL memory RAS feature.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/cleanup.h>
|
|
|
|
#include <linux/edac.h>
|
|
|
|
#include <linux/limits.h>
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
#include <linux/unaligned.h>
|
2025-05-21 13:47:44 +01:00
|
|
|
#include <linux/xarray.h>
|
2025-05-21 13:47:41 +01:00
|
|
|
#include <cxl/features.h>
|
|
|
|
#include <cxl.h>
|
|
|
|
#include <cxlmem.h>
|
|
|
|
#include "core.h"
|
2025-05-21 13:47:44 +01:00
|
|
|
#include "trace.h"
|
2025-05-21 13:47:41 +01:00
|
|
|
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
#define CXL_NR_EDAC_DEV_FEATURES 7
|
2025-05-21 13:47:41 +01:00
|
|
|
|
|
|
|
#define CXL_SCRUB_NO_REGION -1
|
|
|
|
|
|
|
|
struct cxl_patrol_scrub_context {
|
|
|
|
u8 instance;
|
|
|
|
u16 get_feat_size;
|
|
|
|
u16 set_feat_size;
|
|
|
|
u8 get_version;
|
|
|
|
u8 set_version;
|
|
|
|
u16 effects;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
struct cxl_region *cxlr;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.9.11.1 Table 8-222 Device Patrol Scrub Control
|
|
|
|
* Feature Readable Attributes.
|
|
|
|
*/
|
|
|
|
struct cxl_scrub_rd_attrbs {
|
|
|
|
u8 scrub_cycle_cap;
|
|
|
|
__le16 scrub_cycle_hours;
|
|
|
|
u8 scrub_flags;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.9.11.1 Table 8-223 Device Patrol Scrub Control
|
|
|
|
* Feature Writable Attributes.
|
|
|
|
*/
|
|
|
|
struct cxl_scrub_wr_attrbs {
|
|
|
|
u8 scrub_cycle_hours;
|
|
|
|
u8 scrub_flags;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
#define CXL_SCRUB_CONTROL_CHANGEABLE BIT(0)
|
|
|
|
#define CXL_SCRUB_CONTROL_REALTIME BIT(1)
|
|
|
|
#define CXL_SCRUB_CONTROL_CYCLE_MASK GENMASK(7, 0)
|
|
|
|
#define CXL_SCRUB_CONTROL_MIN_CYCLE_MASK GENMASK(15, 8)
|
|
|
|
#define CXL_SCRUB_CONTROL_ENABLE BIT(0)
|
|
|
|
|
|
|
|
#define CXL_GET_SCRUB_CYCLE_CHANGEABLE(cap) \
|
|
|
|
FIELD_GET(CXL_SCRUB_CONTROL_CHANGEABLE, cap)
|
|
|
|
#define CXL_GET_SCRUB_CYCLE(cycle) \
|
|
|
|
FIELD_GET(CXL_SCRUB_CONTROL_CYCLE_MASK, cycle)
|
|
|
|
#define CXL_GET_SCRUB_MIN_CYCLE(cycle) \
|
|
|
|
FIELD_GET(CXL_SCRUB_CONTROL_MIN_CYCLE_MASK, cycle)
|
|
|
|
#define CXL_GET_SCRUB_EN_STS(flags) FIELD_GET(CXL_SCRUB_CONTROL_ENABLE, flags)
|
|
|
|
|
|
|
|
#define CXL_SET_SCRUB_CYCLE(cycle) \
|
|
|
|
FIELD_PREP(CXL_SCRUB_CONTROL_CYCLE_MASK, cycle)
|
|
|
|
#define CXL_SET_SCRUB_EN(en) FIELD_PREP(CXL_SCRUB_CONTROL_ENABLE, en)
|
|
|
|
|
|
|
|
static int cxl_mem_scrub_get_attrbs(struct cxl_mailbox *cxl_mbox, u8 *cap,
|
|
|
|
u16 *cycle, u8 *flags, u8 *min_cycle)
|
|
|
|
{
|
|
|
|
size_t rd_data_size = sizeof(struct cxl_scrub_rd_attrbs);
|
|
|
|
size_t data_size;
|
|
|
|
struct cxl_scrub_rd_attrbs *rd_attrbs __free(kfree) =
|
|
|
|
kzalloc(rd_data_size, GFP_KERNEL);
|
|
|
|
if (!rd_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data_size = cxl_get_feature(cxl_mbox, &CXL_FEAT_PATROL_SCRUB_UUID,
|
|
|
|
CXL_GET_FEAT_SEL_CURRENT_VALUE, rd_attrbs,
|
|
|
|
rd_data_size, 0, NULL);
|
|
|
|
if (!data_size)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
*cap = rd_attrbs->scrub_cycle_cap;
|
|
|
|
*cycle = le16_to_cpu(rd_attrbs->scrub_cycle_hours);
|
|
|
|
*flags = rd_attrbs->scrub_flags;
|
|
|
|
if (min_cycle)
|
|
|
|
*min_cycle = CXL_GET_SCRUB_MIN_CYCLE(*cycle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_scrub_get_attrbs(struct cxl_patrol_scrub_context *cxl_ps_ctx,
|
|
|
|
u8 *cap, u16 *cycle, u8 *flags, u8 *min_cycle)
|
|
|
|
{
|
|
|
|
struct cxl_mailbox *cxl_mbox;
|
|
|
|
struct cxl_region_params *p;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
struct cxl_region *cxlr;
|
2025-06-03 18:43:13 +08:00
|
|
|
u8 min_scrub_cycle = 0;
|
2025-05-21 13:47:41 +01:00
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
if (!cxl_ps_ctx->cxlr) {
|
|
|
|
cxl_mbox = &cxl_ps_ctx->cxlmd->cxlds->cxl_mbox;
|
|
|
|
return cxl_mem_scrub_get_attrbs(cxl_mbox, cap, cycle,
|
|
|
|
flags, min_cycle);
|
|
|
|
}
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
|
|
|
|
return ret;
|
2025-05-21 13:47:41 +01:00
|
|
|
|
|
|
|
cxlr = cxl_ps_ctx->cxlr;
|
|
|
|
p = &cxlr->params;
|
|
|
|
|
|
|
|
for (i = 0; i < p->nr_targets; i++) {
|
|
|
|
struct cxl_endpoint_decoder *cxled = p->targets[i];
|
|
|
|
|
|
|
|
cxlmd = cxled_to_memdev(cxled);
|
|
|
|
cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
ret = cxl_mem_scrub_get_attrbs(cxl_mbox, cap, cycle, flags,
|
|
|
|
min_cycle);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2025-06-03 18:43:13 +08:00
|
|
|
/*
|
|
|
|
* The min_scrub_cycle of a region is the max of minimum scrub
|
|
|
|
* cycles supported by memdevs that back the region.
|
|
|
|
*/
|
2025-05-21 13:47:41 +01:00
|
|
|
if (min_cycle)
|
2025-06-03 18:43:13 +08:00
|
|
|
min_scrub_cycle = max(*min_cycle, min_scrub_cycle);
|
2025-05-21 13:47:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (min_cycle)
|
|
|
|
*min_cycle = min_scrub_cycle;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_scrub_set_attrbs_region(struct device *dev,
|
|
|
|
struct cxl_patrol_scrub_context *cxl_ps_ctx,
|
|
|
|
u8 cycle, u8 flags)
|
|
|
|
{
|
|
|
|
struct cxl_scrub_wr_attrbs wr_attrbs;
|
|
|
|
struct cxl_mailbox *cxl_mbox;
|
|
|
|
struct cxl_region_params *p;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
struct cxl_region *cxlr;
|
|
|
|
int ret, i;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
|
|
|
|
return ret;
|
2025-05-21 13:47:41 +01:00
|
|
|
|
|
|
|
cxlr = cxl_ps_ctx->cxlr;
|
|
|
|
p = &cxlr->params;
|
|
|
|
wr_attrbs.scrub_cycle_hours = cycle;
|
|
|
|
wr_attrbs.scrub_flags = flags;
|
|
|
|
|
|
|
|
for (i = 0; i < p->nr_targets; i++) {
|
|
|
|
struct cxl_endpoint_decoder *cxled = p->targets[i];
|
|
|
|
|
|
|
|
cxlmd = cxled_to_memdev(cxled);
|
|
|
|
cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
ret = cxl_set_feature(cxl_mbox, &CXL_FEAT_PATROL_SCRUB_UUID,
|
|
|
|
cxl_ps_ctx->set_version, &wr_attrbs,
|
|
|
|
sizeof(wr_attrbs),
|
|
|
|
CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET,
|
|
|
|
0, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (cycle != cxlmd->scrub_cycle) {
|
|
|
|
if (cxlmd->scrub_region_id != CXL_SCRUB_NO_REGION)
|
|
|
|
dev_info(dev,
|
|
|
|
"Device scrub rate(%d hours) set by region%d rate overwritten by region%d scrub rate(%d hours)\n",
|
|
|
|
cxlmd->scrub_cycle,
|
|
|
|
cxlmd->scrub_region_id, cxlr->id,
|
|
|
|
cycle);
|
|
|
|
|
|
|
|
cxlmd->scrub_cycle = cycle;
|
|
|
|
cxlmd->scrub_region_id = cxlr->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_scrub_set_attrbs_device(struct device *dev,
|
|
|
|
struct cxl_patrol_scrub_context *cxl_ps_ctx,
|
|
|
|
u8 cycle, u8 flags)
|
|
|
|
{
|
|
|
|
struct cxl_scrub_wr_attrbs wr_attrbs;
|
|
|
|
struct cxl_mailbox *cxl_mbox;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
wr_attrbs.scrub_cycle_hours = cycle;
|
|
|
|
wr_attrbs.scrub_flags = flags;
|
|
|
|
|
|
|
|
cxlmd = cxl_ps_ctx->cxlmd;
|
|
|
|
cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
ret = cxl_set_feature(cxl_mbox, &CXL_FEAT_PATROL_SCRUB_UUID,
|
|
|
|
cxl_ps_ctx->set_version, &wr_attrbs,
|
|
|
|
sizeof(wr_attrbs),
|
|
|
|
CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET, 0,
|
|
|
|
NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (cycle != cxlmd->scrub_cycle) {
|
|
|
|
if (cxlmd->scrub_region_id != CXL_SCRUB_NO_REGION)
|
|
|
|
dev_info(dev,
|
|
|
|
"Device scrub rate(%d hours) set by region%d rate overwritten with device local scrub rate(%d hours)\n",
|
|
|
|
cxlmd->scrub_cycle, cxlmd->scrub_region_id,
|
|
|
|
cycle);
|
|
|
|
|
|
|
|
cxlmd->scrub_cycle = cycle;
|
|
|
|
cxlmd->scrub_region_id = CXL_SCRUB_NO_REGION;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_scrub_set_attrbs(struct device *dev,
|
|
|
|
struct cxl_patrol_scrub_context *cxl_ps_ctx,
|
|
|
|
u8 cycle, u8 flags)
|
|
|
|
{
|
|
|
|
if (cxl_ps_ctx->cxlr)
|
|
|
|
return cxl_scrub_set_attrbs_region(dev, cxl_ps_ctx, cycle, flags);
|
|
|
|
|
|
|
|
return cxl_scrub_set_attrbs_device(dev, cxl_ps_ctx, cycle, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_get_enabled_bg(struct device *dev, void *drv_data,
|
|
|
|
bool *enabled)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *ctx = drv_data;
|
|
|
|
u8 cap, flags;
|
|
|
|
u16 cycle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cxl_scrub_get_attrbs(ctx, &cap, &cycle, &flags, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*enabled = CXL_GET_SCRUB_EN_STS(flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_set_enabled_bg(struct device *dev, void *drv_data,
|
|
|
|
bool enable)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *ctx = drv_data;
|
|
|
|
u8 cap, flags, wr_cycle;
|
|
|
|
u16 rd_cycle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_RAWIO))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
ret = cxl_scrub_get_attrbs(ctx, &cap, &rd_cycle, &flags, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
wr_cycle = CXL_GET_SCRUB_CYCLE(rd_cycle);
|
|
|
|
flags = CXL_SET_SCRUB_EN(enable);
|
|
|
|
|
|
|
|
return cxl_scrub_set_attrbs(dev, ctx, wr_cycle, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_get_min_scrub_cycle(struct device *dev,
|
|
|
|
void *drv_data, u32 *min)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *ctx = drv_data;
|
|
|
|
u8 cap, flags, min_cycle;
|
|
|
|
u16 cycle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cxl_scrub_get_attrbs(ctx, &cap, &cycle, &flags, &min_cycle);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*min = min_cycle * 3600;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_get_max_scrub_cycle(struct device *dev,
|
|
|
|
void *drv_data, u32 *max)
|
|
|
|
{
|
|
|
|
*max = U8_MAX * 3600; /* Max set by register size */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_get_scrub_cycle(struct device *dev, void *drv_data,
|
|
|
|
u32 *scrub_cycle_secs)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *ctx = drv_data;
|
|
|
|
u8 cap, flags;
|
|
|
|
u16 cycle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cxl_scrub_get_attrbs(ctx, &cap, &cycle, &flags, NULL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*scrub_cycle_secs = CXL_GET_SCRUB_CYCLE(cycle) * 3600;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_patrol_scrub_set_scrub_cycle(struct device *dev, void *drv_data,
|
|
|
|
u32 scrub_cycle_secs)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *ctx = drv_data;
|
|
|
|
u8 scrub_cycle_hours = scrub_cycle_secs / 3600;
|
|
|
|
u8 cap, wr_cycle, flags, min_cycle;
|
|
|
|
u16 rd_cycle;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_RAWIO))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
ret = cxl_scrub_get_attrbs(ctx, &cap, &rd_cycle, &flags, &min_cycle);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!CXL_GET_SCRUB_CYCLE_CHANGEABLE(cap))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (scrub_cycle_hours < min_cycle) {
|
|
|
|
dev_dbg(dev, "Invalid CXL patrol scrub cycle(%d) to set\n",
|
|
|
|
scrub_cycle_hours);
|
|
|
|
dev_dbg(dev,
|
|
|
|
"Minimum supported CXL patrol scrub cycle in hour %d\n",
|
|
|
|
min_cycle);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
wr_cycle = CXL_SET_SCRUB_CYCLE(scrub_cycle_hours);
|
|
|
|
|
|
|
|
return cxl_scrub_set_attrbs(dev, ctx, wr_cycle, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct edac_scrub_ops cxl_ps_scrub_ops = {
|
|
|
|
.get_enabled_bg = cxl_patrol_scrub_get_enabled_bg,
|
|
|
|
.set_enabled_bg = cxl_patrol_scrub_set_enabled_bg,
|
|
|
|
.get_min_cycle = cxl_patrol_scrub_get_min_scrub_cycle,
|
|
|
|
.get_max_cycle = cxl_patrol_scrub_get_max_scrub_cycle,
|
|
|
|
.get_cycle_duration = cxl_patrol_scrub_get_scrub_cycle,
|
|
|
|
.set_cycle_duration = cxl_patrol_scrub_set_scrub_cycle,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cxl_memdev_scrub_init(struct cxl_memdev *cxlmd,
|
|
|
|
struct edac_dev_feature *ras_feature,
|
|
|
|
u8 scrub_inst)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *cxl_ps_ctx;
|
|
|
|
struct cxl_feat_entry *feat_entry;
|
|
|
|
u8 cap, flags;
|
|
|
|
u16 cycle;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
feat_entry = cxl_feature_info(to_cxlfs(cxlmd->cxlds),
|
|
|
|
&CXL_FEAT_PATROL_SCRUB_UUID);
|
|
|
|
if (IS_ERR(feat_entry))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!(le32_to_cpu(feat_entry->flags) & CXL_FEATURE_F_CHANGEABLE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
cxl_ps_ctx = devm_kzalloc(&cxlmd->dev, sizeof(*cxl_ps_ctx), GFP_KERNEL);
|
|
|
|
if (!cxl_ps_ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*cxl_ps_ctx = (struct cxl_patrol_scrub_context){
|
|
|
|
.get_feat_size = le16_to_cpu(feat_entry->get_feat_size),
|
|
|
|
.set_feat_size = le16_to_cpu(feat_entry->set_feat_size),
|
|
|
|
.get_version = feat_entry->get_feat_ver,
|
|
|
|
.set_version = feat_entry->set_feat_ver,
|
|
|
|
.effects = le16_to_cpu(feat_entry->effects),
|
|
|
|
.instance = scrub_inst,
|
|
|
|
.cxlmd = cxlmd,
|
|
|
|
};
|
|
|
|
|
|
|
|
rc = cxl_mem_scrub_get_attrbs(&cxlmd->cxlds->cxl_mbox, &cap, &cycle,
|
|
|
|
&flags, NULL);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
cxlmd->scrub_cycle = CXL_GET_SCRUB_CYCLE(cycle);
|
|
|
|
cxlmd->scrub_region_id = CXL_SCRUB_NO_REGION;
|
|
|
|
|
|
|
|
ras_feature->ft_type = RAS_FEAT_SCRUB;
|
|
|
|
ras_feature->instance = cxl_ps_ctx->instance;
|
|
|
|
ras_feature->scrub_ops = &cxl_ps_scrub_ops;
|
|
|
|
ras_feature->ctx = cxl_ps_ctx;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_region_scrub_init(struct cxl_region *cxlr,
|
|
|
|
struct edac_dev_feature *ras_feature,
|
|
|
|
u8 scrub_inst)
|
|
|
|
{
|
|
|
|
struct cxl_patrol_scrub_context *cxl_ps_ctx;
|
|
|
|
struct cxl_region_params *p = &cxlr->params;
|
|
|
|
struct cxl_feat_entry *feat_entry = NULL;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
u8 cap, flags;
|
|
|
|
u16 cycle;
|
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The cxl_region_rwsem must be held if the code below is used in a context
|
|
|
|
* other than when the region is in the probe state, as shown here.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < p->nr_targets; i++) {
|
|
|
|
struct cxl_endpoint_decoder *cxled = p->targets[i];
|
|
|
|
|
|
|
|
cxlmd = cxled_to_memdev(cxled);
|
|
|
|
feat_entry = cxl_feature_info(to_cxlfs(cxlmd->cxlds),
|
|
|
|
&CXL_FEAT_PATROL_SCRUB_UUID);
|
|
|
|
if (IS_ERR(feat_entry))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!(le32_to_cpu(feat_entry->flags) &
|
|
|
|
CXL_FEATURE_F_CHANGEABLE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
rc = cxl_mem_scrub_get_attrbs(&cxlmd->cxlds->cxl_mbox, &cap,
|
|
|
|
&cycle, &flags, NULL);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
cxlmd->scrub_cycle = CXL_GET_SCRUB_CYCLE(cycle);
|
|
|
|
cxlmd->scrub_region_id = CXL_SCRUB_NO_REGION;
|
|
|
|
}
|
|
|
|
|
|
|
|
cxl_ps_ctx = devm_kzalloc(&cxlr->dev, sizeof(*cxl_ps_ctx), GFP_KERNEL);
|
|
|
|
if (!cxl_ps_ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*cxl_ps_ctx = (struct cxl_patrol_scrub_context){
|
|
|
|
.get_feat_size = le16_to_cpu(feat_entry->get_feat_size),
|
|
|
|
.set_feat_size = le16_to_cpu(feat_entry->set_feat_size),
|
|
|
|
.get_version = feat_entry->get_feat_ver,
|
|
|
|
.set_version = feat_entry->set_feat_ver,
|
|
|
|
.effects = le16_to_cpu(feat_entry->effects),
|
|
|
|
.instance = scrub_inst,
|
|
|
|
.cxlr = cxlr,
|
|
|
|
};
|
|
|
|
|
|
|
|
ras_feature->ft_type = RAS_FEAT_SCRUB;
|
|
|
|
ras_feature->instance = cxl_ps_ctx->instance;
|
|
|
|
ras_feature->scrub_ops = &cxl_ps_scrub_ops;
|
|
|
|
ras_feature->ctx = cxl_ps_ctx;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:42 +01:00
|
|
|
struct cxl_ecs_context {
|
|
|
|
u16 num_media_frus;
|
|
|
|
u16 get_feat_size;
|
|
|
|
u16 set_feat_size;
|
|
|
|
u8 get_version;
|
|
|
|
u8 set_version;
|
|
|
|
u16 effects;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.9.11.2 Table 8-225 DDR5 ECS Control Feature
|
|
|
|
* Readable Attributes.
|
|
|
|
*/
|
|
|
|
struct cxl_ecs_fru_rd_attrbs {
|
|
|
|
u8 ecs_cap;
|
|
|
|
__le16 ecs_config;
|
|
|
|
u8 ecs_flags;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct cxl_ecs_rd_attrbs {
|
|
|
|
u8 ecs_log_cap;
|
|
|
|
struct cxl_ecs_fru_rd_attrbs fru_attrbs[];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.9.11.2 Table 8-226 DDR5 ECS Control Feature
|
|
|
|
* Writable Attributes.
|
|
|
|
*/
|
|
|
|
struct cxl_ecs_fru_wr_attrbs {
|
|
|
|
__le16 ecs_config;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct cxl_ecs_wr_attrbs {
|
|
|
|
u8 ecs_log_cap;
|
|
|
|
struct cxl_ecs_fru_wr_attrbs fru_attrbs[];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
#define CXL_ECS_LOG_ENTRY_TYPE_MASK GENMASK(1, 0)
|
|
|
|
#define CXL_ECS_REALTIME_REPORT_CAP_MASK BIT(0)
|
|
|
|
#define CXL_ECS_THRESHOLD_COUNT_MASK GENMASK(2, 0)
|
|
|
|
#define CXL_ECS_COUNT_MODE_MASK BIT(3)
|
|
|
|
#define CXL_ECS_RESET_COUNTER_MASK BIT(4)
|
|
|
|
#define CXL_ECS_RESET_COUNTER 1
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ECS_THRESHOLD_256 = 256,
|
|
|
|
ECS_THRESHOLD_1024 = 1024,
|
|
|
|
ECS_THRESHOLD_4096 = 4096,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ECS_THRESHOLD_IDX_256 = 3,
|
|
|
|
ECS_THRESHOLD_IDX_1024 = 4,
|
|
|
|
ECS_THRESHOLD_IDX_4096 = 5,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u16 ecs_supp_threshold[] = {
|
|
|
|
[ECS_THRESHOLD_IDX_256] = 256,
|
|
|
|
[ECS_THRESHOLD_IDX_1024] = 1024,
|
|
|
|
[ECS_THRESHOLD_IDX_4096] = 4096,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ECS_LOG_ENTRY_TYPE_DRAM = 0x0,
|
|
|
|
ECS_LOG_ENTRY_TYPE_MEM_MEDIA_FRU = 0x1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum cxl_ecs_count_mode {
|
|
|
|
ECS_MODE_COUNTS_ROWS = 0,
|
|
|
|
ECS_MODE_COUNTS_CODEWORDS = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cxl_mem_ecs_get_attrbs(struct device *dev,
|
|
|
|
struct cxl_ecs_context *cxl_ecs_ctx,
|
|
|
|
int fru_id, u8 *log_cap, u16 *config)
|
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ecs_ctx->cxlmd;
|
|
|
|
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
struct cxl_ecs_fru_rd_attrbs *fru_rd_attrbs;
|
|
|
|
size_t rd_data_size;
|
|
|
|
size_t data_size;
|
|
|
|
|
|
|
|
rd_data_size = cxl_ecs_ctx->get_feat_size;
|
|
|
|
|
|
|
|
struct cxl_ecs_rd_attrbs *rd_attrbs __free(kvfree) =
|
|
|
|
kvzalloc(rd_data_size, GFP_KERNEL);
|
|
|
|
if (!rd_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data_size = cxl_get_feature(cxl_mbox, &CXL_FEAT_ECS_UUID,
|
|
|
|
CXL_GET_FEAT_SEL_CURRENT_VALUE, rd_attrbs,
|
|
|
|
rd_data_size, 0, NULL);
|
|
|
|
if (!data_size)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
fru_rd_attrbs = rd_attrbs->fru_attrbs;
|
|
|
|
*log_cap = rd_attrbs->ecs_log_cap;
|
|
|
|
*config = le16_to_cpu(fru_rd_attrbs[fru_id].ecs_config);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_ecs_set_attrbs(struct device *dev,
|
|
|
|
struct cxl_ecs_context *cxl_ecs_ctx,
|
|
|
|
int fru_id, u8 log_cap, u16 config)
|
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ecs_ctx->cxlmd;
|
|
|
|
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
struct cxl_ecs_fru_rd_attrbs *fru_rd_attrbs;
|
|
|
|
struct cxl_ecs_fru_wr_attrbs *fru_wr_attrbs;
|
|
|
|
size_t rd_data_size, wr_data_size;
|
|
|
|
u16 num_media_frus, count;
|
|
|
|
size_t data_size;
|
|
|
|
|
|
|
|
num_media_frus = cxl_ecs_ctx->num_media_frus;
|
|
|
|
rd_data_size = cxl_ecs_ctx->get_feat_size;
|
|
|
|
wr_data_size = cxl_ecs_ctx->set_feat_size;
|
|
|
|
struct cxl_ecs_rd_attrbs *rd_attrbs __free(kvfree) =
|
|
|
|
kvzalloc(rd_data_size, GFP_KERNEL);
|
|
|
|
if (!rd_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data_size = cxl_get_feature(cxl_mbox, &CXL_FEAT_ECS_UUID,
|
|
|
|
CXL_GET_FEAT_SEL_CURRENT_VALUE, rd_attrbs,
|
|
|
|
rd_data_size, 0, NULL);
|
|
|
|
if (!data_size)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
struct cxl_ecs_wr_attrbs *wr_attrbs __free(kvfree) =
|
|
|
|
kvzalloc(wr_data_size, GFP_KERNEL);
|
|
|
|
if (!wr_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill writable attributes from the current attributes read
|
|
|
|
* for all the media FRUs.
|
|
|
|
*/
|
|
|
|
fru_rd_attrbs = rd_attrbs->fru_attrbs;
|
|
|
|
fru_wr_attrbs = wr_attrbs->fru_attrbs;
|
|
|
|
wr_attrbs->ecs_log_cap = log_cap;
|
|
|
|
for (count = 0; count < num_media_frus; count++)
|
|
|
|
fru_wr_attrbs[count].ecs_config =
|
|
|
|
fru_rd_attrbs[count].ecs_config;
|
|
|
|
|
|
|
|
fru_wr_attrbs[fru_id].ecs_config = cpu_to_le16(config);
|
|
|
|
|
|
|
|
return cxl_set_feature(cxl_mbox, &CXL_FEAT_ECS_UUID,
|
|
|
|
cxl_ecs_ctx->set_version, wr_attrbs,
|
|
|
|
wr_data_size,
|
|
|
|
CXL_SET_FEAT_FLAG_DATA_SAVED_ACROSS_RESET,
|
|
|
|
0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 cxl_get_ecs_log_entry_type(u8 log_cap, u16 config)
|
|
|
|
{
|
|
|
|
return FIELD_GET(CXL_ECS_LOG_ENTRY_TYPE_MASK, log_cap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16 cxl_get_ecs_threshold(u8 log_cap, u16 config)
|
|
|
|
{
|
|
|
|
u8 index = FIELD_GET(CXL_ECS_THRESHOLD_COUNT_MASK, config);
|
|
|
|
|
|
|
|
return ecs_supp_threshold[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 cxl_get_ecs_count_mode(u8 log_cap, u16 config)
|
|
|
|
{
|
|
|
|
return FIELD_GET(CXL_ECS_COUNT_MODE_MASK, config);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CXL_ECS_GET_ATTR(attrb) \
|
|
|
|
static int cxl_ecs_get_##attrb(struct device *dev, void *drv_data, \
|
|
|
|
int fru_id, u32 *val) \
|
|
|
|
{ \
|
|
|
|
struct cxl_ecs_context *ctx = drv_data; \
|
|
|
|
u8 log_cap; \
|
|
|
|
u16 config; \
|
|
|
|
int ret; \
|
|
|
|
\
|
|
|
|
ret = cxl_mem_ecs_get_attrbs(dev, ctx, fru_id, &log_cap, \
|
|
|
|
&config); \
|
|
|
|
if (ret) \
|
|
|
|
return ret; \
|
|
|
|
\
|
|
|
|
*val = cxl_get_ecs_##attrb(log_cap, config); \
|
|
|
|
\
|
|
|
|
return 0; \
|
|
|
|
}
|
|
|
|
|
|
|
|
CXL_ECS_GET_ATTR(log_entry_type)
|
|
|
|
CXL_ECS_GET_ATTR(count_mode)
|
|
|
|
CXL_ECS_GET_ATTR(threshold)
|
|
|
|
|
|
|
|
static int cxl_set_ecs_log_entry_type(struct device *dev, u8 *log_cap,
|
|
|
|
u16 *config, u32 val)
|
|
|
|
{
|
|
|
|
if (val != ECS_LOG_ENTRY_TYPE_DRAM &&
|
|
|
|
val != ECS_LOG_ENTRY_TYPE_MEM_MEDIA_FRU)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
*log_cap = FIELD_PREP(CXL_ECS_LOG_ENTRY_TYPE_MASK, val);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_set_ecs_threshold(struct device *dev, u8 *log_cap, u16 *config,
|
|
|
|
u32 val)
|
|
|
|
{
|
|
|
|
*config &= ~CXL_ECS_THRESHOLD_COUNT_MASK;
|
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case ECS_THRESHOLD_256:
|
|
|
|
*config |= FIELD_PREP(CXL_ECS_THRESHOLD_COUNT_MASK,
|
|
|
|
ECS_THRESHOLD_IDX_256);
|
|
|
|
break;
|
|
|
|
case ECS_THRESHOLD_1024:
|
|
|
|
*config |= FIELD_PREP(CXL_ECS_THRESHOLD_COUNT_MASK,
|
|
|
|
ECS_THRESHOLD_IDX_1024);
|
|
|
|
break;
|
|
|
|
case ECS_THRESHOLD_4096:
|
|
|
|
*config |= FIELD_PREP(CXL_ECS_THRESHOLD_COUNT_MASK,
|
|
|
|
ECS_THRESHOLD_IDX_4096);
|
|
|
|
break;
|
|
|
|
default:
|
2025-06-22 11:39:16 -07:00
|
|
|
dev_dbg(dev, "Invalid CXL ECS threshold count(%u) to set\n",
|
2025-05-21 13:47:42 +01:00
|
|
|
val);
|
|
|
|
dev_dbg(dev, "Supported ECS threshold counts: %u, %u, %u\n",
|
|
|
|
ECS_THRESHOLD_256, ECS_THRESHOLD_1024,
|
|
|
|
ECS_THRESHOLD_4096);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_set_ecs_count_mode(struct device *dev, u8 *log_cap, u16 *config,
|
|
|
|
u32 val)
|
|
|
|
{
|
|
|
|
if (val != ECS_MODE_COUNTS_ROWS && val != ECS_MODE_COUNTS_CODEWORDS) {
|
|
|
|
dev_dbg(dev, "Invalid CXL ECS scrub mode(%d) to set\n", val);
|
|
|
|
dev_dbg(dev,
|
|
|
|
"Supported ECS Modes: 0: ECS counts rows with errors,"
|
|
|
|
" 1: ECS counts codewords with errors\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*config &= ~CXL_ECS_COUNT_MODE_MASK;
|
|
|
|
*config |= FIELD_PREP(CXL_ECS_COUNT_MODE_MASK, val);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_set_ecs_reset_counter(struct device *dev, u8 *log_cap,
|
|
|
|
u16 *config, u32 val)
|
|
|
|
{
|
|
|
|
if (val != CXL_ECS_RESET_COUNTER)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
*config &= ~CXL_ECS_RESET_COUNTER_MASK;
|
|
|
|
*config |= FIELD_PREP(CXL_ECS_RESET_COUNTER_MASK, val);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CXL_ECS_SET_ATTR(attrb) \
|
|
|
|
static int cxl_ecs_set_##attrb(struct device *dev, void *drv_data, \
|
|
|
|
int fru_id, u32 val) \
|
|
|
|
{ \
|
|
|
|
struct cxl_ecs_context *ctx = drv_data; \
|
|
|
|
u8 log_cap; \
|
|
|
|
u16 config; \
|
|
|
|
int ret; \
|
|
|
|
\
|
|
|
|
if (!capable(CAP_SYS_RAWIO)) \
|
|
|
|
return -EPERM; \
|
|
|
|
\
|
|
|
|
ret = cxl_mem_ecs_get_attrbs(dev, ctx, fru_id, &log_cap, \
|
|
|
|
&config); \
|
|
|
|
if (ret) \
|
|
|
|
return ret; \
|
|
|
|
\
|
|
|
|
ret = cxl_set_ecs_##attrb(dev, &log_cap, &config, val); \
|
|
|
|
if (ret) \
|
|
|
|
return ret; \
|
|
|
|
\
|
|
|
|
return cxl_mem_ecs_set_attrbs(dev, ctx, fru_id, log_cap, \
|
|
|
|
config); \
|
|
|
|
}
|
|
|
|
CXL_ECS_SET_ATTR(log_entry_type)
|
|
|
|
CXL_ECS_SET_ATTR(count_mode)
|
|
|
|
CXL_ECS_SET_ATTR(reset_counter)
|
|
|
|
CXL_ECS_SET_ATTR(threshold)
|
|
|
|
|
|
|
|
static const struct edac_ecs_ops cxl_ecs_ops = {
|
|
|
|
.get_log_entry_type = cxl_ecs_get_log_entry_type,
|
|
|
|
.set_log_entry_type = cxl_ecs_set_log_entry_type,
|
|
|
|
.get_mode = cxl_ecs_get_count_mode,
|
|
|
|
.set_mode = cxl_ecs_set_count_mode,
|
|
|
|
.reset = cxl_ecs_set_reset_counter,
|
|
|
|
.get_threshold = cxl_ecs_get_threshold,
|
|
|
|
.set_threshold = cxl_ecs_set_threshold,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cxl_memdev_ecs_init(struct cxl_memdev *cxlmd,
|
|
|
|
struct edac_dev_feature *ras_feature)
|
|
|
|
{
|
|
|
|
struct cxl_ecs_context *cxl_ecs_ctx;
|
|
|
|
struct cxl_feat_entry *feat_entry;
|
|
|
|
int num_media_frus;
|
|
|
|
|
|
|
|
feat_entry =
|
|
|
|
cxl_feature_info(to_cxlfs(cxlmd->cxlds), &CXL_FEAT_ECS_UUID);
|
|
|
|
if (IS_ERR(feat_entry))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!(le32_to_cpu(feat_entry->flags) & CXL_FEATURE_F_CHANGEABLE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
num_media_frus = (le16_to_cpu(feat_entry->get_feat_size) -
|
|
|
|
sizeof(struct cxl_ecs_rd_attrbs)) /
|
|
|
|
sizeof(struct cxl_ecs_fru_rd_attrbs);
|
|
|
|
if (!num_media_frus)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
cxl_ecs_ctx =
|
|
|
|
devm_kzalloc(&cxlmd->dev, sizeof(*cxl_ecs_ctx), GFP_KERNEL);
|
|
|
|
if (!cxl_ecs_ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*cxl_ecs_ctx = (struct cxl_ecs_context){
|
|
|
|
.get_feat_size = le16_to_cpu(feat_entry->get_feat_size),
|
|
|
|
.set_feat_size = le16_to_cpu(feat_entry->set_feat_size),
|
|
|
|
.get_version = feat_entry->get_feat_ver,
|
|
|
|
.set_version = feat_entry->set_feat_ver,
|
|
|
|
.effects = le16_to_cpu(feat_entry->effects),
|
|
|
|
.num_media_frus = num_media_frus,
|
|
|
|
.cxlmd = cxlmd,
|
|
|
|
};
|
|
|
|
|
|
|
|
ras_feature->ft_type = RAS_FEAT_ECS;
|
|
|
|
ras_feature->ecs_ops = &cxl_ecs_ops;
|
|
|
|
ras_feature->ctx = cxl_ecs_ctx;
|
|
|
|
ras_feature->ecs_info.num_media_frus = num_media_frus;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:43 +01:00
|
|
|
/*
|
|
|
|
* Perform Maintenance CXL 3.2 Spec 8.2.10.7.1
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform Maintenance input payload
|
|
|
|
* CXL rev 3.2 section 8.2.10.7.1 Table 8-117
|
|
|
|
*/
|
|
|
|
struct cxl_mbox_maintenance_hdr {
|
|
|
|
u8 op_class;
|
|
|
|
u8 op_subclass;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
static int cxl_perform_maintenance(struct cxl_mailbox *cxl_mbox, u8 class,
|
|
|
|
u8 subclass, void *data_in,
|
|
|
|
size_t data_in_size)
|
|
|
|
{
|
|
|
|
struct cxl_memdev_maintenance_pi {
|
|
|
|
struct cxl_mbox_maintenance_hdr hdr;
|
|
|
|
u8 data[];
|
|
|
|
} __packed;
|
|
|
|
struct cxl_mbox_cmd mbox_cmd;
|
|
|
|
size_t hdr_size;
|
|
|
|
|
|
|
|
struct cxl_memdev_maintenance_pi *pi __free(kvfree) =
|
|
|
|
kvzalloc(cxl_mbox->payload_size, GFP_KERNEL);
|
|
|
|
if (!pi)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
pi->hdr.op_class = class;
|
|
|
|
pi->hdr.op_subclass = subclass;
|
|
|
|
hdr_size = sizeof(pi->hdr);
|
|
|
|
/*
|
|
|
|
* Check minimum mbox payload size is available for
|
|
|
|
* the maintenance data transfer.
|
|
|
|
*/
|
|
|
|
if (hdr_size + data_in_size > cxl_mbox->payload_size)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(pi->data, data_in, data_in_size);
|
|
|
|
mbox_cmd = (struct cxl_mbox_cmd){
|
|
|
|
.opcode = CXL_MBOX_OP_DO_MAINTENANCE,
|
|
|
|
.size_in = hdr_size + data_in_size,
|
|
|
|
.payload_in = pi,
|
|
|
|
};
|
|
|
|
|
|
|
|
return cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:44 +01:00
|
|
|
/*
|
|
|
|
* Support for finding a memory operation attributes
|
|
|
|
* are from the current boot or not.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct cxl_mem_err_rec {
|
|
|
|
struct xarray rec_gen_media;
|
|
|
|
struct xarray rec_dram;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum cxl_mem_repair_type {
|
|
|
|
CXL_PPR,
|
|
|
|
CXL_CACHELINE_SPARING,
|
|
|
|
CXL_ROW_SPARING,
|
|
|
|
CXL_BANK_SPARING,
|
|
|
|
CXL_RANK_SPARING,
|
|
|
|
CXL_REPAIR_MAX,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct cxl_mem_repair_attrbs - CXL memory repair attributes
|
|
|
|
* @dpa: DPA of memory to repair
|
|
|
|
* @nibble_mask: nibble mask, identifies one or more nibbles on the memory bus
|
|
|
|
* @row: row of memory to repair
|
|
|
|
* @column: column of memory to repair
|
|
|
|
* @channel: channel of memory to repair
|
|
|
|
* @sub_channel: sub channel of memory to repair
|
|
|
|
* @rank: rank of memory to repair
|
|
|
|
* @bank_group: bank group of memory to repair
|
|
|
|
* @bank: bank of memory to repair
|
|
|
|
* @repair_type: repair type. For eg. PPR, memory sparing etc.
|
|
|
|
*/
|
|
|
|
struct cxl_mem_repair_attrbs {
|
|
|
|
u64 dpa;
|
|
|
|
u32 nibble_mask;
|
|
|
|
u32 row;
|
|
|
|
u16 column;
|
|
|
|
u8 channel;
|
|
|
|
u8 sub_channel;
|
|
|
|
u8 rank;
|
|
|
|
u8 bank_group;
|
|
|
|
u8 bank;
|
|
|
|
enum cxl_mem_repair_type repair_type;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct cxl_event_gen_media *
|
|
|
|
cxl_find_rec_gen_media(struct cxl_memdev *cxlmd,
|
|
|
|
struct cxl_mem_repair_attrbs *attrbs)
|
|
|
|
{
|
|
|
|
struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array;
|
|
|
|
struct cxl_event_gen_media *rec;
|
|
|
|
|
|
|
|
if (!array_rec)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
rec = xa_load(&array_rec->rec_gen_media, attrbs->dpa);
|
|
|
|
if (!rec)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (attrbs->repair_type == CXL_PPR)
|
|
|
|
return rec;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cxl_event_dram *
|
|
|
|
cxl_find_rec_dram(struct cxl_memdev *cxlmd,
|
|
|
|
struct cxl_mem_repair_attrbs *attrbs)
|
|
|
|
{
|
|
|
|
struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array;
|
|
|
|
struct cxl_event_dram *rec;
|
|
|
|
u16 validity_flags;
|
|
|
|
|
|
|
|
if (!array_rec)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
rec = xa_load(&array_rec->rec_dram, attrbs->dpa);
|
|
|
|
if (!rec)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags);
|
|
|
|
if (!(validity_flags & CXL_DER_VALID_CHANNEL) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_RANK))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
switch (attrbs->repair_type) {
|
|
|
|
case CXL_PPR:
|
|
|
|
if (!(validity_flags & CXL_DER_VALID_NIBBLE) ||
|
|
|
|
get_unaligned_le24(rec->nibble_mask) == attrbs->nibble_mask)
|
|
|
|
return rec;
|
|
|
|
break;
|
|
|
|
case CXL_CACHELINE_SPARING:
|
|
|
|
if (!(validity_flags & CXL_DER_VALID_BANK_GROUP) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_BANK) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_ROW) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_COLUMN))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (rec->media_hdr.channel == attrbs->channel &&
|
|
|
|
rec->media_hdr.rank == attrbs->rank &&
|
|
|
|
rec->bank_group == attrbs->bank_group &&
|
|
|
|
rec->bank == attrbs->bank &&
|
|
|
|
get_unaligned_le24(rec->row) == attrbs->row &&
|
|
|
|
get_unaligned_le16(rec->column) == attrbs->column &&
|
|
|
|
(!(validity_flags & CXL_DER_VALID_NIBBLE) ||
|
|
|
|
get_unaligned_le24(rec->nibble_mask) ==
|
|
|
|
attrbs->nibble_mask) &&
|
|
|
|
(!(validity_flags & CXL_DER_VALID_SUB_CHANNEL) ||
|
|
|
|
rec->sub_channel == attrbs->sub_channel))
|
|
|
|
return rec;
|
|
|
|
break;
|
|
|
|
case CXL_ROW_SPARING:
|
|
|
|
if (!(validity_flags & CXL_DER_VALID_BANK_GROUP) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_BANK) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_ROW))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (rec->media_hdr.channel == attrbs->channel &&
|
|
|
|
rec->media_hdr.rank == attrbs->rank &&
|
|
|
|
rec->bank_group == attrbs->bank_group &&
|
|
|
|
rec->bank == attrbs->bank &&
|
|
|
|
get_unaligned_le24(rec->row) == attrbs->row &&
|
|
|
|
(!(validity_flags & CXL_DER_VALID_NIBBLE) ||
|
|
|
|
get_unaligned_le24(rec->nibble_mask) ==
|
|
|
|
attrbs->nibble_mask))
|
|
|
|
return rec;
|
|
|
|
break;
|
|
|
|
case CXL_BANK_SPARING:
|
|
|
|
if (!(validity_flags & CXL_DER_VALID_BANK_GROUP) ||
|
|
|
|
!(validity_flags & CXL_DER_VALID_BANK))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (rec->media_hdr.channel == attrbs->channel &&
|
|
|
|
rec->media_hdr.rank == attrbs->rank &&
|
|
|
|
rec->bank_group == attrbs->bank_group &&
|
|
|
|
rec->bank == attrbs->bank &&
|
|
|
|
(!(validity_flags & CXL_DER_VALID_NIBBLE) ||
|
|
|
|
get_unaligned_le24(rec->nibble_mask) ==
|
|
|
|
attrbs->nibble_mask))
|
|
|
|
return rec;
|
|
|
|
break;
|
|
|
|
case CXL_RANK_SPARING:
|
|
|
|
if (rec->media_hdr.channel == attrbs->channel &&
|
|
|
|
rec->media_hdr.rank == attrbs->rank &&
|
|
|
|
(!(validity_flags & CXL_DER_VALID_NIBBLE) ||
|
|
|
|
get_unaligned_le24(rec->nibble_mask) ==
|
|
|
|
attrbs->nibble_mask))
|
|
|
|
return rec;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CXL_MAX_STORAGE_DAYS 10
|
|
|
|
#define CXL_MAX_STORAGE_TIME_SECS (CXL_MAX_STORAGE_DAYS * 24 * 60 * 60)
|
|
|
|
|
|
|
|
static void cxl_del_expired_gmedia_recs(struct xarray *rec_xarray,
|
|
|
|
struct cxl_event_gen_media *cur_rec)
|
|
|
|
{
|
|
|
|
u64 cur_ts = le64_to_cpu(cur_rec->media_hdr.hdr.timestamp);
|
|
|
|
struct cxl_event_gen_media *rec;
|
|
|
|
unsigned long index;
|
|
|
|
u64 delta_ts_secs;
|
|
|
|
|
|
|
|
xa_for_each(rec_xarray, index, rec) {
|
|
|
|
delta_ts_secs = (cur_ts -
|
|
|
|
le64_to_cpu(rec->media_hdr.hdr.timestamp)) / 1000000000ULL;
|
|
|
|
if (delta_ts_secs >= CXL_MAX_STORAGE_TIME_SECS) {
|
|
|
|
xa_erase(rec_xarray, index);
|
|
|
|
kfree(rec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cxl_del_expired_dram_recs(struct xarray *rec_xarray,
|
|
|
|
struct cxl_event_dram *cur_rec)
|
|
|
|
{
|
|
|
|
u64 cur_ts = le64_to_cpu(cur_rec->media_hdr.hdr.timestamp);
|
|
|
|
struct cxl_event_dram *rec;
|
|
|
|
unsigned long index;
|
|
|
|
u64 delta_secs;
|
|
|
|
|
|
|
|
xa_for_each(rec_xarray, index, rec) {
|
|
|
|
delta_secs = (cur_ts -
|
|
|
|
le64_to_cpu(rec->media_hdr.hdr.timestamp)) / 1000000000ULL;
|
|
|
|
if (delta_secs >= CXL_MAX_STORAGE_TIME_SECS) {
|
|
|
|
xa_erase(rec_xarray, index);
|
|
|
|
kfree(rec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CXL_MAX_REC_STORAGE_COUNT 200
|
|
|
|
|
|
|
|
static void cxl_del_overflow_old_recs(struct xarray *rec_xarray)
|
|
|
|
{
|
|
|
|
void *err_rec;
|
|
|
|
unsigned long index, count = 0;
|
|
|
|
|
|
|
|
xa_for_each(rec_xarray, index, err_rec)
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if (count <= CXL_MAX_REC_STORAGE_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
count -= CXL_MAX_REC_STORAGE_COUNT;
|
|
|
|
xa_for_each(rec_xarray, index, err_rec) {
|
|
|
|
xa_erase(rec_xarray, index);
|
|
|
|
kfree(err_rec);
|
|
|
|
count--;
|
|
|
|
if (!count)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd, union cxl_event *evt)
|
|
|
|
{
|
|
|
|
struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array;
|
|
|
|
struct cxl_event_gen_media *rec;
|
|
|
|
void *old_rec;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_CXL_EDAC_MEM_REPAIR) || !array_rec)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rec = kmemdup(&evt->gen_media, sizeof(*rec), GFP_KERNEL);
|
|
|
|
if (!rec)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
old_rec = xa_store(&array_rec->rec_gen_media,
|
|
|
|
le64_to_cpu(rec->media_hdr.phys_addr), rec,
|
|
|
|
GFP_KERNEL);
|
2025-06-13 09:16:48 +08:00
|
|
|
if (xa_is_err(old_rec)) {
|
|
|
|
kfree(rec);
|
2025-05-21 13:47:44 +01:00
|
|
|
return xa_err(old_rec);
|
2025-06-13 09:16:48 +08:00
|
|
|
}
|
2025-05-21 13:47:44 +01:00
|
|
|
|
|
|
|
kfree(old_rec);
|
|
|
|
|
|
|
|
cxl_del_expired_gmedia_recs(&array_rec->rec_gen_media, rec);
|
|
|
|
cxl_del_overflow_old_recs(&array_rec->rec_gen_media);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_NS_GPL(cxl_store_rec_gen_media, "CXL");
|
|
|
|
|
|
|
|
int cxl_store_rec_dram(struct cxl_memdev *cxlmd, union cxl_event *evt)
|
|
|
|
{
|
|
|
|
struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array;
|
|
|
|
struct cxl_event_dram *rec;
|
|
|
|
void *old_rec;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_CXL_EDAC_MEM_REPAIR) || !array_rec)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rec = kmemdup(&evt->dram, sizeof(*rec), GFP_KERNEL);
|
|
|
|
if (!rec)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
old_rec = xa_store(&array_rec->rec_dram,
|
|
|
|
le64_to_cpu(rec->media_hdr.phys_addr), rec,
|
|
|
|
GFP_KERNEL);
|
2025-06-13 09:16:48 +08:00
|
|
|
if (xa_is_err(old_rec)) {
|
|
|
|
kfree(rec);
|
2025-05-21 13:47:44 +01:00
|
|
|
return xa_err(old_rec);
|
2025-06-13 09:16:48 +08:00
|
|
|
}
|
2025-05-21 13:47:44 +01:00
|
|
|
|
|
|
|
kfree(old_rec);
|
|
|
|
|
|
|
|
cxl_del_expired_dram_recs(&array_rec->rec_dram, rec);
|
|
|
|
cxl_del_overflow_old_recs(&array_rec->rec_dram);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_NS_GPL(cxl_store_rec_dram, "CXL");
|
|
|
|
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
static bool cxl_is_memdev_memory_online(const struct cxl_memdev *cxlmd)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = cxlmd->endpoint;
|
|
|
|
|
|
|
|
if (port && cxl_num_decoders_committed(port))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CXL memory sparing control
|
|
|
|
*/
|
|
|
|
enum cxl_mem_sparing_granularity {
|
|
|
|
CXL_MEM_SPARING_CACHELINE,
|
|
|
|
CXL_MEM_SPARING_ROW,
|
|
|
|
CXL_MEM_SPARING_BANK,
|
|
|
|
CXL_MEM_SPARING_RANK,
|
|
|
|
CXL_MEM_SPARING_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cxl_mem_sparing_context {
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
uuid_t repair_uuid;
|
|
|
|
u16 get_feat_size;
|
|
|
|
u16 set_feat_size;
|
|
|
|
u16 effects;
|
|
|
|
u8 instance;
|
|
|
|
u8 get_version;
|
|
|
|
u8 set_version;
|
|
|
|
u8 op_class;
|
|
|
|
u8 op_subclass;
|
|
|
|
bool cap_safe_when_in_use;
|
|
|
|
bool cap_hard_sparing;
|
|
|
|
bool cap_soft_sparing;
|
|
|
|
u8 channel;
|
|
|
|
u8 rank;
|
|
|
|
u8 bank_group;
|
|
|
|
u32 nibble_mask;
|
|
|
|
u64 dpa;
|
|
|
|
u32 row;
|
|
|
|
u16 column;
|
|
|
|
u8 bank;
|
|
|
|
u8 sub_channel;
|
|
|
|
enum edac_mem_repair_type repair_type;
|
|
|
|
bool persist_mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CXL_SPARING_RD_CAP_SAFE_IN_USE_MASK BIT(0)
|
|
|
|
#define CXL_SPARING_RD_CAP_HARD_SPARING_MASK BIT(1)
|
|
|
|
#define CXL_SPARING_RD_CAP_SOFT_SPARING_MASK BIT(2)
|
|
|
|
|
|
|
|
#define CXL_SPARING_WR_DEVICE_INITIATED_MASK BIT(0)
|
|
|
|
|
|
|
|
#define CXL_SPARING_QUERY_RESOURCE_FLAG BIT(0)
|
|
|
|
#define CXL_SET_HARD_SPARING_FLAG BIT(1)
|
|
|
|
#define CXL_SPARING_SUB_CHNL_VALID_FLAG BIT(2)
|
|
|
|
#define CXL_SPARING_NIB_MASK_VALID_FLAG BIT(3)
|
|
|
|
|
|
|
|
#define CXL_GET_SPARING_SAFE_IN_USE(flags) \
|
|
|
|
(FIELD_GET(CXL_SPARING_RD_CAP_SAFE_IN_USE_MASK, \
|
|
|
|
flags) ^ 1)
|
|
|
|
#define CXL_GET_CAP_HARD_SPARING(flags) \
|
|
|
|
FIELD_GET(CXL_SPARING_RD_CAP_HARD_SPARING_MASK, \
|
|
|
|
flags)
|
|
|
|
#define CXL_GET_CAP_SOFT_SPARING(flags) \
|
|
|
|
FIELD_GET(CXL_SPARING_RD_CAP_SOFT_SPARING_MASK, \
|
|
|
|
flags)
|
|
|
|
|
|
|
|
#define CXL_SET_SPARING_QUERY_RESOURCE(val) \
|
|
|
|
FIELD_PREP(CXL_SPARING_QUERY_RESOURCE_FLAG, val)
|
|
|
|
#define CXL_SET_HARD_SPARING(val) \
|
|
|
|
FIELD_PREP(CXL_SET_HARD_SPARING_FLAG, val)
|
|
|
|
#define CXL_SET_SPARING_SUB_CHNL_VALID(val) \
|
|
|
|
FIELD_PREP(CXL_SPARING_SUB_CHNL_VALID_FLAG, val)
|
|
|
|
#define CXL_SET_SPARING_NIB_MASK_VALID(val) \
|
|
|
|
FIELD_PREP(CXL_SPARING_NIB_MASK_VALID_FLAG, val)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.7.2.3 Table 8-134 Memory Sparing Feature
|
|
|
|
* Readable Attributes.
|
|
|
|
*/
|
|
|
|
struct cxl_memdev_repair_rd_attrbs_hdr {
|
|
|
|
u8 max_op_latency;
|
|
|
|
__le16 op_cap;
|
|
|
|
__le16 op_mode;
|
|
|
|
u8 op_class;
|
|
|
|
u8 op_subclass;
|
|
|
|
u8 rsvd[9];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct cxl_memdev_sparing_rd_attrbs {
|
|
|
|
struct cxl_memdev_repair_rd_attrbs_hdr hdr;
|
|
|
|
u8 rsvd;
|
|
|
|
__le16 restriction_flags;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL spec rev 3.2 @8.2.10.7.1.4 Table 8-120 Memory Sparing Input Payload.
|
|
|
|
*/
|
|
|
|
struct cxl_memdev_sparing_in_payload {
|
|
|
|
u8 flags;
|
|
|
|
u8 channel;
|
|
|
|
u8 rank;
|
|
|
|
u8 nibble_mask[3];
|
|
|
|
u8 bank_group;
|
|
|
|
u8 bank;
|
|
|
|
u8 row[3];
|
|
|
|
__le16 column;
|
|
|
|
u8 sub_channel;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
static int
|
|
|
|
cxl_mem_sparing_get_attrbs(struct cxl_mem_sparing_context *cxl_sparing_ctx)
|
|
|
|
{
|
|
|
|
size_t rd_data_size = sizeof(struct cxl_memdev_sparing_rd_attrbs);
|
|
|
|
struct cxl_memdev *cxlmd = cxl_sparing_ctx->cxlmd;
|
|
|
|
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
u16 restriction_flags;
|
|
|
|
size_t data_size;
|
|
|
|
u16 return_code;
|
|
|
|
struct cxl_memdev_sparing_rd_attrbs *rd_attrbs __free(kfree) =
|
|
|
|
kzalloc(rd_data_size, GFP_KERNEL);
|
|
|
|
if (!rd_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data_size = cxl_get_feature(cxl_mbox, &cxl_sparing_ctx->repair_uuid,
|
|
|
|
CXL_GET_FEAT_SEL_CURRENT_VALUE, rd_attrbs,
|
|
|
|
rd_data_size, 0, &return_code);
|
|
|
|
if (!data_size)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
cxl_sparing_ctx->op_class = rd_attrbs->hdr.op_class;
|
|
|
|
cxl_sparing_ctx->op_subclass = rd_attrbs->hdr.op_subclass;
|
|
|
|
restriction_flags = le16_to_cpu(rd_attrbs->restriction_flags);
|
|
|
|
cxl_sparing_ctx->cap_safe_when_in_use =
|
|
|
|
CXL_GET_SPARING_SAFE_IN_USE(restriction_flags);
|
|
|
|
cxl_sparing_ctx->cap_hard_sparing =
|
|
|
|
CXL_GET_CAP_HARD_SPARING(restriction_flags);
|
|
|
|
cxl_sparing_ctx->cap_soft_sparing =
|
|
|
|
CXL_GET_CAP_SOFT_SPARING(restriction_flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cxl_event_dram *
|
|
|
|
cxl_mem_get_rec_dram(struct cxl_memdev *cxlmd,
|
|
|
|
struct cxl_mem_sparing_context *ctx)
|
|
|
|
{
|
|
|
|
struct cxl_mem_repair_attrbs attrbs = { 0 };
|
|
|
|
|
|
|
|
attrbs.dpa = ctx->dpa;
|
|
|
|
attrbs.channel = ctx->channel;
|
|
|
|
attrbs.rank = ctx->rank;
|
|
|
|
attrbs.nibble_mask = ctx->nibble_mask;
|
|
|
|
switch (ctx->repair_type) {
|
|
|
|
case EDAC_REPAIR_CACHELINE_SPARING:
|
|
|
|
attrbs.repair_type = CXL_CACHELINE_SPARING;
|
|
|
|
attrbs.bank_group = ctx->bank_group;
|
|
|
|
attrbs.bank = ctx->bank;
|
|
|
|
attrbs.row = ctx->row;
|
|
|
|
attrbs.column = ctx->column;
|
|
|
|
attrbs.sub_channel = ctx->sub_channel;
|
|
|
|
break;
|
|
|
|
case EDAC_REPAIR_ROW_SPARING:
|
|
|
|
attrbs.repair_type = CXL_ROW_SPARING;
|
|
|
|
attrbs.bank_group = ctx->bank_group;
|
|
|
|
attrbs.bank = ctx->bank;
|
|
|
|
attrbs.row = ctx->row;
|
|
|
|
break;
|
|
|
|
case EDAC_REPAIR_BANK_SPARING:
|
|
|
|
attrbs.repair_type = CXL_BANK_SPARING;
|
|
|
|
attrbs.bank_group = ctx->bank_group;
|
|
|
|
attrbs.bank = ctx->bank;
|
|
|
|
break;
|
|
|
|
case EDAC_REPAIR_RANK_SPARING:
|
2025-06-20 13:29:24 +08:00
|
|
|
attrbs.repair_type = CXL_RANK_SPARING;
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cxl_find_rec_dram(cxlmd, &attrbs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cxl_mem_perform_sparing(struct device *dev,
|
|
|
|
struct cxl_mem_sparing_context *cxl_sparing_ctx)
|
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxl_sparing_ctx->cxlmd;
|
|
|
|
struct cxl_memdev_sparing_in_payload sparing_pi;
|
|
|
|
struct cxl_event_dram *rec = NULL;
|
|
|
|
u16 validity_flags = 0;
|
2025-07-11 16:49:32 -07:00
|
|
|
int ret;
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem)))
|
|
|
|
return ret;
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
|
|
|
|
return ret;
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
|
|
|
|
if (!cxl_sparing_ctx->cap_safe_when_in_use) {
|
|
|
|
/* Memory to repair must be offline */
|
|
|
|
if (cxl_is_memdev_memory_online(cxlmd))
|
|
|
|
return -EBUSY;
|
|
|
|
} else {
|
|
|
|
if (cxl_is_memdev_memory_online(cxlmd)) {
|
|
|
|
rec = cxl_mem_get_rec_dram(cxlmd, cxl_sparing_ctx);
|
|
|
|
if (!rec)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!get_unaligned_le16(rec->media_hdr.validity_flags))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&sparing_pi, 0, sizeof(sparing_pi));
|
|
|
|
sparing_pi.flags = CXL_SET_SPARING_QUERY_RESOURCE(0);
|
|
|
|
if (cxl_sparing_ctx->persist_mode)
|
|
|
|
sparing_pi.flags |= CXL_SET_HARD_SPARING(1);
|
|
|
|
|
|
|
|
if (rec)
|
|
|
|
validity_flags = get_unaligned_le16(rec->media_hdr.validity_flags);
|
|
|
|
|
|
|
|
switch (cxl_sparing_ctx->repair_type) {
|
|
|
|
case EDAC_REPAIR_CACHELINE_SPARING:
|
|
|
|
sparing_pi.column = cpu_to_le16(cxl_sparing_ctx->column);
|
|
|
|
if (!rec || (validity_flags & CXL_DER_VALID_SUB_CHANNEL)) {
|
|
|
|
sparing_pi.flags |= CXL_SET_SPARING_SUB_CHNL_VALID(1);
|
|
|
|
sparing_pi.sub_channel = cxl_sparing_ctx->sub_channel;
|
|
|
|
}
|
|
|
|
fallthrough;
|
|
|
|
case EDAC_REPAIR_ROW_SPARING:
|
|
|
|
put_unaligned_le24(cxl_sparing_ctx->row, sparing_pi.row);
|
|
|
|
fallthrough;
|
|
|
|
case EDAC_REPAIR_BANK_SPARING:
|
|
|
|
sparing_pi.bank_group = cxl_sparing_ctx->bank_group;
|
|
|
|
sparing_pi.bank = cxl_sparing_ctx->bank;
|
|
|
|
fallthrough;
|
|
|
|
case EDAC_REPAIR_RANK_SPARING:
|
|
|
|
sparing_pi.rank = cxl_sparing_ctx->rank;
|
|
|
|
fallthrough;
|
|
|
|
default:
|
|
|
|
sparing_pi.channel = cxl_sparing_ctx->channel;
|
|
|
|
if ((rec && (validity_flags & CXL_DER_VALID_NIBBLE)) ||
|
|
|
|
(!rec && (!cxl_sparing_ctx->nibble_mask ||
|
|
|
|
(cxl_sparing_ctx->nibble_mask & 0xFFFFFF)))) {
|
|
|
|
sparing_pi.flags |= CXL_SET_SPARING_NIB_MASK_VALID(1);
|
|
|
|
put_unaligned_le24(cxl_sparing_ctx->nibble_mask,
|
|
|
|
sparing_pi.nibble_mask);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cxl_perform_maintenance(&cxlmd->cxlds->cxl_mbox,
|
|
|
|
cxl_sparing_ctx->op_class,
|
|
|
|
cxl_sparing_ctx->op_subclass,
|
|
|
|
&sparing_pi, sizeof(sparing_pi));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_sparing_get_repair_type(struct device *dev, void *drv_data,
|
|
|
|
const char **repair_type)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
|
|
|
|
switch (ctx->repair_type) {
|
|
|
|
case EDAC_REPAIR_CACHELINE_SPARING:
|
|
|
|
case EDAC_REPAIR_ROW_SPARING:
|
|
|
|
case EDAC_REPAIR_BANK_SPARING:
|
|
|
|
case EDAC_REPAIR_RANK_SPARING:
|
|
|
|
*repair_type = edac_repair_type[ctx->repair_type];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CXL_SPARING_GET_ATTR(attrb, data_type) \
|
|
|
|
static int cxl_mem_sparing_get_##attrb( \
|
|
|
|
struct device *dev, void *drv_data, data_type *val) \
|
|
|
|
{ \
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data; \
|
|
|
|
\
|
|
|
|
*val = ctx->attrb; \
|
|
|
|
\
|
|
|
|
return 0; \
|
|
|
|
}
|
|
|
|
CXL_SPARING_GET_ATTR(persist_mode, bool)
|
|
|
|
CXL_SPARING_GET_ATTR(dpa, u64)
|
|
|
|
CXL_SPARING_GET_ATTR(nibble_mask, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(bank_group, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(bank, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(rank, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(row, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(column, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(channel, u32)
|
|
|
|
CXL_SPARING_GET_ATTR(sub_channel, u32)
|
|
|
|
|
|
|
|
#define CXL_SPARING_SET_ATTR(attrb, data_type) \
|
|
|
|
static int cxl_mem_sparing_set_##attrb(struct device *dev, \
|
|
|
|
void *drv_data, data_type val) \
|
|
|
|
{ \
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data; \
|
|
|
|
\
|
|
|
|
ctx->attrb = val; \
|
|
|
|
\
|
|
|
|
return 0; \
|
|
|
|
}
|
|
|
|
CXL_SPARING_SET_ATTR(nibble_mask, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(bank_group, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(bank, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(rank, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(row, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(column, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(channel, u32)
|
|
|
|
CXL_SPARING_SET_ATTR(sub_channel, u32)
|
|
|
|
|
|
|
|
static int cxl_mem_sparing_set_persist_mode(struct device *dev, void *drv_data,
|
|
|
|
bool persist_mode)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
|
|
|
|
if ((persist_mode && ctx->cap_hard_sparing) ||
|
|
|
|
(!persist_mode && ctx->cap_soft_sparing))
|
|
|
|
ctx->persist_mode = persist_mode;
|
|
|
|
else
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_get_mem_sparing_safe_when_in_use(struct device *dev,
|
|
|
|
void *drv_data, bool *safe)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
|
|
|
|
*safe = ctx->cap_safe_when_in_use;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_sparing_get_min_dpa(struct device *dev, void *drv_data,
|
|
|
|
u64 *min_dpa)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
|
|
|
*min_dpa = cxlds->dpa_res.start;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_sparing_get_max_dpa(struct device *dev, void *drv_data,
|
|
|
|
u64 *max_dpa)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
|
|
|
*max_dpa = cxlds->dpa_res.end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_sparing_set_dpa(struct device *dev, void *drv_data, u64 dpa)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
2025-07-11 11:23:57 +08:00
|
|
|
if (!cxl_resource_contains_addr(&cxlds->dpa_res, dpa))
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ctx->dpa = dpa;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_do_mem_sparing(struct device *dev, void *drv_data, u32 val)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *ctx = drv_data;
|
|
|
|
|
|
|
|
if (val != EDAC_DO_MEM_REPAIR)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return cxl_mem_perform_sparing(dev, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define RANK_OPS \
|
|
|
|
.get_repair_type = cxl_mem_sparing_get_repair_type, \
|
|
|
|
.get_persist_mode = cxl_mem_sparing_get_persist_mode, \
|
|
|
|
.set_persist_mode = cxl_mem_sparing_set_persist_mode, \
|
|
|
|
.get_repair_safe_when_in_use = cxl_get_mem_sparing_safe_when_in_use, \
|
|
|
|
.get_min_dpa = cxl_mem_sparing_get_min_dpa, \
|
|
|
|
.get_max_dpa = cxl_mem_sparing_get_max_dpa, \
|
|
|
|
.get_dpa = cxl_mem_sparing_get_dpa, \
|
|
|
|
.set_dpa = cxl_mem_sparing_set_dpa, \
|
|
|
|
.get_nibble_mask = cxl_mem_sparing_get_nibble_mask, \
|
|
|
|
.set_nibble_mask = cxl_mem_sparing_set_nibble_mask, \
|
|
|
|
.get_rank = cxl_mem_sparing_get_rank, \
|
|
|
|
.set_rank = cxl_mem_sparing_set_rank, \
|
|
|
|
.get_channel = cxl_mem_sparing_get_channel, \
|
|
|
|
.set_channel = cxl_mem_sparing_set_channel, \
|
|
|
|
.do_repair = cxl_do_mem_sparing
|
|
|
|
|
|
|
|
#define BANK_OPS \
|
|
|
|
RANK_OPS, .get_bank_group = cxl_mem_sparing_get_bank_group, \
|
|
|
|
.set_bank_group = cxl_mem_sparing_set_bank_group, \
|
|
|
|
.get_bank = cxl_mem_sparing_get_bank, \
|
|
|
|
.set_bank = cxl_mem_sparing_set_bank
|
|
|
|
|
|
|
|
#define ROW_OPS \
|
|
|
|
BANK_OPS, .get_row = cxl_mem_sparing_get_row, \
|
|
|
|
.set_row = cxl_mem_sparing_set_row
|
|
|
|
|
|
|
|
#define CACHELINE_OPS \
|
|
|
|
ROW_OPS, .get_column = cxl_mem_sparing_get_column, \
|
|
|
|
.set_column = cxl_mem_sparing_set_column, \
|
|
|
|
.get_sub_channel = cxl_mem_sparing_get_sub_channel, \
|
|
|
|
.set_sub_channel = cxl_mem_sparing_set_sub_channel
|
|
|
|
|
|
|
|
static const struct edac_mem_repair_ops cxl_rank_sparing_ops = {
|
|
|
|
RANK_OPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct edac_mem_repair_ops cxl_bank_sparing_ops = {
|
|
|
|
BANK_OPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct edac_mem_repair_ops cxl_row_sparing_ops = {
|
|
|
|
ROW_OPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct edac_mem_repair_ops cxl_cacheline_sparing_ops = {
|
|
|
|
CACHELINE_OPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cxl_mem_sparing_desc {
|
|
|
|
const uuid_t repair_uuid;
|
|
|
|
enum edac_mem_repair_type repair_type;
|
|
|
|
const struct edac_mem_repair_ops *repair_ops;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct cxl_mem_sparing_desc mem_sparing_desc[] = {
|
|
|
|
{
|
|
|
|
.repair_uuid = CXL_FEAT_CACHELINE_SPARING_UUID,
|
|
|
|
.repair_type = EDAC_REPAIR_CACHELINE_SPARING,
|
|
|
|
.repair_ops = &cxl_cacheline_sparing_ops,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.repair_uuid = CXL_FEAT_ROW_SPARING_UUID,
|
|
|
|
.repair_type = EDAC_REPAIR_ROW_SPARING,
|
|
|
|
.repair_ops = &cxl_row_sparing_ops,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.repair_uuid = CXL_FEAT_BANK_SPARING_UUID,
|
|
|
|
.repair_type = EDAC_REPAIR_BANK_SPARING,
|
|
|
|
.repair_ops = &cxl_bank_sparing_ops,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.repair_uuid = CXL_FEAT_RANK_SPARING_UUID,
|
|
|
|
.repair_type = EDAC_REPAIR_RANK_SPARING,
|
|
|
|
.repair_ops = &cxl_rank_sparing_ops,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cxl_memdev_sparing_init(struct cxl_memdev *cxlmd,
|
|
|
|
struct edac_dev_feature *ras_feature,
|
|
|
|
const struct cxl_mem_sparing_desc *desc,
|
|
|
|
u8 repair_inst)
|
|
|
|
{
|
|
|
|
struct cxl_mem_sparing_context *cxl_sparing_ctx;
|
|
|
|
struct cxl_feat_entry *feat_entry;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
feat_entry = cxl_feature_info(to_cxlfs(cxlmd->cxlds),
|
|
|
|
&desc->repair_uuid);
|
|
|
|
if (IS_ERR(feat_entry))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!(le32_to_cpu(feat_entry->flags) & CXL_FEATURE_F_CHANGEABLE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
cxl_sparing_ctx = devm_kzalloc(&cxlmd->dev, sizeof(*cxl_sparing_ctx),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!cxl_sparing_ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*cxl_sparing_ctx = (struct cxl_mem_sparing_context){
|
|
|
|
.get_feat_size = le16_to_cpu(feat_entry->get_feat_size),
|
|
|
|
.set_feat_size = le16_to_cpu(feat_entry->set_feat_size),
|
|
|
|
.get_version = feat_entry->get_feat_ver,
|
|
|
|
.set_version = feat_entry->set_feat_ver,
|
|
|
|
.effects = le16_to_cpu(feat_entry->effects),
|
|
|
|
.cxlmd = cxlmd,
|
|
|
|
.repair_type = desc->repair_type,
|
|
|
|
.instance = repair_inst++,
|
|
|
|
};
|
|
|
|
uuid_copy(&cxl_sparing_ctx->repair_uuid, &desc->repair_uuid);
|
|
|
|
|
|
|
|
ret = cxl_mem_sparing_get_attrbs(cxl_sparing_ctx);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if ((cxl_sparing_ctx->cap_soft_sparing &&
|
|
|
|
cxl_sparing_ctx->cap_hard_sparing) ||
|
|
|
|
cxl_sparing_ctx->cap_soft_sparing)
|
|
|
|
cxl_sparing_ctx->persist_mode = 0;
|
|
|
|
else if (cxl_sparing_ctx->cap_hard_sparing)
|
|
|
|
cxl_sparing_ctx->persist_mode = 1;
|
|
|
|
else
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
ras_feature->ft_type = RAS_FEAT_MEM_REPAIR;
|
|
|
|
ras_feature->instance = cxl_sparing_ctx->instance;
|
|
|
|
ras_feature->mem_repair_ops = desc->repair_ops;
|
|
|
|
ras_feature->ctx = cxl_sparing_ctx;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
/*
|
|
|
|
* CXL memory soft PPR & hard PPR control
|
|
|
|
*/
|
|
|
|
struct cxl_ppr_context {
|
|
|
|
uuid_t repair_uuid;
|
|
|
|
u8 instance;
|
|
|
|
u16 get_feat_size;
|
|
|
|
u16 set_feat_size;
|
|
|
|
u8 get_version;
|
|
|
|
u8 set_version;
|
|
|
|
u16 effects;
|
|
|
|
u8 op_class;
|
|
|
|
u8 op_subclass;
|
|
|
|
bool cap_dpa;
|
|
|
|
bool cap_nib_mask;
|
|
|
|
bool media_accessible;
|
|
|
|
bool data_retained;
|
|
|
|
struct cxl_memdev *cxlmd;
|
|
|
|
enum edac_mem_repair_type repair_type;
|
|
|
|
bool persist_mode;
|
|
|
|
u64 dpa;
|
|
|
|
u32 nibble_mask;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL rev 3.2 @8.2.10.7.2.1 Table 8-128 sPPR Feature Readable Attributes
|
|
|
|
*
|
|
|
|
* See CXL rev 3.2 @8.2.10.7.2.2 Table 8-131 hPPR Feature Readable Attributes
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define CXL_PPR_OP_CAP_DEVICE_INITIATED BIT(0)
|
|
|
|
#define CXL_PPR_OP_MODE_DEV_INITIATED BIT(0)
|
|
|
|
|
|
|
|
#define CXL_PPR_FLAG_DPA_SUPPORT_MASK BIT(0)
|
|
|
|
#define CXL_PPR_FLAG_NIB_SUPPORT_MASK BIT(1)
|
|
|
|
#define CXL_PPR_FLAG_MEM_SPARING_EV_REC_SUPPORT_MASK BIT(2)
|
|
|
|
#define CXL_PPR_FLAG_DEV_INITED_PPR_AT_BOOT_CAP_MASK BIT(3)
|
|
|
|
|
|
|
|
#define CXL_PPR_RESTRICTION_FLAG_MEDIA_ACCESSIBLE_MASK BIT(0)
|
|
|
|
#define CXL_PPR_RESTRICTION_FLAG_DATA_RETAINED_MASK BIT(2)
|
|
|
|
|
|
|
|
#define CXL_PPR_SPARING_EV_REC_EN_MASK BIT(0)
|
|
|
|
#define CXL_PPR_DEV_INITED_PPR_AT_BOOT_EN_MASK BIT(1)
|
|
|
|
|
|
|
|
#define CXL_PPR_GET_CAP_DPA(flags) \
|
|
|
|
FIELD_GET(CXL_PPR_FLAG_DPA_SUPPORT_MASK, flags)
|
|
|
|
#define CXL_PPR_GET_CAP_NIB_MASK(flags) \
|
|
|
|
FIELD_GET(CXL_PPR_FLAG_NIB_SUPPORT_MASK, flags)
|
|
|
|
#define CXL_PPR_GET_MEDIA_ACCESSIBLE(restriction_flags) \
|
|
|
|
(FIELD_GET(CXL_PPR_RESTRICTION_FLAG_MEDIA_ACCESSIBLE_MASK, \
|
|
|
|
restriction_flags) ^ 1)
|
|
|
|
#define CXL_PPR_GET_DATA_RETAINED(restriction_flags) \
|
|
|
|
(FIELD_GET(CXL_PPR_RESTRICTION_FLAG_DATA_RETAINED_MASK, \
|
|
|
|
restriction_flags) ^ 1)
|
|
|
|
|
|
|
|
struct cxl_memdev_ppr_rd_attrbs {
|
|
|
|
struct cxl_memdev_repair_rd_attrbs_hdr hdr;
|
|
|
|
u8 ppr_flags;
|
|
|
|
__le16 restriction_flags;
|
|
|
|
u8 ppr_op_mode;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See CXL rev 3.2 @8.2.10.7.1.2 Table 8-118 sPPR Maintenance Input Payload
|
|
|
|
*
|
|
|
|
* See CXL rev 3.2 @8.2.10.7.1.3 Table 8-119 hPPR Maintenance Input Payload
|
|
|
|
*/
|
|
|
|
struct cxl_memdev_ppr_maintenance_attrbs {
|
|
|
|
u8 flags;
|
|
|
|
__le64 dpa;
|
|
|
|
u8 nibble_mask[3];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
static int cxl_mem_ppr_get_attrbs(struct cxl_ppr_context *cxl_ppr_ctx)
|
|
|
|
{
|
|
|
|
size_t rd_data_size = sizeof(struct cxl_memdev_ppr_rd_attrbs);
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_mailbox *cxl_mbox = &cxlmd->cxlds->cxl_mbox;
|
|
|
|
u16 restriction_flags;
|
|
|
|
size_t data_size;
|
|
|
|
u16 return_code;
|
|
|
|
|
|
|
|
struct cxl_memdev_ppr_rd_attrbs *rd_attrbs __free(kfree) =
|
|
|
|
kmalloc(rd_data_size, GFP_KERNEL);
|
|
|
|
if (!rd_attrbs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data_size = cxl_get_feature(cxl_mbox, &cxl_ppr_ctx->repair_uuid,
|
|
|
|
CXL_GET_FEAT_SEL_CURRENT_VALUE, rd_attrbs,
|
|
|
|
rd_data_size, 0, &return_code);
|
|
|
|
if (!data_size)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
cxl_ppr_ctx->op_class = rd_attrbs->hdr.op_class;
|
|
|
|
cxl_ppr_ctx->op_subclass = rd_attrbs->hdr.op_subclass;
|
|
|
|
cxl_ppr_ctx->cap_dpa = CXL_PPR_GET_CAP_DPA(rd_attrbs->ppr_flags);
|
|
|
|
cxl_ppr_ctx->cap_nib_mask =
|
|
|
|
CXL_PPR_GET_CAP_NIB_MASK(rd_attrbs->ppr_flags);
|
|
|
|
|
|
|
|
restriction_flags = le16_to_cpu(rd_attrbs->restriction_flags);
|
|
|
|
cxl_ppr_ctx->media_accessible =
|
|
|
|
CXL_PPR_GET_MEDIA_ACCESSIBLE(restriction_flags);
|
|
|
|
cxl_ppr_ctx->data_retained =
|
|
|
|
CXL_PPR_GET_DATA_RETAINED(restriction_flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_mem_perform_ppr(struct cxl_ppr_context *cxl_ppr_ctx)
|
|
|
|
{
|
|
|
|
struct cxl_memdev_ppr_maintenance_attrbs maintenance_attrbs;
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_mem_repair_attrbs attrbs = { 0 };
|
2025-07-11 16:49:32 -07:00
|
|
|
int ret;
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, region_rwsem)(&cxl_rwsem.region);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, ®ion_rwsem)))
|
|
|
|
return ret;
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
ACQUIRE(rwsem_read_intr, dpa_rwsem)(&cxl_rwsem.dpa);
|
|
|
|
if ((ret = ACQUIRE_ERR(rwsem_read_intr, &dpa_rwsem)))
|
|
|
|
return ret;
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
|
|
|
|
if (!cxl_ppr_ctx->media_accessible || !cxl_ppr_ctx->data_retained) {
|
|
|
|
/* Memory to repair must be offline */
|
|
|
|
if (cxl_is_memdev_memory_online(cxlmd))
|
|
|
|
return -EBUSY;
|
|
|
|
} else {
|
|
|
|
if (cxl_is_memdev_memory_online(cxlmd)) {
|
|
|
|
/* Check memory to repair is from the current boot */
|
|
|
|
attrbs.repair_type = CXL_PPR;
|
|
|
|
attrbs.dpa = cxl_ppr_ctx->dpa;
|
|
|
|
attrbs.nibble_mask = cxl_ppr_ctx->nibble_mask;
|
|
|
|
if (!cxl_find_rec_dram(cxlmd, &attrbs) &&
|
|
|
|
!cxl_find_rec_gen_media(cxlmd, &attrbs))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&maintenance_attrbs, 0, sizeof(maintenance_attrbs));
|
|
|
|
maintenance_attrbs.flags = 0;
|
|
|
|
maintenance_attrbs.dpa = cpu_to_le64(cxl_ppr_ctx->dpa);
|
|
|
|
put_unaligned_le24(cxl_ppr_ctx->nibble_mask,
|
|
|
|
maintenance_attrbs.nibble_mask);
|
|
|
|
|
|
|
|
return cxl_perform_maintenance(&cxlmd->cxlds->cxl_mbox,
|
|
|
|
cxl_ppr_ctx->op_class,
|
|
|
|
cxl_ppr_ctx->op_subclass,
|
|
|
|
&maintenance_attrbs,
|
|
|
|
sizeof(maintenance_attrbs));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_repair_type(struct device *dev, void *drv_data,
|
|
|
|
const char **repair_type)
|
|
|
|
{
|
|
|
|
*repair_type = edac_repair_type[EDAC_REPAIR_PPR];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_persist_mode(struct device *dev, void *drv_data,
|
|
|
|
bool *persist_mode)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
|
|
|
|
*persist_mode = cxl_ppr_ctx->persist_mode;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_get_ppr_safe_when_in_use(struct device *dev, void *drv_data,
|
|
|
|
bool *safe)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
|
|
|
|
*safe = cxl_ppr_ctx->media_accessible & cxl_ppr_ctx->data_retained;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_min_dpa(struct device *dev, void *drv_data, u64 *min_dpa)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
|
|
|
*min_dpa = cxlds->dpa_res.start;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_max_dpa(struct device *dev, void *drv_data, u64 *max_dpa)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
|
|
|
*max_dpa = cxlds->dpa_res.end;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_dpa(struct device *dev, void *drv_data, u64 *dpa)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
|
|
|
|
*dpa = cxl_ppr_ctx->dpa;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_set_dpa(struct device *dev, void *drv_data, u64 dpa)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
2025-07-11 11:23:57 +08:00
|
|
|
if (!cxl_resource_contains_addr(&cxlds->dpa_res, dpa))
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cxl_ppr_ctx->dpa = dpa;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_get_nibble_mask(struct device *dev, void *drv_data,
|
|
|
|
u32 *nibble_mask)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
|
|
|
|
*nibble_mask = cxl_ppr_ctx->nibble_mask;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_ppr_set_nibble_mask(struct device *dev, void *drv_data,
|
|
|
|
u32 nibble_mask)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
|
|
|
|
|
|
|
cxl_ppr_ctx->nibble_mask = nibble_mask;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cxl_do_ppr(struct device *dev, void *drv_data, u32 val)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_ppr_ctx = drv_data;
|
2025-07-11 11:23:56 +08:00
|
|
|
struct cxl_memdev *cxlmd = cxl_ppr_ctx->cxlmd;
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
|
2025-07-11 11:23:56 +08:00
|
|
|
if (val != EDAC_DO_MEM_REPAIR ||
|
|
|
|
!cxl_resource_contains_addr(&cxlds->dpa_res, cxl_ppr_ctx->dpa))
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return cxl_mem_perform_ppr(cxl_ppr_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct edac_mem_repair_ops cxl_sppr_ops = {
|
|
|
|
.get_repair_type = cxl_ppr_get_repair_type,
|
|
|
|
.get_persist_mode = cxl_ppr_get_persist_mode,
|
|
|
|
.get_repair_safe_when_in_use = cxl_get_ppr_safe_when_in_use,
|
|
|
|
.get_min_dpa = cxl_ppr_get_min_dpa,
|
|
|
|
.get_max_dpa = cxl_ppr_get_max_dpa,
|
|
|
|
.get_dpa = cxl_ppr_get_dpa,
|
|
|
|
.set_dpa = cxl_ppr_set_dpa,
|
|
|
|
.get_nibble_mask = cxl_ppr_get_nibble_mask,
|
|
|
|
.set_nibble_mask = cxl_ppr_set_nibble_mask,
|
|
|
|
.do_repair = cxl_do_ppr,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cxl_memdev_soft_ppr_init(struct cxl_memdev *cxlmd,
|
|
|
|
struct edac_dev_feature *ras_feature,
|
|
|
|
u8 repair_inst)
|
|
|
|
{
|
|
|
|
struct cxl_ppr_context *cxl_sppr_ctx;
|
|
|
|
struct cxl_feat_entry *feat_entry;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
feat_entry = cxl_feature_info(to_cxlfs(cxlmd->cxlds),
|
|
|
|
&CXL_FEAT_SPPR_UUID);
|
|
|
|
if (IS_ERR(feat_entry))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!(le32_to_cpu(feat_entry->flags) & CXL_FEATURE_F_CHANGEABLE))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
cxl_sppr_ctx =
|
|
|
|
devm_kzalloc(&cxlmd->dev, sizeof(*cxl_sppr_ctx), GFP_KERNEL);
|
|
|
|
if (!cxl_sppr_ctx)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*cxl_sppr_ctx = (struct cxl_ppr_context){
|
|
|
|
.get_feat_size = le16_to_cpu(feat_entry->get_feat_size),
|
|
|
|
.set_feat_size = le16_to_cpu(feat_entry->set_feat_size),
|
|
|
|
.get_version = feat_entry->get_feat_ver,
|
|
|
|
.set_version = feat_entry->set_feat_ver,
|
|
|
|
.effects = le16_to_cpu(feat_entry->effects),
|
|
|
|
.cxlmd = cxlmd,
|
|
|
|
.repair_type = EDAC_REPAIR_PPR,
|
|
|
|
.persist_mode = 0,
|
|
|
|
.instance = repair_inst,
|
|
|
|
};
|
|
|
|
uuid_copy(&cxl_sppr_ctx->repair_uuid, &CXL_FEAT_SPPR_UUID);
|
|
|
|
|
|
|
|
ret = cxl_mem_ppr_get_attrbs(cxl_sppr_ctx);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ras_feature->ft_type = RAS_FEAT_MEM_REPAIR;
|
|
|
|
ras_feature->instance = cxl_sppr_ctx->instance;
|
|
|
|
ras_feature->mem_repair_ops = &cxl_sppr_ops;
|
|
|
|
ras_feature->ctx = cxl_sppr_ctx;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:41 +01:00
|
|
|
int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd)
|
|
|
|
{
|
|
|
|
struct edac_dev_feature ras_features[CXL_NR_EDAC_DEV_FEATURES];
|
|
|
|
int num_ras_features = 0;
|
2025-05-21 13:47:44 +01:00
|
|
|
u8 repair_inst = 0;
|
2025-05-21 13:47:41 +01:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_CXL_EDAC_SCRUB)) {
|
|
|
|
rc = cxl_memdev_scrub_init(cxlmd, &ras_features[num_ras_features], 0);
|
|
|
|
if (rc < 0 && rc != -EOPNOTSUPP)
|
|
|
|
return rc;
|
2025-05-21 13:47:42 +01:00
|
|
|
|
|
|
|
if (rc != -EOPNOTSUPP)
|
|
|
|
num_ras_features++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_CXL_EDAC_ECS)) {
|
|
|
|
rc = cxl_memdev_ecs_init(cxlmd, &ras_features[num_ras_features]);
|
|
|
|
if (rc < 0 && rc != -EOPNOTSUPP)
|
|
|
|
return rc;
|
2025-05-21 13:47:41 +01:00
|
|
|
|
|
|
|
if (rc != -EOPNOTSUPP)
|
|
|
|
num_ras_features++;
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:44 +01:00
|
|
|
if (IS_ENABLED(CONFIG_CXL_EDAC_MEM_REPAIR)) {
|
cxl/edac: Add CXL memory device memory sparing control feature
Memory sparing is defined as a repair function that replaces a portion of
memory with a portion of functional memory at that same DPA. The subclasses
for this operation vary in terms of the scope of the sparing being
performed. The cacheline sparing subclass refers to a sparing action that
can replace a full cacheline. Row sparing is provided as an alternative to
PPR sparing functions and its scope is that of a single DDR row.
As per CXL r3.2 Table 8-125 foot note 1. Memory sparing is preferred over
PPR when possible.
Bank sparing allows an entire bank to be replaced. Rank sparing is defined
as an operation in which an entire DDR rank is replaced.
Memory sparing maintenance operations may be supported by CXL devices
that implement CXL.mem protocol. A sparing maintenance operation requests
the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support memory sparing
features may implement sparing maintenance operations.
The host may issue a query command by setting query resources flag in the
input payload (CXL spec 3.2 Table 8-120) to determine availability of
sparing resources for a given address. In response to a query request,
the device shall report the resource availability by producing the memory
sparing event record (CXL spec 3.2 Table 8-60) in which the Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy
of the values specified in the request.
During the execution of a sparing maintenance operation, a CXL memory
device:
- may not retain data
- may not be able to process CXL.mem requests correctly.
These CXL memory device capabilities are specified by restriction flags
in the memory sparing feature readable attributes.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a memory sparing maintenance
operation by using DRAM event record, where the 'maintenance needed' flag
may set. The event record contains some of the DPA, Channel, Rank,
Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that
should be repaired. The userspace tool requests for maintenance operation
if the 'maintenance needed' flag set in the CXL DRAM error record.
CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing
maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature
discovery and configuration.
Add support for controlling CXL memory device memory sparing feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for memory sparing to the userspace. For example CXL memory
sparing control for the CXL mem0 device is exposed in
/sys/bus/edac/devices/cxl_mem0/mem_repairX/
Use case
========
1. CXL device identifies a failure in a memory component, report to
userspace in a CXL DRAM trace event with DPA and other attributes of
memory to repair such as channel, rank, nibble mask, bank Group,
bank, row, column, sub-channel.
2. Rasdaemon process the trace event and may issue query request in sysfs
check resources available for memory sparing if either of the following
conditions met.
- 'maintenance needed' flag set in the event record.
- 'threshold event' flag set for CVME threshold feature.
- When the number of corrected error reported on a CXL.mem media to the
userspace exceeds the threshold value for corrected error count defined
by the userspace policy.
3. Rasdaemon process the memory sparing trace event and issue repair
request for memory sparing.
Kernel CXL driver shall report memory sparing event record to the userspace
with the resource availability in order rasdaemon to process the event
record and issue a repair request in sysfs for the memory sparing operation
in the CXL device.
Note: Based on the feedbacks from the community 'query' sysfs attribute is
removed and reporting memory sparing error record to the userspace are not
supported. Instead userspace issues sparing operation and kernel does the
same to the CXL memory device, when 'maintenance needed' flag set in the
DRAM event record.
Add checks to ensure the memory to be repaired is offline and if online,
then originates from a CXL DRAM error record reported in the current boot
before requesting a memory sparing operation on the device.
Note: Tested memory sparing feature control with QEMU patch
"hw/cxl: Add emulation for memory sparing control feature"
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m5f38512a95670d75739f9dad3ee91b95c7f5c8d6
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-8-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:45 +01:00
|
|
|
for (int i = 0; i < CXL_MEM_SPARING_MAX; i++) {
|
|
|
|
rc = cxl_memdev_sparing_init(cxlmd,
|
|
|
|
&ras_features[num_ras_features],
|
|
|
|
&mem_sparing_desc[i], repair_inst);
|
|
|
|
if (rc == -EOPNOTSUPP)
|
|
|
|
continue;
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
repair_inst++;
|
|
|
|
num_ras_features++;
|
|
|
|
}
|
|
|
|
|
cxl/edac: Add CXL memory device soft PPR control feature
Post Package Repair (PPR) maintenance operations may be supported by CXL
devices that implement CXL.mem protocol. A PPR maintenance operation
requests the CXL device to perform a repair operation on its media.
For example, a CXL device with DRAM components that support PPR features
may implement PPR Maintenance operations. DRAM components may support two
types of PPR, hard PPR (hPPR), for a permanent row repair, and Soft PPR
(sPPR), for a temporary row repair. Soft PPR is much faster than hPPR,
but the repair is lost with a power cycle.
During the execution of a PPR Maintenance operation, a CXL memory device:
- May or may not retain data
- May or may not be able to process CXL.mem requests correctly, including
the ones that target the DPA involved in the repair.
These CXL Memory Device capabilities are specified by Restriction Flags
in the sPPR Feature and hPPR Feature.
Soft PPR maintenance operation may be executed at runtime, if data is
retained and CXL.mem requests are correctly processed. For CXL devices with
DRAM components, hPPR maintenance operation may be executed only at boot
because typically data may not be retained with hPPR maintenance operation.
When a CXL device identifies error on a memory component, the device
may inform the host about the need for a PPR maintenance operation by using
an Event Record, where the Maintenance Needed flag is set. The Event Record
specifies the DPA that should be repaired. A CXL device may not keep track
of the requests that have already been sent and the information on which
DPA should be repaired may be lost upon power cycle.
The userspace tool requests for maintenance operation if the number of
corrected error reported on a CXL.mem media exceeds error threshold.
CXL spec 3.2 section 8.2.10.7.1.2 describes the device's sPPR (soft PPR)
maintenance operation and section 8.2.10.7.1.3 describes the device's
hPPR (hard PPR) maintenance operation feature.
CXL spec 3.2 section 8.2.10.7.2.1 describes the sPPR feature discovery and
configuration.
CXL spec 3.2 section 8.2.10.7.2.2 describes the hPPR feature discovery and
configuration.
Add support for controlling CXL memory device soft PPR (sPPR) feature.
Register with EDAC driver, which gets the memory repair attr descriptors
from the EDAC memory repair driver and exposes sysfs repair control
attributes for PRR to the userspace. For example CXL PPR control for the
CXL mem0 device is exposed in /sys/bus/edac/devices/cxl_mem0/mem_repairX/
Add checks to ensure the memory to be repaired is offline and originates
from a CXL DRAM or CXL gen_media error record reported in the current boot,
before requesting a PPR operation on the device.
Note: Tested with QEMU patch for CXL PPR feature.
https://lore.kernel.org/linux-cxl/20250509172229.726-1-shiju.jose@huawei.com/T/#m70b2b010f43f7f4a6f9acee5ec9008498bf292c3
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Shiju Jose <shiju.jose@huawei.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20250521124749.817-9-shiju.jose@huawei.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-05-21 13:47:46 +01:00
|
|
|
rc = cxl_memdev_soft_ppr_init(cxlmd, &ras_features[num_ras_features],
|
|
|
|
repair_inst);
|
|
|
|
if (rc < 0 && rc != -EOPNOTSUPP)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (rc != -EOPNOTSUPP) {
|
|
|
|
repair_inst++;
|
|
|
|
num_ras_features++;
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:44 +01:00
|
|
|
if (repair_inst) {
|
|
|
|
struct cxl_mem_err_rec *array_rec =
|
|
|
|
devm_kzalloc(&cxlmd->dev, sizeof(*array_rec),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!array_rec)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
xa_init(&array_rec->rec_gen_media);
|
|
|
|
xa_init(&array_rec->rec_dram);
|
|
|
|
cxlmd->err_rec_array = array_rec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-21 13:47:41 +01:00
|
|
|
if (!num_ras_features)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
char *cxl_dev_name __free(kfree) =
|
|
|
|
kasprintf(GFP_KERNEL, "cxl_%s", dev_name(&cxlmd->dev));
|
|
|
|
if (!cxl_dev_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return edac_dev_register(&cxlmd->dev, cxl_dev_name, NULL,
|
|
|
|
num_ras_features, ras_features);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_memdev_edac_register, "CXL");
|
|
|
|
|
|
|
|
int devm_cxl_region_edac_register(struct cxl_region *cxlr)
|
|
|
|
{
|
|
|
|
struct edac_dev_feature ras_features[CXL_NR_EDAC_DEV_FEATURES];
|
|
|
|
int num_ras_features = 0;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_CXL_EDAC_SCRUB))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rc = cxl_region_scrub_init(cxlr, &ras_features[num_ras_features], 0);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
num_ras_features++;
|
|
|
|
|
|
|
|
char *cxl_dev_name __free(kfree) =
|
|
|
|
kasprintf(GFP_KERNEL, "cxl_%s", dev_name(&cxlr->dev));
|
|
|
|
if (!cxl_dev_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return edac_dev_register(&cxlr->dev, cxl_dev_name, NULL,
|
|
|
|
num_ras_features, ras_features);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_region_edac_register, "CXL");
|
2025-05-21 13:47:44 +01:00
|
|
|
|
|
|
|
void devm_cxl_memdev_edac_release(struct cxl_memdev *cxlmd)
|
|
|
|
{
|
|
|
|
struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array;
|
|
|
|
struct cxl_event_gen_media *rec_gen_media;
|
|
|
|
struct cxl_event_dram *rec_dram;
|
|
|
|
unsigned long index;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_CXL_EDAC_MEM_REPAIR) || !array_rec)
|
|
|
|
return;
|
|
|
|
|
|
|
|
xa_for_each(&array_rec->rec_dram, index, rec_dram)
|
|
|
|
kfree(rec_dram);
|
|
|
|
xa_destroy(&array_rec->rec_dram);
|
|
|
|
|
|
|
|
xa_for_each(&array_rec->rec_gen_media, index, rec_gen_media)
|
|
|
|
kfree(rec_gen_media);
|
|
|
|
xa_destroy(&array_rec->rec_gen_media);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_memdev_edac_release, "CXL");
|