brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 64c142a Raw
338 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * EDAC PCI component4 *5 * Author: Dave Jiang <djiang@mvista.com>6 *7 * 2007 (c) MontaVista Software, Inc.8 */9#include <asm/page.h>10#include <linux/uaccess.h>11#include <linux/ctype.h>12#include <linux/highmem.h>13#include <linux/init.h>14#include <linux/module.h>15#include <linux/slab.h>16#include <linux/smp.h>17#include <linux/spinlock.h>18#include <linux/sysctl.h>19#include <linux/timer.h>20 21#include "edac_pci.h"22#include "edac_module.h"23 24static DEFINE_MUTEX(edac_pci_ctls_mutex);25static LIST_HEAD(edac_pci_list);26static atomic_t pci_indexes = ATOMIC_INIT(0);27 28struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt,29						  const char *edac_pci_name)30{31	struct edac_pci_ctl_info *pci;32 33	edac_dbg(1, "\n");34 35	pci = kzalloc(sizeof(struct edac_pci_ctl_info), GFP_KERNEL);36	if (!pci)37		return NULL;38 39	if (sz_pvt) {40		pci->pvt_info = kzalloc(sz_pvt, GFP_KERNEL);41		if (!pci->pvt_info)42			goto free;43	}44 45	pci->op_state = OP_ALLOC;46 47	snprintf(pci->name, strlen(edac_pci_name) + 1, "%s", edac_pci_name);48 49	return pci;50 51free:52	kfree(pci);53	return NULL;54}55EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info);56 57void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci)58{59	edac_dbg(1, "\n");60 61	edac_pci_remove_sysfs(pci);62}63EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info);64 65/*66 * find_edac_pci_by_dev()67 * 	scans the edac_pci list for a specific 'struct device *'68 *69 *	return NULL if not found, or return control struct pointer70 */71static struct edac_pci_ctl_info *find_edac_pci_by_dev(struct device *dev)72{73	struct edac_pci_ctl_info *pci;74	struct list_head *item;75 76	edac_dbg(1, "\n");77 78	list_for_each(item, &edac_pci_list) {79		pci = list_entry(item, struct edac_pci_ctl_info, link);80 81		if (pci->dev == dev)82			return pci;83	}84 85	return NULL;86}87 88/*89 * add_edac_pci_to_global_list90 * 	Before calling this function, caller must assign a unique value to91 * 	edac_dev->pci_idx.92 * 	Return:93 * 		0 on success94 * 		1 on failure95 */96static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci)97{98	struct list_head *item, *insert_before;99	struct edac_pci_ctl_info *rover;100 101	edac_dbg(1, "\n");102 103	insert_before = &edac_pci_list;104 105	/* Determine if already on the list */106	rover = find_edac_pci_by_dev(pci->dev);107	if (unlikely(rover != NULL))108		goto fail0;109 110	/* Insert in ascending order by 'pci_idx', so find position */111	list_for_each(item, &edac_pci_list) {112		rover = list_entry(item, struct edac_pci_ctl_info, link);113 114		if (rover->pci_idx >= pci->pci_idx) {115			if (unlikely(rover->pci_idx == pci->pci_idx))116				goto fail1;117 118			insert_before = item;119			break;120		}121	}122 123	list_add_tail_rcu(&pci->link, insert_before);124	return 0;125 126fail0:127	edac_printk(KERN_WARNING, EDAC_PCI,128		"%s (%s) %s %s already assigned %d\n",129		dev_name(rover->dev), edac_dev_name(rover),130		rover->mod_name, rover->ctl_name, rover->pci_idx);131	return 1;132 133fail1:134	edac_printk(KERN_WARNING, EDAC_PCI,135		"but in low-level driver: attempt to assign\n"136		"\tduplicate pci_idx %d in %s()\n", rover->pci_idx,137		__func__);138	return 1;139}140 141/*142 * del_edac_pci_from_global_list143 *144 *	remove the PCI control struct from the global list145 */146static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)147{148	list_del_rcu(&pci->link);149 150	/* these are for safe removal of devices from global list while151	 * NMI handlers may be traversing list152	 */153	synchronize_rcu();154	INIT_LIST_HEAD(&pci->link);155}156 157/*158 * edac_pci_workq_function()159 *160 * 	periodic function that performs the operation161 *	scheduled by a workq request, for a given PCI control struct162 */163static void edac_pci_workq_function(struct work_struct *work_req)164{165	struct delayed_work *d_work = to_delayed_work(work_req);166	struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work);167	int msec;168	unsigned long delay;169 170	edac_dbg(3, "checking\n");171 172	mutex_lock(&edac_pci_ctls_mutex);173 174	if (pci->op_state != OP_RUNNING_POLL) {175		mutex_unlock(&edac_pci_ctls_mutex);176		return;177	}178 179	if (edac_pci_get_check_errors())180		pci->edac_check(pci);181 182	/* if we are on a one second period, then use round */183	msec = edac_pci_get_poll_msec();184	if (msec == 1000)185		delay = round_jiffies_relative(msecs_to_jiffies(msec));186	else187		delay = msecs_to_jiffies(msec);188 189	edac_queue_work(&pci->work, delay);190 191	mutex_unlock(&edac_pci_ctls_mutex);192}193 194int edac_pci_alloc_index(void)195{196	return atomic_inc_return(&pci_indexes) - 1;197}198EXPORT_SYMBOL_GPL(edac_pci_alloc_index);199 200int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx)201{202	edac_dbg(0, "\n");203 204	pci->pci_idx = edac_idx;205	pci->start_time = jiffies;206 207	mutex_lock(&edac_pci_ctls_mutex);208 209	if (add_edac_pci_to_global_list(pci))210		goto fail0;211 212	if (edac_pci_create_sysfs(pci)) {213		edac_pci_printk(pci, KERN_WARNING,214				"failed to create sysfs pci\n");215		goto fail1;216	}217 218	if (pci->edac_check) {219		pci->op_state = OP_RUNNING_POLL;220 221		INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);222		edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));223 224	} else {225		pci->op_state = OP_RUNNING_INTERRUPT;226	}227 228	edac_pci_printk(pci, KERN_INFO,229		"Giving out device to module %s controller %s: DEV %s (%s)\n",230		pci->mod_name, pci->ctl_name, pci->dev_name,231		edac_op_state_to_string(pci->op_state));232 233	mutex_unlock(&edac_pci_ctls_mutex);234	return 0;235 236	/* error unwind stack */237fail1:238	del_edac_pci_from_global_list(pci);239fail0:240	mutex_unlock(&edac_pci_ctls_mutex);241	return 1;242}243EXPORT_SYMBOL_GPL(edac_pci_add_device);244 245struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev)246{247	struct edac_pci_ctl_info *pci;248 249	edac_dbg(0, "\n");250 251	mutex_lock(&edac_pci_ctls_mutex);252 253	/* ensure the control struct is on the global list254	 * if not, then leave255	 */256	pci = find_edac_pci_by_dev(dev);257	if (pci  == NULL) {258		mutex_unlock(&edac_pci_ctls_mutex);259		return NULL;260	}261 262	pci->op_state = OP_OFFLINE;263 264	del_edac_pci_from_global_list(pci);265 266	mutex_unlock(&edac_pci_ctls_mutex);267 268	if (pci->edac_check)269		edac_stop_work(&pci->work);270 271	edac_printk(KERN_INFO, EDAC_PCI,272		"Removed device %d for %s %s: DEV %s\n",273		pci->pci_idx, pci->mod_name, pci->ctl_name, edac_dev_name(pci));274 275	return pci;276}277EXPORT_SYMBOL_GPL(edac_pci_del_device);278 279/*280 * edac_pci_generic_check281 *282 *	a Generic parity check API283 */284static void edac_pci_generic_check(struct edac_pci_ctl_info *pci)285{286	edac_dbg(4, "\n");287	edac_pci_do_parity_check();288}289 290/* free running instance index counter */291static int edac_pci_idx;292#define EDAC_PCI_GENCTL_NAME	"EDAC PCI controller"293 294struct edac_pci_gen_data {295	int edac_idx;296};297 298struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev,299						const char *mod_name)300{301	struct edac_pci_ctl_info *pci;302	struct edac_pci_gen_data *pdata;303 304	pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME);305	if (!pci)306		return NULL;307 308	pdata = pci->pvt_info;309	pci->dev = dev;310	dev_set_drvdata(pci->dev, pci);311	pci->dev_name = pci_name(to_pci_dev(dev));312 313	pci->mod_name = mod_name;314	pci->ctl_name = EDAC_PCI_GENCTL_NAME;315	if (edac_op_state == EDAC_OPSTATE_POLL)316		pci->edac_check = edac_pci_generic_check;317 318	pdata->edac_idx = edac_pci_idx++;319 320	if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {321		edac_dbg(3, "failed edac_pci_add_device()\n");322		edac_pci_free_ctl_info(pci);323		return NULL;324	}325 326	return pci;327}328EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl);329 330void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci)331{332	edac_dbg(0, "pci mod=%s\n", pci->mod_name);333 334	edac_pci_del_device(pci->dev);335	edac_pci_free_ctl_info(pci);336}337EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl);338