summaryrefslogtreecommitdiff
path: root/drivers/s390/cio/qdio_thinint.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/qdio_thinint.c')
-rw-r--r--drivers/s390/cio/qdio_thinint.c66
1 files changed, 38 insertions, 28 deletions
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 8daf1b99f153..752dbee06af5 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -25,24 +25,20 @@
*/
#define TIQDIO_NR_NONSHARED_IND 63
#define TIQDIO_NR_INDICATORS (TIQDIO_NR_NONSHARED_IND + 1)
-#define TIQDIO_SHARED_IND 63
/* list of thin interrupt input queues */
static LIST_HEAD(tiq_list);
DEFINE_MUTEX(tiq_list_lock);
/* adapter local summary indicator */
-static unsigned char *tiqdio_alsi;
+static u8 *tiqdio_alsi;
-/* device state change indicators */
-struct indicator_t {
- u32 ind; /* u32 because of compare-and-swap performance */
- atomic_t count; /* use count, 0 or 1 for non-shared indicators */
-};
-static struct indicator_t *q_indicators;
+struct indicator_t *q_indicators;
static int css_qdio_omit_svs;
+static u64 last_ai_time;
+
static inline unsigned long do_clear_global_summary(void)
{
register unsigned long __fn asm("1") = 3;
@@ -116,59 +112,73 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
}
}
-static inline int shared_ind(struct qdio_irq *irq_ptr)
+static inline int shared_ind_used(void)
{
- return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
+ return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count);
}
/**
* tiqdio_thinint_handler - thin interrupt handler for qdio
- * @ind: pointer to adapter local summary indicator
- * @drv_data: NULL
+ * @alsi: pointer to adapter local summary indicator
+ * @data: NULL
*/
-static void tiqdio_thinint_handler(void *ind, void *drv_data)
+static void tiqdio_thinint_handler(void *alsi, void *data)
{
struct qdio_q *q;
+ last_ai_time = S390_lowcore.int_clock;
+
/*
* SVS only when needed: issue SVS to benefit from iqdio interrupt
- * avoidance (SVS clears adapter interrupt suppression overwrite)
+ * avoidance (SVS clears adapter interrupt suppression overwrite).
*/
if (!css_qdio_omit_svs)
do_clear_global_summary();
- /*
- * reset local summary indicator (tiqdio_alsi) to stop adapter
- * interrupts for now
- */
- xchg((u8 *)ind, 0);
+ /* reset local summary indicator */
+ if (shared_ind_used())
+ xchg(tiqdio_alsi, 0);
/* protect tiq_list entries, only changed in activate or shutdown */
rcu_read_lock();
/* check for work on all inbound thinint queues */
- list_for_each_entry_rcu(q, &tiq_list, entry)
+ list_for_each_entry_rcu(q, &tiq_list, entry) {
+
/* only process queues from changed sets */
- if (*q->irq_ptr->dsci) {
- qperf_inc(q, adapter_int);
+ if (!*q->irq_ptr->dsci)
+ continue;
+ if (q->u.in.queue_start_poll) {
+ /* skip if polling is enabled or already in work */
+ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
+ &q->u.in.queue_irq_state)) {
+ qperf_inc(q, int_discarded);
+ continue;
+ }
+
+ /* avoid dsci clear here, done after processing */
+ q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr,
+ q->irq_ptr->int_parm);
+ } else {
/* only clear it if the indicator is non-shared */
if (!shared_ind(q->irq_ptr))
xchg(q->irq_ptr->dsci, 0);
/*
- * don't call inbound processing directly since
- * that could starve other thinint queues
+ * Call inbound processing but not directly
+ * since that could starve other thinint queues.
*/
tasklet_schedule(&q->tasklet);
}
-
+ qperf_inc(q, adapter_int);
+ }
rcu_read_unlock();
/*
- * if we used the shared indicator clear it now after all queues
- * were processed
+ * If the shared indicator was used clear it now after all queues
+ * were processed.
*/
- if (atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) {
+ if (shared_ind_used()) {
xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
/* prevent racing */