Browse Source

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>
Sebastian Ott 11 years ago
parent
commit
0eb69a0c58
1 changed files with 6 additions and 7 deletions
  1. 6 7
      drivers/s390/cio/airq.c

+ 6 - 7
drivers/s390/cio/airq.c

@@ -196,11 +196,11 @@ EXPORT_SYMBOL(airq_iv_release);
  */
 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)
 		return -1UL;
-	spin_lock(&iv->lock);
+	spin_lock_irqsave(&iv->lock, flags);
 	bit = find_first_bit_inv(iv->avail, iv->bits);
 	while (bit + num <= iv->bits) {
 		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)
 		bit = -1UL;
-	spin_unlock(&iv->lock);
+	spin_unlock_irqrestore(&iv->lock, flags);
 	return bit;
-
 }
 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)
 {
-	unsigned long i;
+	unsigned long i, flags;
 
 	if (!iv->avail || num == 0)
 		return;
-	spin_lock(&iv->lock);
+	spin_lock_irqsave(&iv->lock, flags);
 	for (i = 0; i < num; i++) {
 		/* Clear (possibly left over) interrupt bit */
 		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))
 			iv->end--;
 	}
-	spin_unlock(&iv->lock);
+	spin_unlock_irqrestore(&iv->lock, flags);
 }
 EXPORT_SYMBOL(airq_iv_free);