Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    Support for adapter interruptions
0004  *
0005  *    Copyright IBM Corp. 1999, 2007
0006  *    Author(s): Ingo Adlung <adlung@de.ibm.com>
0007  *       Cornelia Huck <cornelia.huck@de.ibm.com>
0008  *       Arnd Bergmann <arndb@de.ibm.com>
0009  *       Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
0010  */
0011 
0012 #include <linux/init.h>
0013 #include <linux/irq.h>
0014 #include <linux/kernel_stat.h>
0015 #include <linux/module.h>
0016 #include <linux/mutex.h>
0017 #include <linux/rculist.h>
0018 #include <linux/slab.h>
0019 #include <linux/dmapool.h>
0020 
0021 #include <asm/airq.h>
0022 #include <asm/isc.h>
0023 #include <asm/cio.h>
0024 
0025 #include "cio.h"
0026 #include "cio_debug.h"
0027 #include "ioasm.h"
0028 
0029 static DEFINE_SPINLOCK(airq_lists_lock);
0030 static struct hlist_head airq_lists[MAX_ISC+1];
0031 
0032 static struct dma_pool *airq_iv_cache;
0033 
0034 /**
0035  * register_adapter_interrupt() - register adapter interrupt handler
0036  * @airq: pointer to adapter interrupt descriptor
0037  *
0038  * Returns 0 on success, or -EINVAL.
0039  */
0040 int register_adapter_interrupt(struct airq_struct *airq)
0041 {
0042     char dbf_txt[32];
0043 
0044     if (!airq->handler || airq->isc > MAX_ISC)
0045         return -EINVAL;
0046     if (!airq->lsi_ptr) {
0047         airq->lsi_ptr = cio_dma_zalloc(1);
0048         if (!airq->lsi_ptr)
0049             return -ENOMEM;
0050         airq->flags |= AIRQ_PTR_ALLOCATED;
0051     }
0052     if (!airq->lsi_mask)
0053         airq->lsi_mask = 0xff;
0054     snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq);
0055     CIO_TRACE_EVENT(4, dbf_txt);
0056     isc_register(airq->isc);
0057     spin_lock(&airq_lists_lock);
0058     hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]);
0059     spin_unlock(&airq_lists_lock);
0060     return 0;
0061 }
0062 EXPORT_SYMBOL(register_adapter_interrupt);
0063 
0064 /**
0065  * unregister_adapter_interrupt - unregister adapter interrupt handler
0066  * @airq: pointer to adapter interrupt descriptor
0067  */
0068 void unregister_adapter_interrupt(struct airq_struct *airq)
0069 {
0070     char dbf_txt[32];
0071 
0072     if (hlist_unhashed(&airq->list))
0073         return;
0074     snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq);
0075     CIO_TRACE_EVENT(4, dbf_txt);
0076     spin_lock(&airq_lists_lock);
0077     hlist_del_rcu(&airq->list);
0078     spin_unlock(&airq_lists_lock);
0079     synchronize_rcu();
0080     isc_unregister(airq->isc);
0081     if (airq->flags & AIRQ_PTR_ALLOCATED) {
0082         cio_dma_free(airq->lsi_ptr, 1);
0083         airq->lsi_ptr = NULL;
0084         airq->flags &= ~AIRQ_PTR_ALLOCATED;
0085     }
0086 }
0087 EXPORT_SYMBOL(unregister_adapter_interrupt);
0088 
0089 static irqreturn_t do_airq_interrupt(int irq, void *dummy)
0090 {
0091     struct tpi_info *tpi_info;
0092     struct airq_struct *airq;
0093     struct hlist_head *head;
0094 
0095     set_cpu_flag(CIF_NOHZ_DELAY);
0096     tpi_info = &get_irq_regs()->tpi_info;
0097     trace_s390_cio_adapter_int(tpi_info);
0098     head = &airq_lists[tpi_info->isc];
0099     rcu_read_lock();
0100     hlist_for_each_entry_rcu(airq, head, list)
0101         if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
0102             airq->handler(airq, tpi_info);
0103     rcu_read_unlock();
0104 
0105     return IRQ_HANDLED;
0106 }
0107 
0108 void __init init_airq_interrupts(void)
0109 {
0110     irq_set_chip_and_handler(THIN_INTERRUPT,
0111                  &dummy_irq_chip, handle_percpu_irq);
0112     if (request_irq(THIN_INTERRUPT, do_airq_interrupt, 0, "AIO", NULL))
0113         panic("Failed to register AIO interrupt\n");
0114 }
0115 
0116 static inline unsigned long iv_size(unsigned long bits)
0117 {
0118     return BITS_TO_LONGS(bits) * sizeof(unsigned long);
0119 }
0120 
0121 /**
0122  * airq_iv_create - create an interrupt vector
0123  * @bits: number of bits in the interrupt vector
0124  * @flags: allocation flags
0125  * @vec: pointer to pinned guest memory if AIRQ_IV_GUESTVEC
0126  *
0127  * Returns a pointer to an interrupt vector structure
0128  */
0129 struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags,
0130                    unsigned long *vec)
0131 {
0132     struct airq_iv *iv;
0133     unsigned long size;
0134 
0135     iv = kzalloc(sizeof(*iv), GFP_KERNEL);
0136     if (!iv)
0137         goto out;
0138     iv->bits = bits;
0139     iv->flags = flags;
0140     size = iv_size(bits);
0141 
0142     if (flags & AIRQ_IV_CACHELINE) {
0143         if ((cache_line_size() * BITS_PER_BYTE) < bits
0144                 || !airq_iv_cache)
0145             goto out_free;
0146 
0147         iv->vector = dma_pool_zalloc(airq_iv_cache, GFP_KERNEL,
0148                          &iv->vector_dma);
0149         if (!iv->vector)
0150             goto out_free;
0151     } else if (flags & AIRQ_IV_GUESTVEC) {
0152         iv->vector = vec;
0153     } else {
0154         iv->vector = cio_dma_zalloc(size);
0155         if (!iv->vector)
0156             goto out_free;
0157     }
0158     if (flags & AIRQ_IV_ALLOC) {
0159         iv->avail = kmalloc(size, GFP_KERNEL);
0160         if (!iv->avail)
0161             goto out_free;
0162         memset(iv->avail, 0xff, size);
0163         iv->end = 0;
0164     } else
0165         iv->end = bits;
0166     if (flags & AIRQ_IV_BITLOCK) {
0167         iv->bitlock = kzalloc(size, GFP_KERNEL);
0168         if (!iv->bitlock)
0169             goto out_free;
0170     }
0171     if (flags & AIRQ_IV_PTR) {
0172         size = bits * sizeof(unsigned long);
0173         iv->ptr = kzalloc(size, GFP_KERNEL);
0174         if (!iv->ptr)
0175             goto out_free;
0176     }
0177     if (flags & AIRQ_IV_DATA) {
0178         size = bits * sizeof(unsigned int);
0179         iv->data = kzalloc(size, GFP_KERNEL);
0180         if (!iv->data)
0181             goto out_free;
0182     }
0183     spin_lock_init(&iv->lock);
0184     return iv;
0185 
0186 out_free:
0187     kfree(iv->ptr);
0188     kfree(iv->bitlock);
0189     kfree(iv->avail);
0190     if (iv->flags & AIRQ_IV_CACHELINE && iv->vector)
0191         dma_pool_free(airq_iv_cache, iv->vector, iv->vector_dma);
0192     else if (!(iv->flags & AIRQ_IV_GUESTVEC))
0193         cio_dma_free(iv->vector, size);
0194     kfree(iv);
0195 out:
0196     return NULL;
0197 }
0198 EXPORT_SYMBOL(airq_iv_create);
0199 
0200 /**
0201  * airq_iv_release - release an interrupt vector
0202  * @iv: pointer to interrupt vector structure
0203  */
0204 void airq_iv_release(struct airq_iv *iv)
0205 {
0206     kfree(iv->data);
0207     kfree(iv->ptr);
0208     kfree(iv->bitlock);
0209     if (iv->flags & AIRQ_IV_CACHELINE)
0210         dma_pool_free(airq_iv_cache, iv->vector, iv->vector_dma);
0211     else if (!(iv->flags & AIRQ_IV_GUESTVEC))
0212         cio_dma_free(iv->vector, iv_size(iv->bits));
0213     kfree(iv->avail);
0214     kfree(iv);
0215 }
0216 EXPORT_SYMBOL(airq_iv_release);
0217 
0218 /**
0219  * airq_iv_alloc - allocate irq bits from an interrupt vector
0220  * @iv: pointer to an interrupt vector structure
0221  * @num: number of consecutive irq bits to allocate
0222  *
0223  * Returns the bit number of the first irq in the allocated block of irqs,
0224  * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been
0225  * specified
0226  */
0227 unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
0228 {
0229     unsigned long bit, i, flags;
0230 
0231     if (!iv->avail || num == 0)
0232         return -1UL;
0233     spin_lock_irqsave(&iv->lock, flags);
0234     bit = find_first_bit_inv(iv->avail, iv->bits);
0235     while (bit + num <= iv->bits) {
0236         for (i = 1; i < num; i++)
0237             if (!test_bit_inv(bit + i, iv->avail))
0238                 break;
0239         if (i >= num) {
0240             /* Found a suitable block of irqs */
0241             for (i = 0; i < num; i++)
0242                 clear_bit_inv(bit + i, iv->avail);
0243             if (bit + num >= iv->end)
0244                 iv->end = bit + num + 1;
0245             break;
0246         }
0247         bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1);
0248     }
0249     if (bit + num > iv->bits)
0250         bit = -1UL;
0251     spin_unlock_irqrestore(&iv->lock, flags);
0252     return bit;
0253 }
0254 EXPORT_SYMBOL(airq_iv_alloc);
0255 
0256 /**
0257  * airq_iv_free - free irq bits of an interrupt vector
0258  * @iv: pointer to interrupt vector structure
0259  * @bit: number of the first irq bit to free
0260  * @num: number of consecutive irq bits to free
0261  */
0262 void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
0263 {
0264     unsigned long i, flags;
0265 
0266     if (!iv->avail || num == 0)
0267         return;
0268     spin_lock_irqsave(&iv->lock, flags);
0269     for (i = 0; i < num; i++) {
0270         /* Clear (possibly left over) interrupt bit */
0271         clear_bit_inv(bit + i, iv->vector);
0272         /* Make the bit positions available again */
0273         set_bit_inv(bit + i, iv->avail);
0274     }
0275     if (bit + num >= iv->end) {
0276         /* Find new end of bit-field */
0277         while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail))
0278             iv->end--;
0279     }
0280     spin_unlock_irqrestore(&iv->lock, flags);
0281 }
0282 EXPORT_SYMBOL(airq_iv_free);
0283 
0284 /**
0285  * airq_iv_scan - scan interrupt vector for non-zero bits
0286  * @iv: pointer to interrupt vector structure
0287  * @start: bit number to start the search
0288  * @end: bit number to end the search
0289  *
0290  * Returns the bit number of the next non-zero interrupt bit, or
0291  * -1UL if the scan completed without finding any more any non-zero bits.
0292  */
0293 unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
0294                unsigned long end)
0295 {
0296     unsigned long bit;
0297 
0298     /* Find non-zero bit starting from 'ivs->next'. */
0299     bit = find_next_bit_inv(iv->vector, end, start);
0300     if (bit >= end)
0301         return -1UL;
0302     clear_bit_inv(bit, iv->vector);
0303     return bit;
0304 }
0305 EXPORT_SYMBOL(airq_iv_scan);
0306 
0307 int __init airq_init(void)
0308 {
0309     airq_iv_cache = dma_pool_create("airq_iv_cache", cio_get_dma_css_dev(),
0310                     cache_line_size(),
0311                     cache_line_size(), PAGE_SIZE);
0312     if (!airq_iv_cache)
0313         return -ENOMEM;
0314     return 0;
0315 }