diff options
Diffstat (limited to 'drivers/s390/s390mach.c')
-rw-r--r-- | drivers/s390/s390mach.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c new file mode 100644 index 000000000000..ffa996c8a908 --- /dev/null +++ b/drivers/s390/s390mach.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * drivers/s390/s390mach.c | ||
3 | * S/390 machine check handler | ||
4 | * | ||
5 | * S390 version | ||
6 | * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
7 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | ||
8 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/workqueue.h> | ||
16 | |||
17 | #include <asm/lowcore.h> | ||
18 | |||
19 | #include "s390mach.h" | ||
20 | |||
21 | #define DBG printk | ||
22 | // #define DBG(args,...) do {} while (0); | ||
23 | |||
24 | static struct semaphore m_sem; | ||
25 | |||
26 | extern int css_process_crw(int); | ||
27 | extern int chsc_process_crw(void); | ||
28 | extern int chp_process_crw(int, int); | ||
29 | extern void css_reiterate_subchannels(void); | ||
30 | |||
31 | extern struct workqueue_struct *slow_path_wq; | ||
32 | extern struct work_struct slow_path_work; | ||
33 | |||
34 | static void | ||
35 | s390_handle_damage(char *msg) | ||
36 | { | ||
37 | printk(KERN_EMERG "%s\n", msg); | ||
38 | #ifdef CONFIG_SMP | ||
39 | smp_send_stop(); | ||
40 | #endif | ||
41 | disabled_wait((unsigned long) __builtin_return_address(0)); | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * Retrieve CRWs and call function to handle event. | ||
46 | * | ||
47 | * Note : we currently process CRWs for io and chsc subchannels only | ||
48 | */ | ||
49 | static int | ||
50 | s390_collect_crw_info(void *param) | ||
51 | { | ||
52 | struct crw crw; | ||
53 | int ccode, ret, slow; | ||
54 | struct semaphore *sem; | ||
55 | |||
56 | sem = (struct semaphore *)param; | ||
57 | /* Set a nice name. */ | ||
58 | daemonize("kmcheck"); | ||
59 | repeat: | ||
60 | down_interruptible(sem); | ||
61 | slow = 0; | ||
62 | while (1) { | ||
63 | ccode = stcrw(&crw); | ||
64 | if (ccode != 0) | ||
65 | break; | ||
66 | DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " | ||
67 | "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | ||
68 | crw.slct, crw.oflw, crw.chn, crw.rsc, crw.anc, | ||
69 | crw.erc, crw.rsid); | ||
70 | /* Check for overflows. */ | ||
71 | if (crw.oflw) { | ||
72 | pr_debug("%s: crw overflow detected!\n", __FUNCTION__); | ||
73 | css_reiterate_subchannels(); | ||
74 | slow = 1; | ||
75 | continue; | ||
76 | } | ||
77 | switch (crw.rsc) { | ||
78 | case CRW_RSC_SCH: | ||
79 | pr_debug("source is subchannel %04X\n", crw.rsid); | ||
80 | ret = css_process_crw (crw.rsid); | ||
81 | if (ret == -EAGAIN) | ||
82 | slow = 1; | ||
83 | break; | ||
84 | case CRW_RSC_MONITOR: | ||
85 | pr_debug("source is monitoring facility\n"); | ||
86 | break; | ||
87 | case CRW_RSC_CPATH: | ||
88 | pr_debug("source is channel path %02X\n", crw.rsid); | ||
89 | switch (crw.erc) { | ||
90 | case CRW_ERC_IPARM: /* Path has come. */ | ||
91 | ret = chp_process_crw(crw.rsid, 1); | ||
92 | break; | ||
93 | case CRW_ERC_PERRI: /* Path has gone. */ | ||
94 | case CRW_ERC_PERRN: | ||
95 | ret = chp_process_crw(crw.rsid, 0); | ||
96 | break; | ||
97 | default: | ||
98 | pr_debug("Don't know how to handle erc=%x\n", | ||
99 | crw.erc); | ||
100 | ret = 0; | ||
101 | } | ||
102 | if (ret == -EAGAIN) | ||
103 | slow = 1; | ||
104 | break; | ||
105 | case CRW_RSC_CONFIG: | ||
106 | pr_debug("source is configuration-alert facility\n"); | ||
107 | break; | ||
108 | case CRW_RSC_CSS: | ||
109 | pr_debug("source is channel subsystem\n"); | ||
110 | ret = chsc_process_crw(); | ||
111 | if (ret == -EAGAIN) | ||
112 | slow = 1; | ||
113 | break; | ||
114 | default: | ||
115 | pr_debug("unknown source\n"); | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | if (slow) | ||
120 | queue_work(slow_path_wq, &slow_path_work); | ||
121 | goto repeat; | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * machine check handler. | ||
127 | */ | ||
128 | void | ||
129 | s390_do_machine_check(void) | ||
130 | { | ||
131 | struct mci *mci; | ||
132 | |||
133 | mci = (struct mci *) &S390_lowcore.mcck_interruption_code; | ||
134 | |||
135 | if (mci->sd) /* system damage */ | ||
136 | s390_handle_damage("received system damage machine check\n"); | ||
137 | |||
138 | if (mci->pd) /* instruction processing damage */ | ||
139 | s390_handle_damage("received instruction processing " | ||
140 | "damage machine check\n"); | ||
141 | |||
142 | if (mci->se) /* storage error uncorrected */ | ||
143 | s390_handle_damage("received storage error uncorrected " | ||
144 | "machine check\n"); | ||
145 | |||
146 | if (mci->sc) /* storage error corrected */ | ||
147 | printk(KERN_WARNING | ||
148 | "received storage error corrected machine check\n"); | ||
149 | |||
150 | if (mci->ke) /* storage key-error uncorrected */ | ||
151 | s390_handle_damage("received storage key-error uncorrected " | ||
152 | "machine check\n"); | ||
153 | |||
154 | if (mci->ds && mci->fa) /* storage degradation */ | ||
155 | s390_handle_damage("received storage degradation machine " | ||
156 | "check\n"); | ||
157 | |||
158 | if (mci->cp) /* channel report word pending */ | ||
159 | up(&m_sem); | ||
160 | |||
161 | #ifdef CONFIG_MACHCHK_WARNING | ||
162 | /* | ||
163 | * The warning may remain for a prolonged period on the bare iron. | ||
164 | * (actually till the machine is powered off, or until the problem is gone) | ||
165 | * So we just stop listening for the WARNING MCH and prevent continuously | ||
166 | * being interrupted. One caveat is however, that we must do this per | ||
167 | * processor and cannot use the smp version of ctl_clear_bit(). | ||
168 | * On VM we only get one interrupt per virtally presented machinecheck. | ||
169 | * Though one suffices, we may get one interrupt per (virtual) processor. | ||
170 | */ | ||
171 | if (mci->w) { /* WARNING pending ? */ | ||
172 | static int mchchk_wng_posted = 0; | ||
173 | /* | ||
174 | * Use single machine clear, as we cannot handle smp right now | ||
175 | */ | ||
176 | __ctl_clear_bit(14, 24); /* Disable WARNING MCH */ | ||
177 | if (xchg(&mchchk_wng_posted, 1) == 0) | ||
178 | kill_proc(1, SIGPWR, 1); | ||
179 | } | ||
180 | #endif | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * s390_init_machine_check | ||
185 | * | ||
186 | * initialize machine check handling | ||
187 | */ | ||
188 | static int | ||
189 | machine_check_init(void) | ||
190 | { | ||
191 | init_MUTEX_LOCKED(&m_sem); | ||
192 | ctl_clear_bit(14, 25); /* disable damage MCH */ | ||
193 | ctl_set_bit(14, 26); /* enable degradation MCH */ | ||
194 | ctl_set_bit(14, 27); /* enable system recovery MCH */ | ||
195 | #ifdef CONFIG_MACHCHK_WARNING | ||
196 | ctl_set_bit(14, 24); /* enable warning MCH */ | ||
197 | #endif | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Initialize the machine check handler really early to be able to | ||
203 | * catch all machine checks that happen during boot | ||
204 | */ | ||
205 | arch_initcall(machine_check_init); | ||
206 | |||
207 | /* | ||
208 | * Machine checks for the channel subsystem must be enabled | ||
209 | * after the channel subsystem is initialized | ||
210 | */ | ||
211 | static int __init | ||
212 | machine_check_crw_init (void) | ||
213 | { | ||
214 | kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES); | ||
215 | ctl_set_bit(14, 28); /* enable channel report MCH */ | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | device_initcall (machine_check_crw_init); | ||