[S390] qdio: prevent race for shared indicators

If the shared indicator is used the following race leads to
an inbound stall:

Device                  CPU0                    CPU1
========================================================

non-shared DSCI =>1
ALSI => 1
                        Thin INT
                        ALSI => 0

                        non-shared DSCI
                        tasklets scheduled

shared DSCI => 1
ALSI => 1

                        shared DSCI => 0
                        ALSI ? -> set
                                                Thin INT
                                                ALSI => 0
                        ALSI was set,
                        shared DSCI => 1

After that no more interrupts occur because the DSCI is still set.
Fix that race by only resetting the shared DSCI if it was actually
set so the tasklets for all shared devices are scheduled and will
run after the interrupt.

Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Jan Glauber 2011-01-05 12:47:49 +01:00 committed by Martin Schwidefsky
parent 078f8ecaa3
commit 4f325184f2
3 changed files with 13 additions and 49 deletions

View File

@ -423,9 +423,9 @@ struct indicator_t {
extern struct indicator_t *q_indicators;
static inline int shared_ind(struct qdio_irq *irq_ptr)
static inline int shared_ind(u32 *dsci)
{
return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
return dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
}
/* prototypes for thin interrupt */

View File

@ -1552,7 +1552,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
WARN_ON(queue_irqs_enabled(q));
if (!shared_ind(q->irq_ptr))
if (!shared_ind(q->irq_ptr->dsci))
xchg(q->irq_ptr->dsci, 0);
qdio_stop_polling(q);
@ -1562,7 +1562,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
* We need to check again to not lose initiative after
* resetting the ACK state.
*/
if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci)
if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci)
goto rescan;
if (!qdio_inbound_q_done(q))
goto rescan;

View File

@ -36,22 +36,8 @@ static u8 *tiqdio_alsi;
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;
register unsigned long __tmp asm("2");
register unsigned long __time asm("3");
asm volatile(
" .insn rre,0xb2650000,2,0"
: "+d" (__fn), "=d" (__tmp), "=d" (__time));
return __time;
}
/* returns addr for the device state change indicator */
static u32 *get_indicator(void)
{
@ -84,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
struct qdio_q *q;
int i;
/* No TDD facility? If we must use SIGA-s we can also omit SVS. */
if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync)
css_qdio_omit_svs = 1;
mutex_lock(&tiq_list_lock);
for_each_input_queue(irq_ptr, q, i)
list_add_rcu(&q->entry, &tiq_list);
@ -113,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
}
}
static inline int shared_ind_used(void)
static inline u32 shared_ind_set(void)
{
return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count);
return q_indicators[TIQDIO_SHARED_IND].ind;
}
/**
@ -125,22 +107,12 @@ static inline int shared_ind_used(void)
*/
static void tiqdio_thinint_handler(void *alsi, void *data)
{
u32 si_used = shared_ind_set();
struct qdio_q *q;
last_ai_time = S390_lowcore.int_clock;
kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++;
/*
* SVS only when needed: issue SVS to benefit from iqdio interrupt
* avoidance (SVS clears adapter interrupt suppression overwrite).
*/
if (!css_qdio_omit_svs)
do_clear_global_summary();
/* 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();
@ -148,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
list_for_each_entry_rcu(q, &tiq_list, entry) {
/* only process queues from changed sets */
if (!*q->irq_ptr->dsci)
if (unlikely(shared_ind(q->irq_ptr->dsci))) {
if (!si_used)
continue;
} else if (!*q->irq_ptr->dsci)
continue;
if (q->u.in.queue_start_poll) {
@ -164,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
q->irq_ptr->int_parm);
} else {
/* only clear it if the indicator is non-shared */
if (!shared_ind(q->irq_ptr))
if (!shared_ind(q->irq_ptr->dsci))
xchg(q->irq_ptr->dsci, 0);
/*
* Call inbound processing but not directly
@ -180,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data)
* If the shared indicator was used clear it now after all queues
* were processed.
*/
if (shared_ind_used()) {
if (si_used && shared_ind_set())
xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
/* prevent racing */
if (*tiqdio_alsi)
xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7);
}
}
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
@ -271,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr)
{
if (!is_thinint_irq(irq_ptr))
return 0;
/* Check for aif time delay disablement. If installed,
* omit SVS even under LPAR
*/
if (css_general_characteristics.aif_tdd)
css_qdio_omit_svs = 1;
return set_subchannel_ind(irq_ptr, 0);
}