linux/drivers/s390/cio/airq.c
<<
>>
Prefs
   1/*
   2 *    Support for adapter interruptions
   3 *
   4 *    Copyright IBM Corp. 1999, 2007
   5 *    Author(s): Ingo Adlung <adlung@de.ibm.com>
   6 *               Cornelia Huck <cornelia.huck@de.ibm.com>
   7 *               Arnd Bergmann <arndb@de.ibm.com>
   8 *               Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
   9 */
  10
  11#include <linux/init.h>
  12#include <linux/irq.h>
  13#include <linux/kernel_stat.h>
  14#include <linux/module.h>
  15#include <linux/mutex.h>
  16#include <linux/rculist.h>
  17#include <linux/slab.h>
  18#include <linux/dma-mapping.h>
  19
  20#include <asm/airq.h>
  21#include <asm/isc.h>
  22#include <asm/cio.h>
  23
  24#include "cio.h"
  25#include "cio_debug.h"
  26#include "ioasm.h"
  27
  28static DEFINE_SPINLOCK(airq_lists_lock);
  29static struct hlist_head airq_lists[MAX_ISC+1];
  30
  31/**
  32 * register_adapter_interrupt() - register adapter interrupt handler
  33 * @airq: pointer to adapter interrupt descriptor
  34 *
  35 * Returns 0 on success, or -EINVAL.
  36 */
  37int register_adapter_interrupt(struct airq_struct *airq)
  38{
  39        char dbf_txt[32];
  40
  41        if (!airq->handler || airq->isc > MAX_ISC)
  42                return -EINVAL;
  43        if (!airq->lsi_ptr) {
  44                airq->lsi_ptr = kzalloc(1, GFP_KERNEL);
  45                if (!airq->lsi_ptr)
  46                        return -ENOMEM;
  47                airq->flags |= AIRQ_PTR_ALLOCATED;
  48        }
  49        if (!airq->lsi_mask)
  50                airq->lsi_mask = 0xff;
  51        snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
  52        CIO_TRACE_EVENT(4, dbf_txt);
  53        isc_register(airq->isc);
  54        spin_lock(&airq_lists_lock);
  55        hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
  56        spin_unlock(&airq_lists_lock);
  57        return 0;
  58}
  59EXPORT_SYMBOL(register_adapter_interrupt);
  60
  61/**
  62 * unregister_adapter_interrupt - unregister adapter interrupt handler
  63 * @airq: pointer to adapter interrupt descriptor
  64 */
  65void unregister_adapter_interrupt(struct airq_struct *airq)
  66{
  67        char dbf_txt[32];
  68
  69        if (hlist_unhashed(&airq->list))
  70                return;
  71        snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
  72        CIO_TRACE_EVENT(4, dbf_txt);
  73        spin_lock(&airq_lists_lock);
  74        hlist_del_rcu(&airq->list);
  75        spin_unlock(&airq_lists_lock);
  76        synchronize_rcu();
  77        isc_unregister(airq->isc);
  78        if (airq->flags & AIRQ_PTR_ALLOCATED) {
  79                kfree(airq->lsi_ptr);
  80                airq->lsi_ptr = NULL;
  81                airq->flags &= ~AIRQ_PTR_ALLOCATED;
  82        }
  83}
  84EXPORT_SYMBOL(unregister_adapter_interrupt);
  85
  86void do_adapter_IO(u8 isc)
  87{
  88        struct airq_struct *airq;
  89        struct hlist_head *head;
  90
  91        head = &airq_lists[isc];
  92        rcu_read_lock();
  93        hlist_for_each_entry_rcu(airq, head, list)
  94                if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
  95                        airq->handler(airq);
  96        rcu_read_unlock();
  97}
  98
  99static inline unsigned long iv_size(unsigned long bits)
 100{
 101        return BITS_TO_LONGS(bits) * sizeof(unsigned long);
 102}
 103
 104/**
 105 * airq_iv_create - create an interrupt vector
 106 * @bits: number of bits in the interrupt vector
 107 * @flags: allocation flags
 108 *
 109 * Returns a pointer to an interrupt vector structure
 110 */
 111struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags)
 112{
 113        struct airq_iv *iv;
 114        unsigned long size;
 115
 116        iv = kzalloc(sizeof(*iv), GFP_KERNEL);
 117        if (!iv)
 118                goto out;
 119        iv->bits = bits;
 120        size = iv_size(bits);
 121        iv->vector = cio_dma_zalloc(size);
 122        if (!iv->vector)
 123                goto out_free;
 124        if (flags & AIRQ_IV_ALLOC) {
 125                iv->avail = kmalloc(size, GFP_KERNEL);
 126                if (!iv->avail)
 127                        goto out_free;
 128                memset(iv->avail, 0xff, size);
 129                iv->end = 0;
 130        } else
 131                iv->end = bits;
 132        if (flags & AIRQ_IV_BITLOCK) {
 133                iv->bitlock = kzalloc(size, GFP_KERNEL);
 134                if (!iv->bitlock)
 135                        goto out_free;
 136        }
 137        if (flags & AIRQ_IV_PTR) {
 138                size = bits * sizeof(unsigned long);
 139                iv->ptr = kzalloc(size, GFP_KERNEL);
 140                if (!iv->ptr)
 141                        goto out_free;
 142        }
 143        if (flags & AIRQ_IV_DATA) {
 144                size = bits * sizeof(unsigned int);
 145                iv->data = kzalloc(size, GFP_KERNEL);
 146                if (!iv->data)
 147                        goto out_free;
 148        }
 149        spin_lock_init(&iv->lock);
 150        return iv;
 151
 152out_free:
 153        kfree(iv->ptr);
 154        kfree(iv->bitlock);
 155        kfree(iv->avail);
 156        cio_dma_free(iv->vector, size);
 157        kfree(iv);
 158out:
 159        return NULL;
 160}
 161EXPORT_SYMBOL(airq_iv_create);
 162
 163/**
 164 * airq_iv_release - release an interrupt vector
 165 * @iv: pointer to interrupt vector structure
 166 */
 167void airq_iv_release(struct airq_iv *iv)
 168{
 169        kfree(iv->data);
 170        kfree(iv->ptr);
 171        kfree(iv->bitlock);
 172        cio_dma_free(iv->vector, iv_size(iv->bits));
 173        kfree(iv->avail);
 174        kfree(iv);
 175}
 176EXPORT_SYMBOL(airq_iv_release);
 177
 178/**
 179 * airq_iv_alloc - allocate irq bits from an interrupt vector
 180 * @iv: pointer to an interrupt vector structure
 181 * @num: number of consecutive irq bits to allocate
 182 *
 183 * Returns the bit number of the first irq in the allocated block of irqs,
 184 * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been
 185 * specified
 186 */
 187unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
 188{
 189        unsigned long bit, i, flags;
 190
 191        if (!iv->avail || num == 0)
 192                return -1UL;
 193        spin_lock_irqsave(&iv->lock, flags);
 194        bit = find_first_bit_inv(iv->avail, iv->bits);
 195        while (bit + num <= iv->bits) {
 196                for (i = 1; i < num; i++)
 197                        if (!test_bit_inv(bit + i, iv->avail))
 198                                break;
 199                if (i >= num) {
 200                        /* Found a suitable block of irqs */
 201                        for (i = 0; i < num; i++)
 202                                clear_bit_inv(bit + i, iv->avail);
 203                        if (bit + num >= iv->end)
 204                                iv->end = bit + num + 1;
 205                        break;
 206                }
 207                bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1);
 208        }
 209        if (bit + num > iv->bits)
 210                bit = -1UL;
 211        spin_unlock_irqrestore(&iv->lock, flags);
 212        return bit;
 213}
 214EXPORT_SYMBOL(airq_iv_alloc);
 215
 216/**
 217 * airq_iv_free - free irq bits of an interrupt vector
 218 * @iv: pointer to interrupt vector structure
 219 * @bit: number of the first irq bit to free
 220 * @num: number of consecutive irq bits to free
 221 */
 222void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
 223{
 224        unsigned long i, flags;
 225
 226        if (!iv->avail || num == 0)
 227                return;
 228        spin_lock_irqsave(&iv->lock, flags);
 229        for (i = 0; i < num; i++) {
 230                /* Clear (possibly left over) interrupt bit */
 231                clear_bit_inv(bit + i, iv->vector);
 232                /* Make the bit positions available again */
 233                set_bit_inv(bit + i, iv->avail);
 234        }
 235        if (bit + num >= iv->end) {
 236                /* Find new end of bit-field */
 237                while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail))
 238                        iv->end--;
 239        }
 240        spin_unlock_irqrestore(&iv->lock, flags);
 241}
 242EXPORT_SYMBOL(airq_iv_free);
 243
 244/**
 245 * airq_iv_scan - scan interrupt vector for non-zero bits
 246 * @iv: pointer to interrupt vector structure
 247 * @start: bit number to start the search
 248 * @end: bit number to end the search
 249 *
 250 * Returns the bit number of the next non-zero interrupt bit, or
 251 * -1UL if the scan completed without finding any more any non-zero bits.
 252 */
 253unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
 254                           unsigned long end)
 255{
 256        unsigned long bit;
 257
 258        /* Find non-zero bit starting from 'ivs->next'. */
 259        bit = find_next_bit_inv(iv->vector, end, start);
 260        if (bit >= end)
 261                return -1UL;
 262        clear_bit_inv(bit, iv->vector);
 263        return bit;
 264}
 265EXPORT_SYMBOL(airq_iv_scan);
 266