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