Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Shiju Jose and committed by
Borislav Petkov (AMD)
f90b7381 db99ea5f

+638 -4
+69
Documentation/ABI/testing/sysfs-edac-scrub
··· 1 + What: /sys/bus/edac/devices/<dev-name>/scrubX 2 + Date: March 2025 3 + KernelVersion: 6.15 4 + Contact: linux-edac@vger.kernel.org 5 + Description: 6 + The sysfs EDAC bus devices /<dev-name>/scrubX subdirectory 7 + belongs to an instance of memory scrub control feature, 8 + where <dev-name> directory corresponds to a device/memory 9 + region registered with the EDAC device driver for the 10 + scrub control feature. 11 + 12 + The sysfs scrub attr nodes are only present if the parent 13 + driver has implemented the corresponding attr callback 14 + function and provided the necessary operations to the EDAC 15 + device driver during registration. 16 + 17 + What: /sys/bus/edac/devices/<dev-name>/scrubX/addr 18 + Date: March 2025 19 + KernelVersion: 6.15 20 + Contact: linux-edac@vger.kernel.org 21 + Description: 22 + (RW) The base address of the memory region to be scrubbed 23 + for on-demand scrubbing. Setting address starts scrubbing. 24 + The size must be set before that. 25 + 26 + The readback addr value is non-zero if the requested 27 + on-demand scrubbing is in progress, zero otherwise. 28 + 29 + What: /sys/bus/edac/devices/<dev-name>/scrubX/size 30 + Date: March 2025 31 + KernelVersion: 6.15 32 + Contact: linux-edac@vger.kernel.org 33 + Description: 34 + (RW) The size of the memory region to be scrubbed 35 + (on-demand scrubbing). 36 + 37 + What: /sys/bus/edac/devices/<dev-name>/scrubX/enable_background 38 + Date: March 2025 39 + KernelVersion: 6.15 40 + Contact: linux-edac@vger.kernel.org 41 + Description: 42 + (RW) Start/Stop background (patrol) scrubbing if supported. 43 + 44 + What: /sys/bus/edac/devices/<dev-name>/scrubX/min_cycle_duration 45 + Date: March 2025 46 + KernelVersion: 6.15 47 + Contact: linux-edac@vger.kernel.org 48 + Description: 49 + (RO) Supported minimum scrub cycle duration in seconds 50 + by the memory scrubber. 51 + 52 + What: /sys/bus/edac/devices/<dev-name>/scrubX/max_cycle_duration 53 + Date: March 2025 54 + KernelVersion: 6.15 55 + Contact: linux-edac@vger.kernel.org 56 + Description: 57 + (RO) Supported maximum scrub cycle duration in seconds 58 + by the memory scrubber. 59 + 60 + What: /sys/bus/edac/devices/<dev-name>/scrubX/current_cycle_duration 61 + Date: March 2025 62 + KernelVersion: 6.15 63 + Contact: linux-edac@vger.kernel.org 64 + Description: 65 + (RW) The current scrub cycle duration in seconds and must be 66 + within the supported range by the memory scrubber. 67 + 68 + Scrub has an overhead when running and that may want to be 69 + reduced by taking longer to do it.
+6
Documentation/edac/features.rst
··· 91 91 3. RAS dynamic feature controller - Userspace sample modules in rasdaemon for 92 92 dynamic scrub/repair control to issue scrubbing/repair when excess number 93 93 of corrected memory errors are reported in a short span of time. 94 + 95 + RAS features 96 + ------------ 97 + 1. Memory Scrub 98 + 99 + Memory scrub features are documented in `Documentation/edac/scrub.rst`.
+1
Documentation/edac/index.rst
··· 8 8 :maxdepth: 1 9 9 10 10 features 11 + scrub
+264
Documentation/edac/scrub.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.2-no-invariants-or-later 2 + 3 + ============= 4 + Scrub Control 5 + ============= 6 + 7 + Copyright (c) 2024-2025 HiSilicon Limited. 8 + 9 + :Author: Shiju Jose <shiju.jose@huawei.com> 10 + :License: The GNU Free Documentation License, Version 1.2 without 11 + Invariant Sections, Front-Cover Texts nor Back-Cover Texts. 12 + (dual licensed under the GPL v2) 13 + 14 + - Written for: 6.15 15 + 16 + Introduction 17 + ------------ 18 + 19 + Increasing DRAM size and cost have made memory subsystem reliability an 20 + important concern. These modules are used where potentially corrupted data 21 + could cause expensive or fatal issues. Memory errors are among the top 22 + hardware failures that cause server and workload crashes. 23 + 24 + Memory scrubbing is a feature where an ECC (Error-Correcting Code) engine 25 + reads data from each memory media location, corrects if necessary and writes 26 + the corrected data back to the same memory media location. 27 + 28 + DIMMs can be scrubbed at a configurable rate to detect uncorrected memory 29 + errors and attempt recovery from detected errors, providing the following 30 + benefits: 31 + 32 + 1. Proactively scrubbing DIMMs reduces the chance of a correctable error 33 + becoming uncorrectable. 34 + 35 + 2. When detected, uncorrected errors caught in unallocated memory pages are 36 + isolated and prevented from being allocated to an application or the OS. 37 + 38 + 3. This reduces the likelihood of software or hardware products encountering 39 + memory errors. 40 + 41 + 4. The additional data on failures in memory may be used to build up 42 + statistics that are later used to decide whether to use memory repair 43 + technologies such as Post Package Repair or Sparing. 44 + 45 + There are 2 types of memory scrubbing: 46 + 47 + 1. Background (patrol) scrubbing while the DRAM is otherwise idle. 48 + 49 + 2. On-demand scrubbing for a specific address range or region of memory. 50 + 51 + Several types of interfaces to hardware memory scrubbers have been 52 + identified, such as CXL memory device patrol scrub, CXL DDR5 ECS, ACPI 53 + RAS2 memory scrubbing, and ACPI NVDIMM ARS (Address Range Scrub). 54 + 55 + The control mechanisms vary across different memory scrubbers. To enable 56 + standardized userspace tooling, there is a need to present these controls 57 + through a standardized ABI. 58 + 59 + A generic memory EDAC scrub control allows users to manage underlying 60 + scrubbers in the system through a standardized sysfs control interface. It 61 + abstracts the management of various scrubbing functionalities into a unified 62 + set of functions. 63 + 64 + Use cases of common scrub control feature 65 + ----------------------------------------- 66 + 67 + 1. Several types of interfaces for hardware memory scrubbers have been 68 + identified, including the CXL memory device patrol scrub, CXL DDR5 ECS, 69 + ACPI RAS2 memory scrubbing features, ACPI NVDIMM ARS (Address Range Scrub), 70 + and software-based memory scrubbers. 71 + 72 + Of the identified interfaces to hardware memory scrubbers some support 73 + control over patrol (background) scrubbing (e.g., ACPI RAS2, CXL) and/or 74 + on-demand scrubbing (e.g., ACPI RAS2, ACPI ARS). However, the scrub control 75 + interfaces vary between memory scrubbers, highlighting the need for 76 + a standardized, generic sysfs scrub control interface that is accessible to 77 + userspace for administration and use by scripts/tools. 78 + 79 + 2. User-space scrub controls allow users to disable scrubbing if necessary, 80 + for example, to disable background patrol scrubbing or adjust the scrub 81 + rate for performance-aware operations where background activities need to 82 + be minimized or disabled. 83 + 84 + 3. User-space tools enable on-demand scrubbing for specific address ranges, 85 + provided that the scrubber supports this functionality. 86 + 87 + 4. User-space tools can also control memory DIMM scrubbing at a configurable 88 + scrub rate via sysfs scrub controls. This approach offers several benefits: 89 + 90 + 4.1. Detects uncorrectable memory errors early, before user access to affected 91 + memory, helping facilitate recovery. 92 + 93 + 4.2. Reduces the likelihood of correctable errors developing into uncorrectable 94 + errors. 95 + 96 + 5. Policy control for hotplugged memory is necessary because there may not 97 + be a system-wide BIOS or similar control to manage scrub settings for a CXL 98 + device added after boot. Determining these settings is a policy decision, 99 + balancing reliability against performance, so userspace should control it. 100 + Therefore, a unified interface is recommended for handling this function in 101 + a way that aligns with other similar interfaces, rather than creating a 102 + separate one. 103 + 104 + Scrubbing features 105 + ------------------ 106 + 107 + CXL Memory Scrubbing features 108 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 109 + 110 + CXL spec r3.1 [1]_ section 8.2.9.9.11.1 describes the memory device patrol 111 + scrub control feature. The device patrol scrub proactively locates and makes 112 + corrections to errors in regular cycle. The patrol scrub control allows the 113 + userspace request to change CXL patrol scrubber's configurations. 114 + 115 + The patrol scrub control allows the requester to specify the number of 116 + hours in which the patrol scrub cycles must be completed, provided that 117 + the requested scrub rate must be within the supported range of the 118 + scrub rate that the device is capable of. In the CXL driver, the 119 + number of seconds per scrub cycles, which user requests via sysfs, is 120 + rescaled to hours per scrub cycles. 121 + 122 + In addition, they allow the host to disable the feature in case it interferes 123 + with performance-aware operations which require the background operations to 124 + be turned off. 125 + 126 + Error Check Scrub (ECS) 127 + ~~~~~~~~~~~~~~~~~~~~~~~ 128 + 129 + CXL spec r3.1 [1]_ section 8.2.9.9.11.2 describes Error Check Scrub (ECS) 130 + - a feature defined in the JEDEC DDR5 SDRAM Specification (JESD79-5) and 131 + allowing DRAM to internally read, correct single-bit errors, and write back 132 + corrected data bits to the DRAM array while providing transparency to error 133 + counts. 134 + 135 + The DDR5 device contains number of memory media Field Replaceable Units (FRU) 136 + per device. The DDR5 ECS feature and thus the ECS control driver supports 137 + configuring the ECS parameters per FRU. 138 + 139 + ACPI RAS2 Hardware-based Memory Scrubbing 140 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 141 + 142 + ACPI spec 6.5 [2]_ section 5.2.21 ACPI RAS2 describes an ACPI RAS2 table 143 + which provides interfaces for platform RAS features and supports independent 144 + RAS controls and capabilities for a given RAS feature for multiple instances 145 + of the same component in a given system. 146 + 147 + Memory RAS features apply to RAS capabilities, controls and operations that 148 + are specific to memory. RAS2 PCC sub-spaces for memory-specific RAS features 149 + have a Feature Type of 0x00 (Memory). 150 + 151 + The platform can use the hardware-based memory scrubbing feature to expose 152 + controls and capabilities associated with hardware-based memory scrub 153 + engines. The RAS2 memory scrubbing feature supports as per spec, 154 + 155 + 1. Independent memory scrubbing controls for each NUMA domain, identified 156 + using its proximity domain. 157 + 158 + 2. Provision for background (patrol) scrubbing of the entire memory system, 159 + as well as on-demand scrubbing for a specific region of memory. 160 + 161 + ACPI Address Range Scrubbing (ARS) 162 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 163 + 164 + ACPI spec 6.5 [2]_ section 9.19.7.2 describes Address Range Scrubbing (ARS). 165 + ARS allows the platform to communicate memory errors to system software. 166 + This capability allows system software to prevent accesses to addresses with 167 + uncorrectable errors in memory. ARS functions manage all NVDIMMs present in 168 + the system. Only one scrub can be in progress system wide at any given time. 169 + 170 + The following functions are supported as per the specification: 171 + 172 + 1. Query ARS Capabilities for a given address range, indicates platform 173 + supports the ACPI NVDIMM Root Device Unconsumed Error Notification. 174 + 175 + 2. Start ARS triggers an Address Range Scrub for the given memory range. 176 + Address scrubbing can be done for volatile or persistent memory, or both. 177 + 178 + 3. Query ARS Status command allows software to get the status of ARS, 179 + including the progress of ARS and ARS error record. 180 + 181 + 4. Clear Uncorrectable Error. 182 + 183 + 5. Translate SPA 184 + 185 + 6. ARS Error Inject etc. 186 + 187 + The kernel supports an existing control for ARS and ARS is currently not 188 + supported in EDAC. 189 + 190 + .. [1] https://computeexpresslink.org/cxl-specification/ 191 + 192 + .. [2] https://uefi.org/specs/ACPI/6.5/ 193 + 194 + Comparison of various scrubbing features 195 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 196 + 197 + +--------------+-----------+-----------+-----------+-----------+ 198 + | | ACPI | CXL patrol| CXL ECS | ARS | 199 + | Name | RAS2 | scrub | | | 200 + +--------------+-----------+-----------+-----------+-----------+ 201 + | | | | | | 202 + | On-demand | Supported | No | No | Supported | 203 + | Scrubbing | | | | | 204 + | | | | | | 205 + +--------------+-----------+-----------+-----------+-----------+ 206 + | | | | | | 207 + | Background | Supported | Supported | Supported | No | 208 + | scrubbing | | | | | 209 + | | | | | | 210 + +--------------+-----------+-----------+-----------+-----------+ 211 + | | | | | | 212 + | Mode of | Scrub ctrl| per device| per memory| Unknown | 213 + | scrubbing | per NUMA | | media | | 214 + | | domain. | | | | 215 + +--------------+-----------+-----------+-----------+-----------+ 216 + | | | | | | 217 + | Query scrub | Supported | Supported | Supported | Supported | 218 + | capabilities | | | | | 219 + | | | | | | 220 + +--------------+-----------+-----------+-----------+-----------+ 221 + | | | | | | 222 + | Setting | Supported | No | No | Supported | 223 + | address range| | | | | 224 + | | | | | | 225 + +--------------+-----------+-----------+-----------+-----------+ 226 + | | | | | | 227 + | Setting | Supported | Supported | No | No | 228 + | scrub rate | | | | | 229 + | | | | | | 230 + +--------------+-----------+-----------+-----------+-----------+ 231 + | | | | | | 232 + | Unit for | Not | in hours | No | No | 233 + | scrub rate | Defined | | | | 234 + | | | | | | 235 + +--------------+-----------+-----------+-----------+-----------+ 236 + | | Supported | | | | 237 + | Scrub | on-demand | No | No | Supported | 238 + | status/ | scrubbing | | | | 239 + | Completion | only | | | | 240 + +--------------+-----------+-----------+-----------+-----------+ 241 + | UC error | |CXL general|CXL general| ACPI UCE | 242 + | reporting | Exception |media/DRAM |media/DRAM | notify and| 243 + | | |event/media|event/media| query | 244 + | | |scan? |scan? | ARS status| 245 + +--------------+-----------+-----------+-----------+-----------+ 246 + | | | | | | 247 + | Support for | Supported | Supported | Supported | No | 248 + | EDAC control | | | | | 249 + | | | | | | 250 + +--------------+-----------+-----------+-----------+-----------+ 251 + 252 + The File System 253 + --------------- 254 + 255 + The control attributes of a registered scrubber instance could be 256 + accessed in: 257 + 258 + /sys/bus/edac/devices/<dev-name>/scrubX/ 259 + 260 + sysfs 261 + ----- 262 + 263 + Sysfs files are documented in 264 + `Documentation/ABI/testing/sysfs-edac-scrub`
+9
drivers/edac/Kconfig
··· 75 75 76 76 In doubt, say 'Y'. 77 77 78 + config EDAC_SCRUB 79 + bool "EDAC scrub feature" 80 + help 81 + The EDAC scrub feature is optional and is designed to control the 82 + memory scrubbers in the system. The common sysfs scrub interface 83 + abstracts the control of various arbitrary scrubbing functionalities 84 + into a unified set of functions. 85 + Say 'y/n' to enable/disable EDAC scrub feature. 86 + 78 87 config EDAC_AMD64 79 88 tristate "AMD64 (Opteron, Athlon64)" 80 89 depends on AMD_NB && EDAC_DECODE_MCE
+1
drivers/edac/Makefile
··· 12 12 edac_core-y += edac_module.o edac_device_sysfs.o wq.o 13 13 14 14 edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o 15 + edac_core-$(CONFIG_EDAC_SCRUB) += scrub.o 15 16 16 17 ifdef CONFIG_PCI 17 18 edac_core-y += edac_pci.o edac_pci_sysfs.o
+36 -4
drivers/edac/edac_device.c
··· 575 575 { 576 576 struct edac_dev_feat_ctx *ctx = container_of(dev, struct edac_dev_feat_ctx, dev); 577 577 578 + kfree(ctx->scrub); 578 579 kfree(ctx->dev.groups); 579 580 kfree(ctx); 580 581 } ··· 611 610 const struct edac_dev_feature *ras_features) 612 611 { 613 612 const struct attribute_group **ras_attr_groups; 613 + struct edac_dev_data *dev_data; 614 614 struct edac_dev_feat_ctx *ctx; 615 615 int attr_gcnt = 0; 616 616 int ret = -ENOMEM; 617 + int scrub_cnt = 0; 617 618 int feat; 618 619 619 620 if (!parent || !name || !num_features || !ras_features) ··· 624 621 /* Double parse to make space for attributes */ 625 622 for (feat = 0; feat < num_features; feat++) { 626 623 switch (ras_features[feat].ft_type) { 627 - /* Add feature specific code */ 624 + case RAS_FEAT_SCRUB: 625 + attr_gcnt++; 626 + scrub_cnt++; 627 + break; 628 628 default: 629 629 return -EINVAL; 630 630 } ··· 641 635 if (!ras_attr_groups) 642 636 goto ctx_free; 643 637 638 + if (scrub_cnt) { 639 + ctx->scrub = kcalloc(scrub_cnt, sizeof(*ctx->scrub), GFP_KERNEL); 640 + if (!ctx->scrub) 641 + goto groups_free; 642 + } 643 + 644 644 attr_gcnt = 0; 645 + scrub_cnt = 0; 645 646 for (feat = 0; feat < num_features; feat++, ras_features++) { 646 647 switch (ras_features->ft_type) { 647 - /* Add feature specific code */ 648 + case RAS_FEAT_SCRUB: 649 + if (!ras_features->scrub_ops || scrub_cnt != ras_features->instance) { 650 + ret = -EINVAL; 651 + goto data_mem_free; 652 + } 653 + 654 + dev_data = &ctx->scrub[scrub_cnt]; 655 + dev_data->instance = scrub_cnt; 656 + dev_data->scrub_ops = ras_features->scrub_ops; 657 + dev_data->private = ras_features->ctx; 658 + ret = edac_scrub_get_desc(parent, &ras_attr_groups[attr_gcnt], 659 + ras_features->instance); 660 + if (ret) 661 + goto data_mem_free; 662 + 663 + scrub_cnt++; 664 + attr_gcnt++; 665 + break; 648 666 default: 649 667 ret = -EINVAL; 650 - goto groups_free; 668 + goto data_mem_free; 651 669 } 652 670 } 653 671 ··· 684 654 685 655 ret = dev_set_name(&ctx->dev, name); 686 656 if (ret) 687 - goto groups_free; 657 + goto data_mem_free; 688 658 689 659 ret = device_register(&ctx->dev); 690 660 if (ret) { ··· 694 664 695 665 return devm_add_action_or_reset(parent, edac_dev_unreg, &ctx->dev); 696 666 667 + data_mem_free: 668 + kfree(ctx->scrub); 697 669 groups_free: 698 670 kfree(ras_attr_groups); 699 671 ctx_free:
+209
drivers/edac/scrub.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * The generic EDAC scrub driver controls the memory scrubbers in the 4 + * system. The common sysfs scrub interface abstracts the control of 5 + * various arbitrary scrubbing functionalities into a unified set of 6 + * functions. 7 + * 8 + * Copyright (c) 2024-2025 HiSilicon Limited. 9 + */ 10 + 11 + #include <linux/edac.h> 12 + 13 + enum edac_scrub_attributes { 14 + SCRUB_ADDRESS, 15 + SCRUB_SIZE, 16 + SCRUB_ENABLE_BACKGROUND, 17 + SCRUB_MIN_CYCLE_DURATION, 18 + SCRUB_MAX_CYCLE_DURATION, 19 + SCRUB_CUR_CYCLE_DURATION, 20 + SCRUB_MAX_ATTRS 21 + }; 22 + 23 + struct edac_scrub_dev_attr { 24 + struct device_attribute dev_attr; 25 + u8 instance; 26 + }; 27 + 28 + struct edac_scrub_context { 29 + char name[EDAC_FEAT_NAME_LEN]; 30 + struct edac_scrub_dev_attr scrub_dev_attr[SCRUB_MAX_ATTRS]; 31 + struct attribute *scrub_attrs[SCRUB_MAX_ATTRS + 1]; 32 + struct attribute_group group; 33 + }; 34 + 35 + #define TO_SCRUB_DEV_ATTR(_dev_attr) \ 36 + container_of(_dev_attr, struct edac_scrub_dev_attr, dev_attr) 37 + 38 + #define EDAC_SCRUB_ATTR_SHOW(attrib, cb, type, format) \ 39 + static ssize_t attrib##_show(struct device *ras_feat_dev, \ 40 + struct device_attribute *attr, char *buf) \ 41 + { \ 42 + u8 inst = TO_SCRUB_DEV_ATTR(attr)->instance; \ 43 + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); \ 44 + const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops; \ 45 + type data; \ 46 + int ret; \ 47 + \ 48 + ret = ops->cb(ras_feat_dev->parent, ctx->scrub[inst].private, &data); \ 49 + if (ret) \ 50 + return ret; \ 51 + \ 52 + return sysfs_emit(buf, format, data); \ 53 + } 54 + 55 + EDAC_SCRUB_ATTR_SHOW(addr, read_addr, u64, "0x%llx\n") 56 + EDAC_SCRUB_ATTR_SHOW(size, read_size, u64, "0x%llx\n") 57 + EDAC_SCRUB_ATTR_SHOW(enable_background, get_enabled_bg, bool, "%u\n") 58 + EDAC_SCRUB_ATTR_SHOW(min_cycle_duration, get_min_cycle, u32, "%u\n") 59 + EDAC_SCRUB_ATTR_SHOW(max_cycle_duration, get_max_cycle, u32, "%u\n") 60 + EDAC_SCRUB_ATTR_SHOW(current_cycle_duration, get_cycle_duration, u32, "%u\n") 61 + 62 + #define EDAC_SCRUB_ATTR_STORE(attrib, cb, type, conv_func) \ 63 + static ssize_t attrib##_store(struct device *ras_feat_dev, \ 64 + struct device_attribute *attr, \ 65 + const char *buf, size_t len) \ 66 + { \ 67 + u8 inst = TO_SCRUB_DEV_ATTR(attr)->instance; \ 68 + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); \ 69 + const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops; \ 70 + type data; \ 71 + int ret; \ 72 + \ 73 + ret = conv_func(buf, 0, &data); \ 74 + if (ret < 0) \ 75 + return ret; \ 76 + \ 77 + ret = ops->cb(ras_feat_dev->parent, ctx->scrub[inst].private, data); \ 78 + if (ret) \ 79 + return ret; \ 80 + \ 81 + return len; \ 82 + } 83 + 84 + EDAC_SCRUB_ATTR_STORE(addr, write_addr, u64, kstrtou64) 85 + EDAC_SCRUB_ATTR_STORE(size, write_size, u64, kstrtou64) 86 + EDAC_SCRUB_ATTR_STORE(enable_background, set_enabled_bg, unsigned long, kstrtoul) 87 + EDAC_SCRUB_ATTR_STORE(current_cycle_duration, set_cycle_duration, unsigned long, kstrtoul) 88 + 89 + static umode_t scrub_attr_visible(struct kobject *kobj, struct attribute *a, int attr_id) 90 + { 91 + struct device *ras_feat_dev = kobj_to_dev(kobj); 92 + struct device_attribute *dev_attr = container_of(a, struct device_attribute, attr); 93 + u8 inst = TO_SCRUB_DEV_ATTR(dev_attr)->instance; 94 + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); 95 + const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops; 96 + 97 + switch (attr_id) { 98 + case SCRUB_ADDRESS: 99 + if (ops->read_addr) { 100 + if (ops->write_addr) 101 + return a->mode; 102 + else 103 + return 0444; 104 + } 105 + break; 106 + case SCRUB_SIZE: 107 + if (ops->read_size) { 108 + if (ops->write_size) 109 + return a->mode; 110 + else 111 + return 0444; 112 + } 113 + break; 114 + case SCRUB_ENABLE_BACKGROUND: 115 + if (ops->get_enabled_bg) { 116 + if (ops->set_enabled_bg) 117 + return a->mode; 118 + else 119 + return 0444; 120 + } 121 + break; 122 + case SCRUB_MIN_CYCLE_DURATION: 123 + if (ops->get_min_cycle) 124 + return a->mode; 125 + break; 126 + case SCRUB_MAX_CYCLE_DURATION: 127 + if (ops->get_max_cycle) 128 + return a->mode; 129 + break; 130 + case SCRUB_CUR_CYCLE_DURATION: 131 + if (ops->get_cycle_duration) { 132 + if (ops->set_cycle_duration) 133 + return a->mode; 134 + else 135 + return 0444; 136 + } 137 + break; 138 + default: 139 + break; 140 + } 141 + 142 + return 0; 143 + } 144 + 145 + #define EDAC_SCRUB_ATTR_RO(_name, _instance) \ 146 + ((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_RO(_name), \ 147 + .instance = _instance }) 148 + 149 + #define EDAC_SCRUB_ATTR_WO(_name, _instance) \ 150 + ((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_WO(_name), \ 151 + .instance = _instance }) 152 + 153 + #define EDAC_SCRUB_ATTR_RW(_name, _instance) \ 154 + ((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_RW(_name), \ 155 + .instance = _instance }) 156 + 157 + static int scrub_create_desc(struct device *scrub_dev, 158 + const struct attribute_group **attr_groups, u8 instance) 159 + { 160 + struct edac_scrub_context *scrub_ctx; 161 + struct attribute_group *group; 162 + int i; 163 + struct edac_scrub_dev_attr dev_attr[] = { 164 + [SCRUB_ADDRESS] = EDAC_SCRUB_ATTR_RW(addr, instance), 165 + [SCRUB_SIZE] = EDAC_SCRUB_ATTR_RW(size, instance), 166 + [SCRUB_ENABLE_BACKGROUND] = EDAC_SCRUB_ATTR_RW(enable_background, instance), 167 + [SCRUB_MIN_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RO(min_cycle_duration, instance), 168 + [SCRUB_MAX_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RO(max_cycle_duration, instance), 169 + [SCRUB_CUR_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RW(current_cycle_duration, instance) 170 + }; 171 + 172 + scrub_ctx = devm_kzalloc(scrub_dev, sizeof(*scrub_ctx), GFP_KERNEL); 173 + if (!scrub_ctx) 174 + return -ENOMEM; 175 + 176 + group = &scrub_ctx->group; 177 + for (i = 0; i < SCRUB_MAX_ATTRS; i++) { 178 + memcpy(&scrub_ctx->scrub_dev_attr[i], &dev_attr[i], sizeof(dev_attr[i])); 179 + scrub_ctx->scrub_attrs[i] = &scrub_ctx->scrub_dev_attr[i].dev_attr.attr; 180 + } 181 + sprintf(scrub_ctx->name, "%s%d", "scrub", instance); 182 + group->name = scrub_ctx->name; 183 + group->attrs = scrub_ctx->scrub_attrs; 184 + group->is_visible = scrub_attr_visible; 185 + 186 + attr_groups[0] = group; 187 + 188 + return 0; 189 + } 190 + 191 + /** 192 + * edac_scrub_get_desc - get EDAC scrub descriptors 193 + * @scrub_dev: client device, with scrub support 194 + * @attr_groups: pointer to attribute group container 195 + * @instance: device's scrub instance number. 196 + * 197 + * Return: 198 + * * %0 - Success. 199 + * * %-EINVAL - Invalid parameters passed. 200 + * * %-ENOMEM - Dynamic memory allocation failed. 201 + */ 202 + int edac_scrub_get_desc(struct device *scrub_dev, 203 + const struct attribute_group **attr_groups, u8 instance) 204 + { 205 + if (!scrub_dev || !attr_groups) 206 + return -EINVAL; 207 + 208 + return scrub_create_desc(scrub_dev, attr_groups, instance); 209 + }
+43
include/linux/edac.h
··· 662 662 return mci->dimms[index]; 663 663 } 664 664 665 + #define EDAC_FEAT_NAME_LEN 128 666 + 665 667 /* RAS feature type */ 666 668 enum edac_dev_feat { 669 + RAS_FEAT_SCRUB, 667 670 RAS_FEAT_MAX 668 671 }; 669 672 673 + /** 674 + * struct edac_scrub_ops - scrub device operations (all elements optional) 675 + * @read_addr: read base address of scrubbing range. 676 + * @read_size: read offset of scrubbing range. 677 + * @write_addr: set base address of the scrubbing range. 678 + * @write_size: set offset of the scrubbing range. 679 + * @get_enabled_bg: check if currently performing background scrub. 680 + * @set_enabled_bg: start or stop a bg-scrub. 681 + * @get_min_cycle: get minimum supported scrub cycle duration in seconds. 682 + * @get_max_cycle: get maximum supported scrub cycle duration in seconds. 683 + * @get_cycle_duration: get current scrub cycle duration in seconds. 684 + * @set_cycle_duration: set current scrub cycle duration in seconds. 685 + */ 686 + struct edac_scrub_ops { 687 + int (*read_addr)(struct device *dev, void *drv_data, u64 *base); 688 + int (*read_size)(struct device *dev, void *drv_data, u64 *size); 689 + int (*write_addr)(struct device *dev, void *drv_data, u64 base); 690 + int (*write_size)(struct device *dev, void *drv_data, u64 size); 691 + int (*get_enabled_bg)(struct device *dev, void *drv_data, bool *enable); 692 + int (*set_enabled_bg)(struct device *dev, void *drv_data, bool enable); 693 + int (*get_min_cycle)(struct device *dev, void *drv_data, u32 *min); 694 + int (*get_max_cycle)(struct device *dev, void *drv_data, u32 *max); 695 + int (*get_cycle_duration)(struct device *dev, void *drv_data, u32 *cycle); 696 + int (*set_cycle_duration)(struct device *dev, void *drv_data, u32 cycle); 697 + }; 698 + 699 + #if IS_ENABLED(CONFIG_EDAC_SCRUB) 700 + int edac_scrub_get_desc(struct device *scrub_dev, 701 + const struct attribute_group **attr_groups, 702 + u8 instance); 703 + #else 704 + static inline int edac_scrub_get_desc(struct device *scrub_dev, 705 + const struct attribute_group **attr_groups, 706 + u8 instance) 707 + { return -EOPNOTSUPP; } 708 + #endif /* CONFIG_EDAC_SCRUB */ 709 + 670 710 /* EDAC device feature information structure */ 671 711 struct edac_dev_data { 712 + const struct edac_scrub_ops *scrub_ops; 672 713 u8 instance; 673 714 void *private; 674 715 }; ··· 717 676 struct edac_dev_feat_ctx { 718 677 struct device dev; 719 678 void *private; 679 + struct edac_dev_data *scrub; 720 680 }; 721 681 722 682 struct edac_dev_feature { 723 683 enum edac_dev_feat ft_type; 724 684 u8 instance; 685 + const struct edac_scrub_ops *scrub_ops; 725 686 void *ctx; 726 687 }; 727 688