diff options
Diffstat (limited to 'meta-elisa/recipes-kernel')
-rw-r--r-- | meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb | 30 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/Makefile | 12 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_device.h | 352 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_inject.c | 357 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_inject.rst | 43 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_mc.h | 258 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_module.h | 130 | ||||
-rw-r--r-- | meta-elisa/recipes-kernel/linux/files/edac_pci.h | 271 |
8 files changed, 1453 insertions, 0 deletions
diff --git a/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb b/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb new file mode 100644 index 00000000..e02b64ab --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/edac-inject-module_1.0.bb @@ -0,0 +1,30 @@ +DESCRIPTION = "Builds the EDAC inject module" +HOMEPAGE = "https://github.com/elisa-tech/linux/commits/edac_inject" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6" + +DEPENDS += "virtual/kernel" + +SRC_URI = " \ + file://edac_device.h \ + file://edac_inject.c \ + file://edac_mc.h \ + file://edac_module.h \ + file://edac_pci.h \ + file://Makefile \ +" + +S = "${WORKDIR}/" + +inherit module + +EXTRA_OEMAKE += "KDIR=${STAGING_KERNEL_DIR}" + +do_compile_prepend () { + sed -i "s/arm-none-linux-gnueabi-/${TARGET_PREFIX}/g" ${S}/Makefile +} + +do_install () { + install -d ${D}/lib/modules/${KERNEL_VERSION}/drivers/edac/ + install -m 0755 ${S}/edac_inject.ko ${D}/lib/modules/${KERNEL_VERSION}/drivers/edac/ +} diff --git a/meta-elisa/recipes-kernel/linux/files/Makefile b/meta-elisa/recipes-kernel/linux/files/Makefile new file mode 100644 index 00000000..017577c7 --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/Makefile @@ -0,0 +1,12 @@ +obj-m += edac_inject.o + +export KDIR := /lib/modules/$(shell uname -r)/build + +allofit: modules +modules: + @$(MAKE) -C $(KDIR) M=$(shell pwd) modules +kernel_clean: + @$(MAKE) -C $(KDIR) M=$(shell pwd) clean + +clean: kernel_clean + rm -rf Module.symvers modules.order diff --git a/meta-elisa/recipes-kernel/linux/files/edac_device.h b/meta-elisa/recipes-kernel/linux/files/edac_device.h new file mode 100644 index 00000000..c4c0e0bd --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_device.h @@ -0,0 +1,352 @@ +/* + * Defines, structures, APIs for edac_device + * + * (C) 2007 Linux Networx (http://lnxi.com) + * This file may be distributed under the terms of the + * GNU General Public License. + * + * Written by Thayne Harbaugh + * Based on work by Dan Hollis <goemon at anime dot net> and others. + * http://www.anime.net/~goemon/linux-ecc/ + * + * NMI handling support added by + * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> + * + * Refactored for multi-source files: + * Doug Thompson <norsk5@xmission.com> + * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. + */ + +#ifndef _EDAC_DEVICE_H_ +#define _EDAC_DEVICE_H_ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/edac.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/types.h> +#include <linux/sysfs.h> +#include <linux/workqueue.h> + + +/* + * The following are the structures to provide for a generic + * or abstract 'edac_device'. This set of structures and the + * code that implements the APIs for the same, provide for + * registering EDAC type devices which are NOT standard memory. + * + * CPU caches (L1 and L2) + * DMA engines + * Core CPU switches + * Fabric switch units + * PCIe interface controllers + * other EDAC/ECC type devices that can be monitored for + * errors, etc. + * + * It allows for a 2 level set of hierarchy. For example: + * + * cache could be composed of L1, L2 and L3 levels of cache. + * Each CPU core would have its own L1 cache, while sharing + * L2 and maybe L3 caches. + * + * View them arranged, via the sysfs presentation: + * /sys/devices/system/edac/.. + * + * mc/ <existing memory device directory> + * cpu/cpu0/.. <L1 and L2 block directory> + * /L1-cache/ce_count + * /ue_count + * /L2-cache/ce_count + * /ue_count + * cpu/cpu1/.. <L1 and L2 block directory> + * /L1-cache/ce_count + * /ue_count + * /L2-cache/ce_count + * /ue_count + * ... + * + * the L1 and L2 directories would be "edac_device_block's" + */ + +struct edac_device_counter { + u32 ue_count; + u32 ce_count; +}; + +/* forward reference */ +struct edac_device_ctl_info; +struct edac_device_block; + +/* edac_dev_sysfs_attribute structure + * used for driver sysfs attributes in mem_ctl_info + * for extra controls and attributes: + * like high level error Injection controls + */ +struct edac_dev_sysfs_attribute { + struct attribute attr; + ssize_t (*show)(struct edac_device_ctl_info *, char *); + ssize_t (*store)(struct edac_device_ctl_info *, const char *, size_t); +}; + +/* edac_dev_sysfs_block_attribute structure + * + * used in leaf 'block' nodes for adding controls/attributes + * + * each block in each instance of the containing control structure + * can have an array of the following. The show and store functions + * will be filled in with the show/store function in the + * low level driver. + * + * The 'value' field will be the actual value field used for + * counting + */ +struct edac_dev_sysfs_block_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *, struct attribute *, char *); + ssize_t (*store)(struct kobject *, struct attribute *, + const char *, size_t); + struct edac_device_block *block; + + unsigned int value; +}; + +/* device block control structure */ +struct edac_device_block { + struct edac_device_instance *instance; /* Up Pointer */ + char name[EDAC_DEVICE_NAME_LEN + 1]; + + struct edac_device_counter counters; /* basic UE and CE counters */ + + int nr_attribs; /* how many attributes */ + + /* this block's attributes, could be NULL */ + struct edac_dev_sysfs_block_attribute *block_attributes; + + /* edac sysfs device control */ + struct kobject kobj; +}; + +/* device instance control structure */ +struct edac_device_instance { + struct edac_device_ctl_info *ctl; /* Up pointer */ + char name[EDAC_DEVICE_NAME_LEN + 4]; + + struct edac_device_counter counters; /* instance counters */ + + u32 nr_blocks; /* how many blocks */ + struct edac_device_block *blocks; /* block array */ + + /* edac sysfs device control */ + struct kobject kobj; +}; + + +/* + * Abstract edac_device control info structure + * + */ +struct edac_device_ctl_info { + /* for global list of edac_device_ctl_info structs */ + struct list_head link; + + struct module *owner; /* Module owner of this control struct */ + + int dev_idx; + + /* Per instance controls for this edac_device */ + int log_ue; /* boolean for logging UEs */ + int log_ce; /* boolean for logging CEs */ + int panic_on_ue; /* boolean for panic'ing on an UE */ + unsigned poll_msec; /* number of milliseconds to poll interval */ + unsigned long delay; /* number of jiffies for poll_msec */ + + /* Additional top controller level attributes, but specified + * by the low level driver. + * + * Set by the low level driver to provide attributes at the + * controller level, same level as 'ue_count' and 'ce_count' above. + * An array of structures, NULL terminated + * + * If attributes are desired, then set to array of attributes + * If no attributes are desired, leave NULL + */ + struct edac_dev_sysfs_attribute *sysfs_attributes; + + /* pointer to main 'edac' subsys in sysfs */ + struct bus_type *edac_subsys; + + /* the internal state of this controller instance */ + int op_state; + /* work struct for this instance */ + struct delayed_work work; + + /* pointer to edac polling checking routine: + * If NOT NULL: points to polling check routine + * If NULL: Then assumes INTERRUPT operation, where + * MC driver will receive events + */ + void (*edac_check) (struct edac_device_ctl_info * edac_dev); + + struct device *dev; /* pointer to device structure */ + + const char *mod_name; /* module name */ + const char *ctl_name; /* edac controller name */ + const char *dev_name; /* pci/platform/etc... name */ + + void *pvt_info; /* pointer to 'private driver' info */ + + unsigned long start_time; /* edac_device load start time (jiffies) */ + + struct completion removal_complete; + + /* sysfs top name under 'edac' directory + * and instance name: + * cpu/cpu0/... + * cpu/cpu1/... + * cpu/cpu2/... + * ... + */ + char name[EDAC_DEVICE_NAME_LEN + 1]; + + /* Number of instances supported on this control structure + * and the array of those instances + */ + u32 nr_instances; + struct edac_device_instance *instances; + + /* Event counters for the this whole EDAC Device */ + struct edac_device_counter counters; + + /* edac sysfs device control for the 'name' + * device this structure controls + */ + struct kobject kobj; +}; + +/* To get from the instance's wq to the beginning of the ctl structure */ +#define to_edac_mem_ctl_work(w) \ + container_of(w, struct mem_ctl_info, work) + +#define to_edac_device_ctl_work(w) \ + container_of(w,struct edac_device_ctl_info,work) + +/* + * The alloc() and free() functions for the 'edac_device' control info + * structure. A MC driver will allocate one of these for each edac_device + * it is going to control/register with the EDAC CORE. + */ +extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( + unsigned sizeof_private, + char *edac_device_name, unsigned nr_instances, + char *edac_block_name, unsigned nr_blocks, + unsigned offset_value, + struct edac_dev_sysfs_block_attribute *block_attributes, + unsigned nr_attribs, + int device_index); + +/* The offset value can be: + * -1 indicating no offset value + * 0 for zero-based block numbers + * 1 for 1-based block number + * other for other-based block number + */ +#define BLOCK_OFFSET_VALUE_OFF ((unsigned) -1) + +extern void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info); + +/** + * edac_device_add_device: Insert the 'edac_dev' structure into the + * edac_device global list and create sysfs entries associated with + * edac_device structure. + * + * @edac_dev: pointer to edac_device structure to be added to the list + * 'edac_device' structure. + * + * Returns: + * 0 on Success, or an error code on failure + */ +extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev); + +/** + * edac_device_del_device: + * Remove sysfs entries for specified edac_device structure and + * then remove edac_device structure from global list + * + * @dev: + * Pointer to struct &device representing the edac device + * structure to remove. + * + * Returns: + * Pointer to removed edac_device structure, + * or %NULL if device not found. + */ +extern struct edac_device_ctl_info *edac_device_del_device(struct device *dev); + +/** + * Log correctable errors. + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the CE error happened + * @count: Number of errors to log. + * @block_nr: number of the block where the CE error happened + * @msg: message to be printed + */ +void edac_device_handle_ce_count(struct edac_device_ctl_info *edac_dev, + unsigned int count, int inst_nr, int block_nr, + const char *msg); + +/** + * Log uncorrectable errors. + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the CE error happened + * @count: Number of errors to log. + * @block_nr: number of the block where the CE error happened + * @msg: message to be printed + */ +void edac_device_handle_ue_count(struct edac_device_ctl_info *edac_dev, + unsigned int count, int inst_nr, int block_nr, + const char *msg); + +/** + * edac_device_handle_ce(): Log a single correctable error + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the CE error happened + * @block_nr: number of the block where the CE error happened + * @msg: message to be printed + */ +static inline void +edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, int inst_nr, + int block_nr, const char *msg) +{ + edac_device_handle_ce_count(edac_dev, 1, inst_nr, block_nr, msg); +} + +/** + * edac_device_handle_ue(): Log a single uncorrectable error + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the UE error happened + * @block_nr: number of the block where the UE error happened + * @msg: message to be printed + */ +static inline void +edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, int inst_nr, + int block_nr, const char *msg) +{ + edac_device_handle_ue_count(edac_dev, 1, inst_nr, block_nr, msg); +} + +/** + * edac_device_alloc_index: Allocate a unique device index number + * + * Returns: + * allocated index number + */ +extern int edac_device_alloc_index(void); +extern const char *edac_layer_name[]; +#endif diff --git a/meta-elisa/recipes-kernel/linux/files/edac_inject.c b/meta-elisa/recipes-kernel/linux/files/edac_inject.c new file mode 100644 index 00000000..aa431c7c --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_inject.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * edac_inject.c; platform agnostic MC error injector + * + * Copyright (c) 2020 Intel Corporation + * + * Authors: Gabriele Paoloni <gabriele.paoloni@intel.com> + * Corey Minyard <cminyard@mvista.com> + */ + +#include <linux/edac.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/stddef.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include "edac_module.h" + +static struct platform_device dummy_pdev; + +#define EDAC_INJECT_MAX_MSG_SIZE 64 + +/** + * Information about an error. + */ +struct inj_errinfo { + unsigned long page_frame_number; + unsigned long offset_in_page; + unsigned long syndrome; + int top_layer; + int mid_layer; + int low_layer; + char msg[EDAC_INJECT_MAX_MSG_SIZE]; + char other_detail[EDAC_INJECT_MAX_MSG_SIZE]; +}; + +/* A single error type. */ +struct inj_err { + struct list_head link; + enum hw_event_mc_err_type type; + u16 count; + struct inj_errinfo info; +}; + +/** + * mci private structure to store the errors + */ +struct inj_pvt { + struct mutex lock; + struct list_head errors; + + /* Information to put into the edac error report. */ + struct inj_errinfo info; +}; + +static void edac_inject_handle(struct mem_ctl_info *mci, + struct inj_err *err) +{ + edac_mc_handle_error(err->type, mci, err->count, + err->info.page_frame_number, + err->info.offset_in_page, + err->info.syndrome, + err->info.top_layer, err->info.mid_layer, + err->info.low_layer, + err->info.msg, err->info.other_detail); + err->count = 0; +} + +/** + * inject_edac_check() - Calls the error checking subroutines + * @mci: struct mem_ctl_info pointer + */ +static void inject_edac_check(struct mem_ctl_info *mci) +{ + struct inj_pvt *pvt = mci->pvt_info; + struct inj_err *val, *val2; + + mutex_lock(&pvt->lock); + list_for_each_entry_safe(val, val2, &pvt->errors, link) { + edac_inject_handle(mci, val); + list_del(&val->link); + kfree(val); + } + mutex_unlock(&pvt->lock); +}; + +struct inject_edac_attribute { + struct device_attribute attr; + unsigned long offset; + char *default_msg; +}; + +#define to_inj_edac_attr(x) container_of(x, struct inject_edac_attribute, attr) + +static ssize_t inject_edac_store_count(struct device *dev, + struct device_attribute *attr, + const char *data, size_t count) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + struct inj_err *val; + int ret; + u16 errcount; + + ret = kstrtou16(data, 10, &errcount); + if (ret < 0) + return ret; + if (errcount == 0) + goto out; + + val = kzalloc(sizeof(struct inj_err), GFP_KERNEL); + if (!val) + return -ENOMEM; + + val->type = ea->offset; + val->count = errcount; + val->info = pvt->info; + if (!pvt->info.msg[0]) + strncpy(val->info.msg, ea->default_msg, + EDAC_INJECT_MAX_MSG_SIZE); + + mutex_lock(&pvt->lock); + list_add(&val->link, &pvt->errors); + mutex_unlock(&pvt->lock); + +out: + return count; +} + +static ssize_t inject_edac_show_count(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + struct inj_err *val; + unsigned int count = 0; + + mutex_lock(&pvt->lock); + list_for_each_entry(val, &pvt->errors, link) { + if (val->type == ea->offset) + count += val->count; + } + mutex_unlock(&pvt->lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", count); +} + +static ssize_t inject_edac_store_ulong(struct device *dev, + struct device_attribute *attr, + const char *data, size_t count) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + unsigned long *val = (unsigned long *) (((char *) pvt) + ea->offset); + int ret; + + ret = kstrtoul(data, 10, val); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t inject_edac_show_ulong(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + unsigned long *val = (unsigned long *) (((char *) pvt) + ea->offset); + + return snprintf(buf, PAGE_SIZE, "%lu\n", *val); +} + +static ssize_t inject_edac_store_int(struct device *dev, + struct device_attribute *attr, + const char *data, size_t count) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + int *val = (int *) (((char *) pvt) + ea->offset); + int ret; + + ret = kstrtoint(data, 10, val); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t inject_edac_show_int(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + int *val = (int *) (((char *) pvt) + ea->offset); + + return snprintf(buf, PAGE_SIZE, "%d\n", *val); +} + +static ssize_t inject_edac_store_str(struct device *dev, + struct device_attribute *attr, + const char *data, size_t count) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + size_t real_size = count; + char *val = (((char *) pvt) + ea->offset); + + while (real_size > 0 && data[real_size - 1] == '\n') + real_size--; + if (real_size > EDAC_INJECT_MAX_MSG_SIZE - 1) + real_size = EDAC_INJECT_MAX_MSG_SIZE - 1; + memcpy(val, data, real_size); + val[real_size] = '\0'; + + return count; +} + +static ssize_t inject_edac_show_str(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inject_edac_attribute *ea = to_inj_edac_attr(attr); + struct inj_pvt *pvt = to_mci(dev)->pvt_info; + char *val = (((char *) pvt) + ea->offset); + + return snprintf(buf, PAGE_SIZE, "%s\n", val); +} + +#define DEVICE_INJECT_EDAC_COUNT(_name, _member, _type, _defstr)\ + struct inject_edac_attribute inject_edac_attr_##_name =\ + { __ATTR(_name, 0600, inject_edac_show_count,\ + inject_edac_store_count),\ + _type, _defstr } + +#define DEVICE_INJECT_EDAC_ULONG(_name, _member)\ + struct inject_edac_attribute inject_edac_attr_##_name =\ + { __ATTR(_name, 0600, inject_edac_show_ulong,\ + inject_edac_store_ulong),\ + offsetof(struct inj_pvt, info._member) } + +#define DEVICE_INJECT_EDAC_INT(_name, _member)\ + struct inject_edac_attribute inject_edac_attr_##_name =\ + { __ATTR(_name, 0600, inject_edac_show_int,\ + inject_edac_store_int),\ + offsetof(struct inj_pvt, info._member) } + +#define DEVICE_INJECT_EDAC_STR(_name, _member)\ + struct inject_edac_attribute inject_edac_attr_##_name =\ + { __ATTR(_name, 0600, inject_edac_show_str,\ + inject_edac_store_str),\ + offsetof(struct inj_pvt, info._member) } + +static DEVICE_INJECT_EDAC_COUNT(inject_ce, correctable_errors, + HW_EVENT_ERR_CORRECTED, + "injected correctable errors"); +static DEVICE_INJECT_EDAC_COUNT(inject_ue, uncorrectable_errors, + HW_EVENT_ERR_UNCORRECTED, + "injected uncorrectable errors"); +static DEVICE_INJECT_EDAC_COUNT(inject_de, deferrable_errors, + HW_EVENT_ERR_DEFERRED, + "injected deferrable errors"); +static DEVICE_INJECT_EDAC_COUNT(inject_fe, fatal_errors, + HW_EVENT_ERR_FATAL, + "injected fatal errors"); +static DEVICE_INJECT_EDAC_COUNT(inject_ie, informative_errors, + HW_EVENT_ERR_INFO, + "injected informative errors"); +static DEVICE_INJECT_EDAC_ULONG(inject_pfn, page_frame_number); +static DEVICE_INJECT_EDAC_ULONG(inject_oip, offset_in_page); +static DEVICE_INJECT_EDAC_ULONG(inject_syndrome, syndrome); +static DEVICE_INJECT_EDAC_INT(inject_top, top_layer); +static DEVICE_INJECT_EDAC_INT(inject_mid, mid_layer); +static DEVICE_INJECT_EDAC_INT(inject_low, low_layer); +static DEVICE_INJECT_EDAC_STR(inject_msg, msg); +static DEVICE_INJECT_EDAC_STR(inject_other_detail, other_detail); + +static struct attribute *edac_inj_attrs[] = { + &inject_edac_attr_inject_ce.attr.attr, + &inject_edac_attr_inject_ue.attr.attr, + &inject_edac_attr_inject_de.attr.attr, + &inject_edac_attr_inject_fe.attr.attr, + &inject_edac_attr_inject_ie.attr.attr, + &inject_edac_attr_inject_pfn.attr.attr, + &inject_edac_attr_inject_oip.attr.attr, + &inject_edac_attr_inject_syndrome.attr.attr, + &inject_edac_attr_inject_top.attr.attr, + &inject_edac_attr_inject_mid.attr.attr, + &inject_edac_attr_inject_low.attr.attr, + &inject_edac_attr_inject_msg.attr.attr, + &inject_edac_attr_inject_other_detail.attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(edac_inj); + +static int __init edac_inject_init(void) +{ + struct edac_mc_layer layer; + struct inj_pvt *pvt; + struct mem_ctl_info *mci; + int rc; + + edac_printk(KERN_INFO, EDAC_MC, + "EDAC MC error inject module init\n"); + edac_printk(KERN_INFO, EDAC_MC, + "\t(c) 2020 Intel Corporation\n"); + + /* Only POLL mode supported so far */ + edac_op_state = EDAC_OPSTATE_POLL; + + layer.type = EDAC_MC_LAYER_CHANNEL; + layer.size = 1; + layer.is_virt_csrow = false; + mci = edac_mc_alloc(0, 1, &layer, sizeof(struct inj_pvt)); + if (!mci) { + edac_printk(KERN_ERR, EDAC_MC, + "EDAC INJECT: edac_mc_alloc failed\n"); + return -ENOMEM; + } + + mci->pdev = &dummy_pdev.dev; + pvt = mci->pvt_info; + mutex_init(&pvt->lock); + INIT_LIST_HEAD(&pvt->errors); + + /* Set the function pointer for periodic errors checks */ + mci->edac_check = inject_edac_check; + + rc = edac_mc_add_mc_with_groups(mci, edac_inj_groups); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, + "EDAC INJECT: edac_mc_add_mc failed\n"); + edac_mc_free(mci); + } + + return rc; +} + +static void __exit edac_inject_exit(void) +{ + struct mem_ctl_info *mci = platform_get_drvdata(&dummy_pdev); + + edac_mc_del_mc(&dummy_pdev.dev); + edac_mc_free(mci); +} + + +module_init(edac_inject_init); +module_exit(edac_inject_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@intel.com>\n"); +MODULE_AUTHOR("Corey Minyard <cminyard@mvista.com>\n"); +MODULE_DESCRIPTION("EDAC MC error inject module"); diff --git a/meta-elisa/recipes-kernel/linux/files/edac_inject.rst b/meta-elisa/recipes-kernel/linux/files/edac_inject.rst new file mode 100644 index 00000000..0c91eda2 --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_inject.rst @@ -0,0 +1,43 @@ +EDAC Injection Device +=========================== + +This device lets you inject errors into the Linux EDAC system for +testing userland software that interacts with EDAC, since causing your +own memory errors is hard. + +The interface for injecting errors appears in the sysfs file system in +the same place as the EDAC driver's interface:: + + /sys/devices/system/edac/mc/mc<n>/.. + +The files for injection all start with "inject_", they are: + +- inject_ce - correctable error +- inject_de - deferred error +- inject_fe - fatal error +- inject_ie - informative error +- inject_ue - uncorrectable error + +Writing a number besides zero to these will result in that many errors +being injected. Each write injects new errors. Reading the value +returns the number of pending errors to be injected that have not +yet completed. + +In addition to these, some data is passed along with the error to show +where it occurred. These values mostly default to zero or empty, but +they can be set in the following write only values: + +- inject_low - low layer +- inject_mid - mid layer +- inject_msg - error message string. This defaults to "dummy <error + type>" where "<error type>" is "correctable error", "uncorrectable + error", etc. +- inject_oip - offset in page +- inject_other_detail - Other details string +- inject_pfn - page frame number +- inject_syndrome - symdrome +- inject_top - top layer + +Note that these values are taken when the count is written, so you are +free to set up the info, write an error count, change the info, write +another error count, etc. diff --git a/meta-elisa/recipes-kernel/linux/files/edac_mc.h b/meta-elisa/recipes-kernel/linux/files/edac_mc.h new file mode 100644 index 00000000..881b00ea --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_mc.h @@ -0,0 +1,258 @@ +/* + * Defines, structures, APIs for edac_mc module + * + * (C) 2007 Linux Networx (http://lnxi.com) + * This file may be distributed under the terms of the + * GNU General Public License. + * + * Written by Thayne Harbaugh + * Based on work by Dan Hollis <goemon at anime dot net> and others. + * http://www.anime.net/~goemon/linux-ecc/ + * + * NMI handling support added by + * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> + * + * Refactored for multi-source files: + * Doug Thompson <norsk5@xmission.com> + * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. + */ + +#ifndef _EDAC_MC_H_ +#define _EDAC_MC_H_ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/smp.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/nmi.h> +#include <linux/rcupdate.h> +#include <linux/completion.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/edac.h> + +#if PAGE_SHIFT < 20 +#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT)) +#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) +#else /* PAGE_SHIFT > 20 */ +#define PAGES_TO_MiB(pages) ((pages) << (PAGE_SHIFT - 20)) +#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20)) +#endif + +#define edac_printk(level, prefix, fmt, arg...) \ + printk(level "EDAC " prefix ": " fmt, ##arg) + +#define edac_mc_printk(mci, level, fmt, arg...) \ + printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg) + +#define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ + printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) + +#define edac_device_printk(ctl, level, fmt, arg...) \ + printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) + +#define edac_pci_printk(ctl, level, fmt, arg...) \ + printk(level "EDAC PCI%d: " fmt, ctl->pci_idx, ##arg) + +/* prefixes for edac_printk() and edac_mc_printk() */ +#define EDAC_MC "MC" +#define EDAC_PCI "PCI" +#define EDAC_DEBUG "DEBUG" + +extern const char * const edac_mem_types[]; + +#ifdef CONFIG_EDAC_DEBUG +extern int edac_debug_level; + +#define edac_dbg(level, fmt, ...) \ +do { \ + if (level <= edac_debug_level) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +#else /* !CONFIG_EDAC_DEBUG */ + +#define edac_dbg(level, fmt, ...) \ +do { \ + if (0) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +#endif /* !CONFIG_EDAC_DEBUG */ + +#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ + PCI_DEVICE_ID_ ## vend ## _ ## dev + +#define edac_dev_name(dev) (dev)->dev_name + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + +/** + * edac_mc_alloc() - Allocate and partially fill a struct &mem_ctl_info. + * + * @mc_num: Memory controller number + * @n_layers: Number of MC hierarchy layers + * @layers: Describes each layer as seen by the Memory Controller + * @sz_pvt: size of private storage needed + * + * + * Everything is kmalloc'ed as one big chunk - more efficient. + * Only can be used if all structures have the same lifetime - otherwise + * you have to allocate and initialize your own structures. + * + * Use edac_mc_free() to free mc structures allocated by this function. + * + * .. note:: + * + * drivers handle multi-rank memories in different ways: in some + * drivers, one multi-rank memory stick is mapped as one entry, while, in + * others, a single multi-rank memory stick would be mapped into several + * entries. Currently, this function will allocate multiple struct dimm_info + * on such scenarios, as grouping the multiple ranks require drivers change. + * + * Returns: + * On success, return a pointer to struct mem_ctl_info pointer; + * %NULL otherwise + */ +struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, + unsigned int n_layers, + struct edac_mc_layer *layers, + unsigned int sz_pvt); + +/** + * edac_get_owner - Return the owner's mod_name of EDAC MC + * + * Returns: + * Pointer to mod_name string when EDAC MC is owned. NULL otherwise. + */ +extern const char *edac_get_owner(void); + +/* + * edac_mc_add_mc_with_groups() - Insert the @mci structure into the mci + * global list and create sysfs entries associated with @mci structure. + * + * @mci: pointer to the mci structure to be added to the list + * @groups: optional attribute groups for the driver-specific sysfs entries + * + * Returns: + * 0 on Success, or an error code on failure + */ +extern int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, + const struct attribute_group **groups); +#define edac_mc_add_mc(mci) edac_mc_add_mc_with_groups(mci, NULL) + +/** + * edac_mc_free() - Frees a previously allocated @mci structure + * + * @mci: pointer to a struct mem_ctl_info structure + */ +extern void edac_mc_free(struct mem_ctl_info *mci); + +/** + * edac_has_mcs() - Check if any MCs have been allocated. + * + * Returns: + * True if MC instances have been registered successfully. + * False otherwise. + */ +extern bool edac_has_mcs(void); + +/** + * edac_mc_find() - Search for a mem_ctl_info structure whose index is @idx. + * + * @idx: index to be seek + * + * If found, return a pointer to the structure. + * Else return NULL. + */ +extern struct mem_ctl_info *edac_mc_find(int idx); + +/** + * find_mci_by_dev() - Scan list of controllers looking for the one that + * manages the @dev device. + * + * @dev: pointer to a struct device related with the MCI + * + * Returns: on success, returns a pointer to struct &mem_ctl_info; + * %NULL otherwise. + */ +extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); + +/** + * edac_mc_del_mc() - Remove sysfs entries for mci structure associated with + * @dev and remove mci structure from global list. + * + * @dev: Pointer to struct &device representing mci structure to remove. + * + * Returns: pointer to removed mci structure, or %NULL if device not found. + */ +extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); + +/** + * edac_mc_find_csrow_by_page() - Ancillary routine to identify what csrow + * contains a memory page. + * + * @mci: pointer to a struct mem_ctl_info structure + * @page: memory page to find + * + * Returns: on success, returns the csrow. -1 if not found. + */ +extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, + unsigned long page); + +/** + * edac_raw_mc_handle_error() - Reports a memory event to userspace without + * doing anything to discover the error location. + * + * @e: error description + * + * This raw function is used internally by edac_mc_handle_error(). It should + * only be called directly when the hardware error come directly from BIOS, + * like in the case of APEI GHES driver. + */ +void edac_raw_mc_handle_error(struct edac_raw_error_desc *e); + +/** + * edac_mc_handle_error() - Reports a memory event to userspace. + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @error_count: Number of errors of the same type + * @page_frame_number: mem page where the error occurred + * @offset_in_page: offset of the error inside the page + * @syndrome: ECC syndrome + * @top_layer: Memory layer[0] position + * @mid_layer: Memory layer[1] position + * @low_layer: Memory layer[2] position + * @msg: Message meaningful to the end users that + * explains the event + * @other_detail: Technical details about the event that + * may help hardware manufacturers and + * EDAC developers to analyse the event + */ +void edac_mc_handle_error(const enum hw_event_mc_err_type type, + struct mem_ctl_info *mci, + const u16 error_count, + const unsigned long page_frame_number, + const unsigned long offset_in_page, + const unsigned long syndrome, + const int top_layer, + const int mid_layer, + const int low_layer, + const char *msg, + const char *other_detail); + +/* + * edac misc APIs + */ +extern char *edac_op_state_to_string(int op_state); + +#endif /* _EDAC_MC_H_ */ diff --git a/meta-elisa/recipes-kernel/linux/files/edac_module.h b/meta-elisa/recipes-kernel/linux/files/edac_module.h new file mode 100644 index 00000000..aa1f9168 --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_module.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * edac_module.h + * + * For defining functions/data for within the EDAC_CORE module only + * + * written by doug thompson <norsk5@xmission.h> + */ + +#ifndef __EDAC_MODULE_H__ +#define __EDAC_MODULE_H__ + +#include "edac_mc.h" +#include "edac_pci.h" +#include "edac_device.h" + +/* + * INTERNAL EDAC MODULE: + * EDAC memory controller sysfs create/remove functions + * and setup/teardown functions + * + * edac_mc objects + */ + /* on edac_mc_sysfs.c */ +int edac_mc_sysfs_init(void); +void edac_mc_sysfs_exit(void); +extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, + const struct attribute_group **groups); +extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); +extern int edac_get_log_ue(void); +extern int edac_get_log_ce(void); +extern int edac_get_panic_on_ue(void); +extern int edac_mc_get_log_ue(void); +extern int edac_mc_get_log_ce(void); +extern int edac_mc_get_panic_on_ue(void); +extern int edac_get_poll_msec(void); +extern unsigned int edac_mc_get_poll_msec(void); + +unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, + unsigned len); + + /* on edac_device.c */ +extern int edac_device_register_sysfs_main_kobj( + struct edac_device_ctl_info *edac_dev); +extern void edac_device_unregister_sysfs_main_kobj( + struct edac_device_ctl_info *edac_dev); +extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev); +extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev); + +/* edac core workqueue: single CPU mode */ +int edac_workqueue_setup(void); +void edac_workqueue_teardown(void); +bool edac_queue_work(struct delayed_work *work, unsigned long delay); +bool edac_stop_work(struct delayed_work *work); +bool edac_mod_work(struct delayed_work *work, unsigned long delay); + +extern void edac_device_reset_delay_period(struct edac_device_ctl_info + *edac_dev, unsigned long value); +extern void edac_mc_reset_delay_period(unsigned long value); + +extern void *edac_align_ptr(void **p, unsigned size, int n_elems); + +/* + * EDAC debugfs functions + */ + +#define edac_debugfs_remove_recursive debugfs_remove_recursive +#define edac_debugfs_remove debugfs_remove +#ifdef CONFIG_EDAC_DEBUG +void edac_debugfs_init(void); +void edac_debugfs_exit(void); +void edac_create_debugfs_nodes(struct mem_ctl_info *mci); +struct dentry *edac_debugfs_create_dir(const char *dirname); +struct dentry * +edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent); +struct dentry * +edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, + void *data, const struct file_operations *fops); +void edac_debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); +void edac_debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); +void edac_debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); +#else +static inline void edac_debugfs_init(void) { } +static inline void edac_debugfs_exit(void) { } +static inline void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { } +static inline struct dentry *edac_debugfs_create_dir(const char *dirname) { return NULL; } +static inline struct dentry * +edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) { return NULL; } +static inline struct dentry * +edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, + void *data, const struct file_operations *fops) { return NULL; } +static inline void edac_debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value) { } +static inline void edac_debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value) { } +static inline void edac_debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value) { } +#endif + +/* + * EDAC PCI functions + */ +#ifdef CONFIG_PCI +extern void edac_pci_do_parity_check(void); +extern void edac_pci_clear_parity_errors(void); +extern int edac_sysfs_pci_setup(void); +extern void edac_sysfs_pci_teardown(void); +extern int edac_pci_get_check_errors(void); +extern int edac_pci_get_poll_msec(void); +extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci); +extern void edac_pci_handle_pe(struct edac_pci_ctl_info *pci, const char *msg); +extern void edac_pci_handle_npe(struct edac_pci_ctl_info *pci, + const char *msg); +#else /* CONFIG_PCI */ +/* pre-process these away */ +#define edac_pci_do_parity_check() +#define edac_pci_clear_parity_errors() +#define edac_sysfs_pci_setup() (0) +#define edac_sysfs_pci_teardown() +#define edac_pci_get_check_errors() +#define edac_pci_get_poll_msec() +#define edac_pci_handle_pe() +#define edac_pci_handle_npe() +#endif /* CONFIG_PCI */ + +#endif /* __EDAC_MODULE_H__ */ diff --git a/meta-elisa/recipes-kernel/linux/files/edac_pci.h b/meta-elisa/recipes-kernel/linux/files/edac_pci.h new file mode 100644 index 00000000..5175f572 --- /dev/null +++ b/meta-elisa/recipes-kernel/linux/files/edac_pci.h @@ -0,0 +1,271 @@ +/* + * Defines, structures, APIs for edac_pci and edac_pci_sysfs + * + * (C) 2007 Linux Networx (http://lnxi.com) + * This file may be distributed under the terms of the + * GNU General Public License. + * + * Written by Thayne Harbaugh + * Based on work by Dan Hollis <goemon at anime dot net> and others. + * http://www.anime.net/~goemon/linux-ecc/ + * + * NMI handling support added by + * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> + * + * Refactored for multi-source files: + * Doug Thompson <norsk5@xmission.com> + * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. + */ + +#ifndef _EDAC_PCI_H_ +#define _EDAC_PCI_H_ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/edac.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#ifdef CONFIG_PCI + +struct edac_pci_counter { + atomic_t pe_count; + atomic_t npe_count; +}; + +/* + * Abstract edac_pci control info structure + * + */ +struct edac_pci_ctl_info { + /* for global list of edac_pci_ctl_info structs */ + struct list_head link; + + int pci_idx; + + struct bus_type *edac_subsys; /* pointer to subsystem */ + + /* the internal state of this controller instance */ + int op_state; + /* work struct for this instance */ + struct delayed_work work; + + /* pointer to edac polling checking routine: + * If NOT NULL: points to polling check routine + * If NULL: Then assumes INTERRUPT operation, where + * MC driver will receive events + */ + void (*edac_check) (struct edac_pci_ctl_info * edac_dev); + + struct device *dev; /* pointer to device structure */ + + const char *mod_name; /* module name */ + const char *ctl_name; /* edac controller name */ + const char *dev_name; /* pci/platform/etc... name */ + + void *pvt_info; /* pointer to 'private driver' info */ + + unsigned long start_time; /* edac_pci load start time (jiffies) */ + + struct completion complete; + + /* sysfs top name under 'edac' directory + * and instance name: + * cpu/cpu0/... + * cpu/cpu1/... + * cpu/cpu2/... + * ... + */ + char name[EDAC_DEVICE_NAME_LEN + 1]; + + /* Event counters for the this whole EDAC Device */ + struct edac_pci_counter counters; + + /* edac sysfs device control for the 'name' + * device this structure controls + */ + struct kobject kobj; +}; + +#define to_edac_pci_ctl_work(w) \ + container_of(w, struct edac_pci_ctl_info,work) + +/* write all or some bits in a byte-register*/ +static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, + u8 mask) +{ + if (mask != 0xff) { + u8 buf; + + pci_read_config_byte(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_byte(pdev, offset, value); +} + +/* write all or some bits in a word-register*/ +static inline void pci_write_bits16(struct pci_dev *pdev, int offset, + u16 value, u16 mask) +{ + if (mask != 0xffff) { + u16 buf; + + pci_read_config_word(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_word(pdev, offset, value); +} + +/* + * pci_write_bits32 + * + * edac local routine to do pci_write_config_dword, but adds + * a mask parameter. If mask is all ones, ignore the mask. + * Otherwise utilize the mask to isolate specified bits + * + * write all or some bits in a dword-register + */ +static inline void pci_write_bits32(struct pci_dev *pdev, int offset, + u32 value, u32 mask) +{ + if (mask != 0xffffffff) { + u32 buf; + + pci_read_config_dword(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_dword(pdev, offset, value); +} + +#endif /* CONFIG_PCI */ + +/* + * edac_pci APIs + */ + +/** + * edac_pci_alloc_ctl_info: + * The alloc() function for the 'edac_pci' control info + * structure. + * + * @sz_pvt: size of the private info at struct &edac_pci_ctl_info + * @edac_pci_name: name of the PCI device + * + * The chip driver will allocate one of these for each + * edac_pci it is going to control/register with the EDAC CORE. + * + * Returns: a pointer to struct &edac_pci_ctl_info on success; %NULL otherwise. + */ +extern struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, + const char *edac_pci_name); + +/** + * edac_pci_free_ctl_info(): + * Last action on the pci control structure. + * + * @pci: pointer to struct &edac_pci_ctl_info + * + * Calls the remove sysfs information, which will unregister + * this control struct's kobj. When that kobj's ref count + * goes to zero, its release function will be call and then + * kfree() the memory. + */ +extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_alloc_index: Allocate a unique PCI index number + * + * Returns: + * allocated index number + * + */ +extern int edac_pci_alloc_index(void); + +/** + * edac_pci_add_device(): Insert the 'edac_dev' structure into the + * edac_pci global list and create sysfs entries associated with + * edac_pci structure. + * + * @pci: pointer to the edac_device structure to be added to the list + * @edac_idx: A unique numeric identifier to be assigned to the + * 'edac_pci' structure. + * + * Returns: + * 0 on Success, or an error code on failure + */ +extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx); + +/** + * edac_pci_del_device() + * Remove sysfs entries for specified edac_pci structure and + * then remove edac_pci structure from global list + * + * @dev: + * Pointer to 'struct device' representing edac_pci structure + * to remove + * + * Returns: + * Pointer to removed edac_pci structure, + * or %NULL if device not found + */ +extern struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev); + +/** + * edac_pci_create_generic_ctl() + * A generic constructor for a PCI parity polling device + * Some systems have more than one domain of PCI busses. + * For systems with one domain, then this API will + * provide for a generic poller. + * + * @dev: pointer to struct &device; + * @mod_name: name of the PCI device + * + * This routine calls the edac_pci_alloc_ctl_info() for + * the generic device, with default values + * + * Returns: Pointer to struct &edac_pci_ctl_info on success, %NULL on + * failure. + */ +extern struct edac_pci_ctl_info *edac_pci_create_generic_ctl( + struct device *dev, + const char *mod_name); + +/** + * edac_pci_release_generic_ctl + * The release function of a generic EDAC PCI polling device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_create_sysfs + * Create the controls/attributes for the specified EDAC PCI device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_remove_sysfs() + * remove the controls and attributes for this EDAC PCI device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci); + +#endif |