summaryrefslogtreecommitdiff
path: root/drivers/edac/edac_device.c
diff options
context:
space:
mode:
authorShiju Jose <shiju.jose@huawei.com>2025-02-12 14:36:40 +0000
committerBorislav Petkov (AMD) <bp@alien8.de>2025-02-25 15:39:09 +0100
commitf90b738166fe909df48de6a03744ddfbad5002f8 (patch)
treec0886a9d910aef8ecae11aec5e90aa35af5e5e36 /drivers/edac/edac_device.c
parentdb99ea5f2c0361c8fc2878792e97c7b67c811bd0 (diff)
EDAC: Add scrub control feature
Add a scrub control to manage memory scrubbers in the system. Devices with a scrub feature register with the EDAC device driver which retrieves the scrub descriptor from the scrub driver and exposes the control attributes for a instance to userspace at /sys/bus/edac/devices/<dev-name>/scrubX/. The common sysfs scrub control interface abstracts the control of arbitrary scrubbing functionality into a common set of functions. The attribute nodes are only present if the client driver has implemented the corresponding attribute callback function and passed the operations to the device driver during registration. [ bp: Massage commit message, docs and code, simplify text a bit. Integrate fixup for: https://lore.kernel.org/r/202502251009.0sGkolEJ-lkp@intel.com Reported-by: kernel test robot <lkp@intel.com> Reported-by: Dan Carpenter <dan.carpenter@linaro.org> ] Co-developed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Shiju Jose <shiju.jose@huawei.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Tested-by: Daniel Ferguson <danielf@os.amperecomputing.com> Tested-by: Fan Ni <fan.ni@samsung.com> Link: https://lore.kernel.org/r/20250212143654.1893-3-shiju.jose@huawei.com
Diffstat (limited to 'drivers/edac/edac_device.c')
-rw-r--r--drivers/edac/edac_device.c40
1 files changed, 36 insertions, 4 deletions
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index 6af0893cadc9..54c1e2d024ac 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -575,6 +575,7 @@ static void edac_dev_release(struct device *dev)
{
struct edac_dev_feat_ctx *ctx = container_of(dev, struct edac_dev_feat_ctx, dev);
+ kfree(ctx->scrub);
kfree(ctx->dev.groups);
kfree(ctx);
}
@@ -610,9 +611,11 @@ int edac_dev_register(struct device *parent, char *name,
const struct edac_dev_feature *ras_features)
{
const struct attribute_group **ras_attr_groups;
+ struct edac_dev_data *dev_data;
struct edac_dev_feat_ctx *ctx;
int attr_gcnt = 0;
int ret = -ENOMEM;
+ int scrub_cnt = 0;
int feat;
if (!parent || !name || !num_features || !ras_features)
@@ -621,7 +624,10 @@ int edac_dev_register(struct device *parent, char *name,
/* Double parse to make space for attributes */
for (feat = 0; feat < num_features; feat++) {
switch (ras_features[feat].ft_type) {
- /* Add feature specific code */
+ case RAS_FEAT_SCRUB:
+ attr_gcnt++;
+ scrub_cnt++;
+ break;
default:
return -EINVAL;
}
@@ -635,13 +641,37 @@ int edac_dev_register(struct device *parent, char *name,
if (!ras_attr_groups)
goto ctx_free;
+ if (scrub_cnt) {
+ ctx->scrub = kcalloc(scrub_cnt, sizeof(*ctx->scrub), GFP_KERNEL);
+ if (!ctx->scrub)
+ goto groups_free;
+ }
+
attr_gcnt = 0;
+ scrub_cnt = 0;
for (feat = 0; feat < num_features; feat++, ras_features++) {
switch (ras_features->ft_type) {
- /* Add feature specific code */
+ case RAS_FEAT_SCRUB:
+ if (!ras_features->scrub_ops || scrub_cnt != ras_features->instance) {
+ ret = -EINVAL;
+ goto data_mem_free;
+ }
+
+ dev_data = &ctx->scrub[scrub_cnt];
+ dev_data->instance = scrub_cnt;
+ dev_data->scrub_ops = ras_features->scrub_ops;
+ dev_data->private = ras_features->ctx;
+ ret = edac_scrub_get_desc(parent, &ras_attr_groups[attr_gcnt],
+ ras_features->instance);
+ if (ret)
+ goto data_mem_free;
+
+ scrub_cnt++;
+ attr_gcnt++;
+ break;
default:
ret = -EINVAL;
- goto groups_free;
+ goto data_mem_free;
}
}
@@ -654,7 +684,7 @@ int edac_dev_register(struct device *parent, char *name,
ret = dev_set_name(&ctx->dev, name);
if (ret)
- goto groups_free;
+ goto data_mem_free;
ret = device_register(&ctx->dev);
if (ret) {
@@ -664,6 +694,8 @@ int edac_dev_register(struct device *parent, char *name,
return devm_add_action_or_reset(parent, edac_dev_unreg, &ctx->dev);
+data_mem_free:
+ kfree(ctx->scrub);
groups_free:
kfree(ras_attr_groups);
ctx_free: