Commit 9575499d authored by Helge Deller's avatar Helge Deller Committed by Dmitry Torokhov

Input: HIL - fix rwlock recursion bug

The following bug happens when insmoding hp_sdc_mlc.ko:

    HP SDC MLC: Registering the System Domain Controller's HIL MLC.
    BUG: rwlock recursion on CPU#0, hotplug/1814, 00854734
    Backtrace:
     [<10267560>] _raw_write_lock+0x50/0x88
     [<10104008>] _write_lock_irqsave+0x14/0x24
     [<008537d4>] hp_sdc_mlc_out+0x38/0x25c [hp_sdc_mlc]
     [<0084ebd8>] hilse_donode+0x308/0x470 [hil_mlc]
     [<0084ed80>] hil_mlcs_process+0x40/0x6c [hil_mlc]
     [<10130f80>] tasklet_action+0x78/0xb8
     [<10130cec>] __do_softirq+0x60/0xcc
     [<1010428c>] __lock_text_end+0x38/0x48
     [<10108348>] do_cpu_irq_mask+0xf0/0x11c
     [<1010b068>] intr_return+0x0/0xc
Signed-off-by: default avatarHelge Deller <deller@gmx.de>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 5a90e5bc
...@@ -716,7 +716,9 @@ static int hilse_donode(hil_mlc *mlc) ...@@ -716,7 +716,9 @@ static int hilse_donode(hil_mlc *mlc)
break; break;
case HILSE_CTS: case HILSE_CTS:
write_lock_irqsave(&mlc->lock, flags);
nextidx = mlc->cts(mlc) ? node->bad : node->good; nextidx = mlc->cts(mlc) ? node->bad : node->good;
write_unlock_irqrestore(&mlc->lock, flags);
break; break;
default: default:
......
...@@ -100,6 +100,7 @@ EXPORT_SYMBOL(hp_sdc_release_timer_irq); ...@@ -100,6 +100,7 @@ EXPORT_SYMBOL(hp_sdc_release_timer_irq);
EXPORT_SYMBOL(hp_sdc_release_hil_irq); EXPORT_SYMBOL(hp_sdc_release_hil_irq);
EXPORT_SYMBOL(hp_sdc_release_cooked_irq); EXPORT_SYMBOL(hp_sdc_release_cooked_irq);
EXPORT_SYMBOL(__hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
EXPORT_SYMBOL(hp_sdc_dequeue_transaction); EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
...@@ -593,18 +594,15 @@ unsigned long hp_sdc_put(void) ...@@ -593,18 +594,15 @@ unsigned long hp_sdc_put(void)
} }
/******* Functions called in either user or kernel context ****/ /******* Functions called in either user or kernel context ****/
int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this)
{ {
unsigned long flags;
int i; int i;
if (this == NULL) { if (this == NULL) {
tasklet_schedule(&hp_sdc.task); BUG();
return -EINVAL; return -EINVAL;
} }
write_lock_irqsave(&hp_sdc.lock, flags);
/* Can't have same transaction on queue twice */ /* Can't have same transaction on queue twice */
for (i = 0; i < HP_SDC_QUEUE_LEN; i++) for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
if (hp_sdc.tq[i] == this) if (hp_sdc.tq[i] == this)
...@@ -617,21 +615,29 @@ int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) ...@@ -617,21 +615,29 @@ int hp_sdc_enqueue_transaction(hp_sdc_transaction *this)
for (i = 0; i < HP_SDC_QUEUE_LEN; i++) for (i = 0; i < HP_SDC_QUEUE_LEN; i++)
if (hp_sdc.tq[i] == NULL) { if (hp_sdc.tq[i] == NULL) {
hp_sdc.tq[i] = this; hp_sdc.tq[i] = this;
write_unlock_irqrestore(&hp_sdc.lock, flags);
tasklet_schedule(&hp_sdc.task); tasklet_schedule(&hp_sdc.task);
return 0; return 0;
} }
write_unlock_irqrestore(&hp_sdc.lock, flags);
printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); printk(KERN_WARNING PREFIX "No free slot to add transaction.\n");
return -EBUSY; return -EBUSY;
fail: fail:
write_unlock_irqrestore(&hp_sdc.lock,flags);
printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n");
return -EINVAL; return -EINVAL;
} }
int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
unsigned long flags;
int ret;
write_lock_irqsave(&hp_sdc.lock, flags);
ret = __hp_sdc_enqueue_transaction(this);
write_unlock_irqrestore(&hp_sdc.lock,flags);
return ret;
}
int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) int hp_sdc_dequeue_transaction(hp_sdc_transaction *this)
{ {
unsigned long flags; unsigned long flags;
......
...@@ -142,14 +142,11 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id, ...@@ -142,14 +142,11 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id,
static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
{ {
unsigned long flags;
struct hp_sdc_mlc_priv_s *priv; struct hp_sdc_mlc_priv_s *priv;
int rc = 2; int rc = 2;
priv = mlc->priv; priv = mlc->priv;
write_lock_irqsave(&mlc->lock, flags);
/* Try to down the semaphore */ /* Try to down the semaphore */
if (down_trylock(&mlc->isem)) { if (down_trylock(&mlc->isem)) {
struct timeval tv; struct timeval tv;
...@@ -178,21 +175,16 @@ static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) ...@@ -178,21 +175,16 @@ static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
wasup: wasup:
up(&mlc->isem); up(&mlc->isem);
rc = 0; rc = 0;
goto done;
done: done:
write_unlock_irqrestore(&mlc->lock, flags);
return rc; return rc;
} }
static int hp_sdc_mlc_cts(hil_mlc *mlc) static int hp_sdc_mlc_cts(hil_mlc *mlc)
{ {
struct hp_sdc_mlc_priv_s *priv; struct hp_sdc_mlc_priv_s *priv;
unsigned long flags;
priv = mlc->priv; priv = mlc->priv;
write_lock_irqsave(&mlc->lock, flags);
/* Try to down the semaphores -- they should be up. */ /* Try to down the semaphores -- they should be up. */
BUG_ON(down_trylock(&mlc->isem)); BUG_ON(down_trylock(&mlc->isem));
BUG_ON(down_trylock(&mlc->osem)); BUG_ON(down_trylock(&mlc->osem));
...@@ -221,26 +213,21 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) ...@@ -221,26 +213,21 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc)
priv->tseq[2] = 1; priv->tseq[2] = 1;
priv->tseq[3] = 0; priv->tseq[3] = 0;
priv->tseq[4] = 0; priv->tseq[4] = 0;
hp_sdc_enqueue_transaction(&priv->trans); __hp_sdc_enqueue_transaction(&priv->trans);
busy: busy:
write_unlock_irqrestore(&mlc->lock, flags);
return 1; return 1;
done: done:
priv->trans.act.semaphore = &mlc->osem; priv->trans.act.semaphore = &mlc->osem;
up(&mlc->csem); up(&mlc->csem);
write_unlock_irqrestore(&mlc->lock, flags);
return 0; return 0;
} }
static void hp_sdc_mlc_out(hil_mlc *mlc) static void hp_sdc_mlc_out(hil_mlc *mlc)
{ {
struct hp_sdc_mlc_priv_s *priv; struct hp_sdc_mlc_priv_s *priv;
unsigned long flags;
priv = mlc->priv; priv = mlc->priv;
write_lock_irqsave(&mlc->lock, flags);
/* Try to down the semaphore -- it should be up. */ /* Try to down the semaphore -- it should be up. */
BUG_ON(down_trylock(&mlc->osem)); BUG_ON(down_trylock(&mlc->osem));
...@@ -250,7 +237,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) ...@@ -250,7 +237,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
do_data: do_data:
if (priv->emtestmode) { if (priv->emtestmode) {
up(&mlc->osem); up(&mlc->osem);
goto done; return;
} }
/* Shouldn't be sending commands when loop may be busy */ /* Shouldn't be sending commands when loop may be busy */
BUG_ON(down_trylock(&mlc->csem)); BUG_ON(down_trylock(&mlc->csem));
...@@ -313,8 +300,6 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) ...@@ -313,8 +300,6 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
} }
enqueue: enqueue:
hp_sdc_enqueue_transaction(&priv->trans); hp_sdc_enqueue_transaction(&priv->trans);
done:
write_unlock_irqrestore(&mlc->lock, flags);
} }
static int __init hp_sdc_mlc_init(void) static int __init hp_sdc_mlc_init(void)
......
...@@ -71,6 +71,7 @@ typedef struct { ...@@ -71,6 +71,7 @@ typedef struct {
struct semaphore *semaphore; /* Semaphore to sleep on. */ struct semaphore *semaphore; /* Semaphore to sleep on. */
} act; } act;
} hp_sdc_transaction; } hp_sdc_transaction;
int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this);
int hp_sdc_enqueue_transaction(hp_sdc_transaction *this); int hp_sdc_enqueue_transaction(hp_sdc_transaction *this);
int hp_sdc_dequeue_transaction(hp_sdc_transaction *this); int hp_sdc_dequeue_transaction(hp_sdc_transaction *this);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment