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/mutex.h> 12 #include <linux/kthread.h> 13 #include <linux/init.h> 14 #include <linux/wait.h> 15 #include <asm/crw.h> 16 17 static DEFINE_MUTEX(crw_handler_mutex); 18 static crw_handler_t crw_handlers[NR_RSCS]; 19 static atomic_t crw_nr_req = ATOMIC_INIT(0); 20 static DECLARE_WAIT_QUEUE_HEAD(crw_handler_wait_q); 21 22 /** 23 * crw_register_handler() - register a channel report word handler 24 * @rsc: reporting source code to handle 25 * @handler: handler to be registered 26 * 27 * Returns %0 on success and a negative error value otherwise. 28 */ 29 int crw_register_handler(int rsc, crw_handler_t handler) 30 { 31 int rc = 0; 32 33 if ((rsc < 0) || (rsc >= NR_RSCS)) 34 return -EINVAL; 35 mutex_lock(&crw_handler_mutex); 36 if (crw_handlers[rsc]) 37 rc = -EBUSY; 38 else 39 crw_handlers[rsc] = handler; 40 mutex_unlock(&crw_handler_mutex); 41 return rc; 42 } 43 44 /** 45 * crw_unregister_handler() - unregister a channel report word handler 46 * @rsc: reporting source code to handle 47 */ 48 void crw_unregister_handler(int rsc) 49 { 50 if ((rsc < 0) || (rsc >= NR_RSCS)) 51 return; 52 mutex_lock(&crw_handler_mutex); 53 crw_handlers[rsc] = NULL; 54 mutex_unlock(&crw_handler_mutex); 55 } 56 57 /* 58 * Retrieve CRWs and call function to handle event. 59 */ 60 static int crw_collect_info(void *unused) 61 { 62 struct crw crw[2]; 63 int ccode, signal; 64 unsigned int chain; 65 66 repeat: 67 signal = wait_event_interruptible(crw_handler_wait_q, 68 atomic_read(&crw_nr_req) > 0); 69 if (unlikely(signal)) 70 atomic_inc(&crw_nr_req); 71 chain = 0; 72 while (1) { 73 crw_handler_t handler; 74 75 if (unlikely(chain > 1)) { 76 struct crw tmp_crw; 77 78 printk(KERN_WARNING"%s: Code does not support more " 79 "than two chained crws; please report to " 80 "linux390@de.ibm.com!\n", __func__); 81 ccode = stcrw(&tmp_crw); 82 printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, " 83 "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", 84 __func__, tmp_crw.slct, tmp_crw.oflw, 85 tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc, 86 tmp_crw.erc, tmp_crw.rsid); 87 printk(KERN_WARNING"%s: This was crw number %x in the " 88 "chain\n", __func__, chain); 89 if (ccode != 0) 90 break; 91 chain = tmp_crw.chn ? chain + 1 : 0; 92 continue; 93 } 94 ccode = stcrw(&crw[chain]); 95 if (ccode != 0) 96 break; 97 printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, " 98 "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", 99 crw[chain].slct, crw[chain].oflw, crw[chain].chn, 100 crw[chain].rsc, crw[chain].anc, crw[chain].erc, 101 crw[chain].rsid); 102 /* Check for overflows. */ 103 if (crw[chain].oflw) { 104 int i; 105 106 pr_debug("%s: crw overflow detected!\n", __func__); 107 mutex_lock(&crw_handler_mutex); 108 for (i = 0; i < NR_RSCS; i++) { 109 if (crw_handlers[i]) 110 crw_handlers[i](NULL, NULL, 1); 111 } 112 mutex_unlock(&crw_handler_mutex); 113 chain = 0; 114 continue; 115 } 116 if (crw[0].chn && !chain) { 117 chain++; 118 continue; 119 } 120 mutex_lock(&crw_handler_mutex); 121 handler = crw_handlers[crw[chain].rsc]; 122 if (handler) 123 handler(&crw[0], chain ? &crw[1] : NULL, 0); 124 mutex_unlock(&crw_handler_mutex); 125 /* chain is always 0 or 1 here. */ 126 chain = crw[chain].chn ? chain + 1 : 0; 127 } 128 if (atomic_dec_and_test(&crw_nr_req)) 129 wake_up(&crw_handler_wait_q); 130 goto repeat; 131 return 0; 132 } 133 134 void crw_handle_channel_report(void) 135 { 136 atomic_inc(&crw_nr_req); 137 wake_up(&crw_handler_wait_q); 138 } 139 140 void crw_wait_for_channel_report(void) 141 { 142 crw_handle_channel_report(); 143 wait_event(crw_handler_wait_q, atomic_read(&crw_nr_req) == 0); 144 } 145 146 /* 147 * Machine checks for the channel subsystem must be enabled 148 * after the channel subsystem is initialized 149 */ 150 static int __init crw_machine_check_init(void) 151 { 152 struct task_struct *task; 153 154 task = kthread_run(crw_collect_info, NULL, "kmcheck"); 155 if (IS_ERR(task)) 156 return PTR_ERR(task); 157 ctl_set_bit(14, 28); /* enable channel report MCH */ 158 return 0; 159 } 160 device_initcall(crw_machine_check_init); 161