Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright IBM Corp. 2000, 2009
0004  * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
0005  *        Cornelia Huck <cornelia.huck@de.ibm.com>
0006  *        Jan Glauber <jang@linux.vnet.ibm.com>
0007  */
0008 #include <linux/io.h>
0009 #include <linux/slab.h>
0010 #include <linux/kernel_stat.h>
0011 #include <linux/atomic.h>
0012 #include <linux/rculist.h>
0013 
0014 #include <asm/debug.h>
0015 #include <asm/qdio.h>
0016 #include <asm/airq.h>
0017 #include <asm/isc.h>
0018 #include <asm/tpi.h>
0019 
0020 #include "cio.h"
0021 #include "ioasm.h"
0022 #include "qdio.h"
0023 #include "qdio_debug.h"
0024 
0025 /*
0026  * Restriction: only 63 iqdio subchannels would have its own indicator,
0027  * after that, subsequent subchannels share one indicator
0028  */
0029 #define TIQDIO_NR_NONSHARED_IND     63
0030 #define TIQDIO_NR_INDICATORS        (TIQDIO_NR_NONSHARED_IND + 1)
0031 #define TIQDIO_SHARED_IND       63
0032 
0033 /* device state change indicators */
0034 struct indicator_t {
0035     u32 ind;    /* u32 because of compare-and-swap performance */
0036     atomic_t count; /* use count, 0 or 1 for non-shared indicators */
0037 };
0038 
0039 /* list of thin interrupt input queues */
0040 static LIST_HEAD(tiq_list);
0041 static DEFINE_MUTEX(tiq_list_lock);
0042 
0043 static struct indicator_t *q_indicators;
0044 
0045 u64 last_ai_time;
0046 
0047 /* returns addr for the device state change indicator */
0048 static u32 *get_indicator(void)
0049 {
0050     int i;
0051 
0052     for (i = 0; i < TIQDIO_NR_NONSHARED_IND; i++)
0053         if (!atomic_cmpxchg(&q_indicators[i].count, 0, 1))
0054             return &q_indicators[i].ind;
0055 
0056     /* use the shared indicator */
0057     atomic_inc(&q_indicators[TIQDIO_SHARED_IND].count);
0058     return &q_indicators[TIQDIO_SHARED_IND].ind;
0059 }
0060 
0061 static void put_indicator(u32 *addr)
0062 {
0063     struct indicator_t *ind = container_of(addr, struct indicator_t, ind);
0064 
0065     if (!addr)
0066         return;
0067     atomic_dec(&ind->count);
0068 }
0069 
0070 static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
0071 {
0072     return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
0073 }
0074 
0075 int test_nonshared_ind(struct qdio_irq *irq_ptr)
0076 {
0077     if (!is_thinint_irq(irq_ptr))
0078         return 0;
0079     if (references_shared_dsci(irq_ptr))
0080         return 0;
0081     if (*irq_ptr->dsci)
0082         return 1;
0083     else
0084         return 0;
0085 }
0086 
0087 static inline u32 clear_shared_ind(void)
0088 {
0089     if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count))
0090         return 0;
0091     return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
0092 }
0093 
0094 /**
0095  * tiqdio_thinint_handler - thin interrupt handler for qdio
0096  * @airq: pointer to adapter interrupt descriptor
0097  * @tpi_info: interrupt information (e.g. floating vs directed -- unused)
0098  */
0099 static void tiqdio_thinint_handler(struct airq_struct *airq,
0100                    struct tpi_info *tpi_info)
0101 {
0102     u64 irq_time = S390_lowcore.int_clock;
0103     u32 si_used = clear_shared_ind();
0104     struct qdio_irq *irq;
0105 
0106     last_ai_time = irq_time;
0107     inc_irq_stat(IRQIO_QAI);
0108 
0109     /* protect tiq_list entries, only changed in activate or shutdown */
0110     rcu_read_lock();
0111 
0112     list_for_each_entry_rcu(irq, &tiq_list, entry) {
0113         /* only process queues from changed sets */
0114         if (unlikely(references_shared_dsci(irq))) {
0115             if (!si_used)
0116                 continue;
0117         } else {
0118             if (!*irq->dsci)
0119                 continue;
0120 
0121             xchg(irq->dsci, 0);
0122         }
0123 
0124         qdio_deliver_irq(irq);
0125         irq->last_data_irq_time = irq_time;
0126 
0127         QDIO_PERF_STAT_INC(irq, adapter_int);
0128     }
0129     rcu_read_unlock();
0130 }
0131 
0132 static struct airq_struct tiqdio_airq = {
0133     .handler = tiqdio_thinint_handler,
0134     .isc = QDIO_AIRQ_ISC,
0135 };
0136 
0137 static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
0138 {
0139     struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
0140     u64 summary_indicator_addr, subchannel_indicator_addr;
0141     int rc;
0142 
0143     if (reset) {
0144         summary_indicator_addr = 0;
0145         subchannel_indicator_addr = 0;
0146     } else {
0147         summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr);
0148         subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci);
0149     }
0150 
0151     rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr,
0152                subchannel_indicator_addr, tiqdio_airq.isc);
0153     if (rc) {
0154         DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
0155               scssc->response.code);
0156         goto out;
0157     }
0158 
0159     DBF_EVENT("setscind");
0160     DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr));
0161     DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr));
0162 out:
0163     return rc;
0164 }
0165 
0166 int qdio_establish_thinint(struct qdio_irq *irq_ptr)
0167 {
0168     int rc;
0169 
0170     if (!is_thinint_irq(irq_ptr))
0171         return 0;
0172 
0173     irq_ptr->dsci = get_indicator();
0174     DBF_HEX(&irq_ptr->dsci, sizeof(void *));
0175 
0176     rc = set_subchannel_ind(irq_ptr, 0);
0177     if (rc) {
0178         put_indicator(irq_ptr->dsci);
0179         return rc;
0180     }
0181 
0182     mutex_lock(&tiq_list_lock);
0183     list_add_rcu(&irq_ptr->entry, &tiq_list);
0184     mutex_unlock(&tiq_list_lock);
0185     return 0;
0186 }
0187 
0188 void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
0189 {
0190     if (!is_thinint_irq(irq_ptr))
0191         return;
0192 
0193     mutex_lock(&tiq_list_lock);
0194     list_del_rcu(&irq_ptr->entry);
0195     mutex_unlock(&tiq_list_lock);
0196     synchronize_rcu();
0197 
0198     /* reset adapter interrupt indicators */
0199     set_subchannel_ind(irq_ptr, 1);
0200     put_indicator(irq_ptr->dsci);
0201 }
0202 
0203 int __init qdio_thinint_init(void)
0204 {
0205     int rc;
0206 
0207     q_indicators = kcalloc(TIQDIO_NR_INDICATORS, sizeof(struct indicator_t),
0208                    GFP_KERNEL);
0209     if (!q_indicators)
0210         return -ENOMEM;
0211 
0212     rc = register_adapter_interrupt(&tiqdio_airq);
0213     if (rc) {
0214         DBF_EVENT("RTI:%x", rc);
0215         kfree(q_indicators);
0216         return rc;
0217     }
0218     return 0;
0219 }
0220 
0221 void __exit qdio_thinint_exit(void)
0222 {
0223     WARN_ON(!list_empty(&tiq_list));
0224     unregister_adapter_interrupt(&tiqdio_airq);
0225     kfree(q_indicators);
0226 }