[S390] split/move machine check handler code
[linux-block.git] / drivers / s390 / cio / crw.c
1 /*
2  *   Channel report handling code
3  *
4  *    Copyright IBM Corp. 2000,2009
5  *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
6  *               Martin Schwidefsky <schwidefsky@de.ibm.com>,
7  *               Cornelia Huck <cornelia.huck@de.ibm.com>,
8  *               Heiko Carstens <heiko.carstens@de.ibm.com>,
9  */
10
11 #include <linux/semaphore.h>
12 #include <linux/kthread.h>
13 #include <linux/init.h>
14 #include <asm/crw.h>
15
16 static struct semaphore crw_semaphore;
17 static crw_handler_t crw_handlers[NR_RSCS];
18
19 /**
20  * crw_register_handler() - register a channel report word handler
21  * @rsc: reporting source code to handle
22  * @handler: handler to be registered
23  *
24  * Returns %0 on success and a negative error value otherwise.
25  */
26 int crw_register_handler(int rsc, crw_handler_t handler)
27 {
28         if ((rsc < 0) || (rsc >= NR_RSCS))
29                 return -EINVAL;
30         if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
31                 return 0;
32         return -EBUSY;
33 }
34
35 /**
36  * crw_unregister_handler() - unregister a channel report word handler
37  * @rsc: reporting source code to handle
38  */
39 void crw_unregister_handler(int rsc)
40 {
41         if ((rsc < 0) || (rsc >= NR_RSCS))
42                 return;
43         xchg(&crw_handlers[rsc], NULL);
44         synchronize_sched();
45 }
46
47 /*
48  * Retrieve CRWs and call function to handle event.
49  */
50 static int crw_collect_info(void *unused)
51 {
52         struct crw crw[2];
53         int ccode;
54         unsigned int chain;
55         int ignore;
56
57 repeat:
58         ignore = down_interruptible(&crw_semaphore);
59         chain = 0;
60         while (1) {
61                 if (unlikely(chain > 1)) {
62                         struct crw tmp_crw;
63
64                         printk(KERN_WARNING"%s: Code does not support more "
65                                "than two chained crws; please report to "
66                                "linux390@de.ibm.com!\n", __func__);
67                         ccode = stcrw(&tmp_crw);
68                         printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
69                                "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
70                                __func__, tmp_crw.slct, tmp_crw.oflw,
71                                tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
72                                tmp_crw.erc, tmp_crw.rsid);
73                         printk(KERN_WARNING"%s: This was crw number %x in the "
74                                "chain\n", __func__, chain);
75                         if (ccode != 0)
76                                 break;
77                         chain = tmp_crw.chn ? chain + 1 : 0;
78                         continue;
79                 }
80                 ccode = stcrw(&crw[chain]);
81                 if (ccode != 0)
82                         break;
83                 printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
84                        "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
85                        crw[chain].slct, crw[chain].oflw, crw[chain].chn,
86                        crw[chain].rsc, crw[chain].anc, crw[chain].erc,
87                        crw[chain].rsid);
88                 /* Check for overflows. */
89                 if (crw[chain].oflw) {
90                         int i;
91
92                         pr_debug("%s: crw overflow detected!\n", __func__);
93                         for (i = 0; i < NR_RSCS; i++) {
94                                 if (crw_handlers[i])
95                                         crw_handlers[i](NULL, NULL, 1);
96                         }
97                         chain = 0;
98                         continue;
99                 }
100                 if (crw[0].chn && !chain) {
101                         chain++;
102                         continue;
103                 }
104                 if (crw_handlers[crw[chain].rsc])
105                         crw_handlers[crw[chain].rsc](&crw[0],
106                                                      chain ? &crw[1] : NULL,
107                                                      0);
108                 /* chain is always 0 or 1 here. */
109                 chain = crw[chain].chn ? chain + 1 : 0;
110         }
111         goto repeat;
112         return 0;
113 }
114
115 void crw_handle_channel_report(void)
116 {
117         up(&crw_semaphore);
118 }
119
120 /*
121  * Separate initcall needed for semaphore initialization since
122  * crw_handle_channel_report might be called before crw_machine_check_init.
123  */
124 static int __init crw_init_semaphore(void)
125 {
126         init_MUTEX_LOCKED(&crw_semaphore);
127         return 0;
128 }
129 pure_initcall(crw_init_semaphore);
130
131 /*
132  * Machine checks for the channel subsystem must be enabled
133  * after the channel subsystem is initialized
134  */
135 static int __init crw_machine_check_init(void)
136 {
137         struct task_struct *task;
138
139         task = kthread_run(crw_collect_info, NULL, "kmcheck");
140         if (IS_ERR(task))
141                 return PTR_ERR(task);
142         ctl_set_bit(14, 28);    /* enable channel report MCH */
143         return 0;
144 }
145 device_initcall(crw_machine_check_init);