aboutsummaryrefslogtreecommitdiff
path: root/drivers/edac/edac_module.c
blob: 8db0471a947684a01dc38884ba03ed027b3403cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <linux/freezer.h>
#include <linux/kthread.h>

#include "edac_mc.h"
#include "edac_module.h"

#define EDAC_MC_VERSION "Ver: 2.0.3" __DATE__

#ifdef CONFIG_EDAC_DEBUG
/* Values of 0 to 4 will generate output */
int edac_debug_level = 1;
EXPORT_SYMBOL_GPL(edac_debug_level);
#endif

static struct task_struct *edac_thread;

/*
 * Check MC status every edac_get_poll_msec().
 * Check PCI status every edac_get_poll_msec() as well.
 *
 * This where the work gets done for edac.
 *
 * SMP safe, doesn't use NMI, and auto-rate-limits.
 */
static void do_edac_check(void)
{
	debugf3("%s()\n", __func__);

	/* perform the poll activities */
	edac_check_mc_devices();
	edac_pci_do_parity_check();
}

/*
 * Action thread for EDAC to perform the POLL operations
 */
static int edac_kernel_thread(void *arg)
{
	int msec;

	while (!kthread_should_stop()) {

		do_edac_check();

		/* goto sleep for the interval */
		msec = (HZ * edac_get_poll_msec()) / 1000;
		schedule_timeout_interruptible(msec);
		try_to_freeze();
	}

	return 0;
}

/*
 * edac_init
 *      module initialization entry point
 */
static int __init edac_init(void)
{
	edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n");

	/*
	 * Harvest and clear any boot/initialization PCI parity errors
	 *
	 * FIXME: This only clears errors logged by devices present at time of
	 * 	module initialization.  We should also do an initial clear
	 *	of each newly hotplugged device.
	 */
	edac_pci_clear_parity_errors();

	/* Create the MC sysfs entries */
	if (edac_sysfs_memctrl_setup()) {
		edac_printk(KERN_ERR, EDAC_MC,
			"Error initializing sysfs code\n");
		return -ENODEV;
	}

	/* Create the PCI parity sysfs entries */
	if (edac_sysfs_pci_setup()) {
		edac_sysfs_memctrl_teardown();
		edac_printk(KERN_ERR, EDAC_MC,
			"PCI: Error initializing sysfs code\n");
		return -ENODEV;
	}

	/* create our kernel thread */
	edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac");

	if (IS_ERR(edac_thread)) {
		/* remove the sysfs entries */
		edac_sysfs_memctrl_teardown();
		edac_sysfs_pci_teardown();
		return PTR_ERR(edac_thread);
	}

	return 0;
}

/*
 * edac_exit()
 *      module exit/termination function
 */
static void __exit edac_exit(void)
{
	debugf0("%s()\n", __func__);
	kthread_stop(edac_thread);

	/* tear down the sysfs device */
	edac_sysfs_memctrl_teardown();
	edac_sysfs_pci_teardown();
}

/*
 * Inform the kernel of our entry and exit points
 */
module_init(edac_init);
module_exit(edac_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al");
MODULE_DESCRIPTION("Core library routines for EDAC reporting");

/* refer to *_sysfs.c files for parameters that are exported via sysfs */

#ifdef CONFIG_EDAC_DEBUG
module_param(edac_debug_level, int, 0644);
MODULE_PARM_DESC(edac_debug_level, "Debug level");
#endif