s390/airq: silence lockdep warning

airq_iv_(alloc|free) is called by some users with interrupts enabled
and by some with interrupts disabled which leads to the following
lockdep warning:

[ INFO: possible irq lock inversion dependency detected ]
3.14.0-15249-gbf29b7b-dirty #25 Not tainted
---------------------------------------------------------
insmod/2108 just changed the state of lock:
 (&(&iv->lock)->rlock){+.....}, at: [<000000000046ee3e>] airq_iv_alloc+0x62/0x228
but this lock was taken by another, HARDIRQ-READ-safe lock in the past:
 (&info->lock){.-.-..}

and interrupts could create inverse lock ordering between them.

other info that might help us debug this:
 Possible interrupt unsafe locking scenario:

       CPU0                    CPU1
       ----                    ----
  lock(&(&iv->lock)->rlock);
                               local_irq_disable();
                               lock(&info->lock);
                               lock(&(&iv->lock)->rlock);
  <Interrupt>
    lock(&info->lock);

 *** DEADLOCK ***

Although this is a false alarm (since each airq user consistently
calls these functions from the same context) fix this by ensuring
that interrupts are disabled when the airq lock is held.

Reported-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Sebastian Ott 2014-06-05 14:30:19 +02:00 committed by Martin Schwidefsky
parent 646f919e93
commit 0eb69a0c58

View File

@ -196,11 +196,11 @@ EXPORT_SYMBOL(airq_iv_release);
*/ */
unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num) unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
{ {
unsigned long bit, i; unsigned long bit, i, flags;
if (!iv->avail || num == 0) if (!iv->avail || num == 0)
return -1UL; return -1UL;
spin_lock(&iv->lock); spin_lock_irqsave(&iv->lock, flags);
bit = find_first_bit_inv(iv->avail, iv->bits); bit = find_first_bit_inv(iv->avail, iv->bits);
while (bit + num <= iv->bits) { while (bit + num <= iv->bits) {
for (i = 1; i < num; i++) for (i = 1; i < num; i++)
@ -218,9 +218,8 @@ unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num)
} }
if (bit + num > iv->bits) if (bit + num > iv->bits)
bit = -1UL; bit = -1UL;
spin_unlock(&iv->lock); spin_unlock_irqrestore(&iv->lock, flags);
return bit; return bit;
} }
EXPORT_SYMBOL(airq_iv_alloc); EXPORT_SYMBOL(airq_iv_alloc);
@ -232,11 +231,11 @@ EXPORT_SYMBOL(airq_iv_alloc);
*/ */
void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num) void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
{ {
unsigned long i; unsigned long i, flags;
if (!iv->avail || num == 0) if (!iv->avail || num == 0)
return; return;
spin_lock(&iv->lock); spin_lock_irqsave(&iv->lock, flags);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
/* Clear (possibly left over) interrupt bit */ /* Clear (possibly left over) interrupt bit */
clear_bit_inv(bit + i, iv->vector); clear_bit_inv(bit + i, iv->vector);
@ -248,7 +247,7 @@ void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num)
while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail)) while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail))
iv->end--; iv->end--;
} }
spin_unlock(&iv->lock); spin_unlock_irqrestore(&iv->lock, flags);
} }
EXPORT_SYMBOL(airq_iv_free); EXPORT_SYMBOL(airq_iv_free);