diff options
author | Helge Deller <deller@gmx.de> | 2007-03-16 00:59:29 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor@insightbb.com> | 2007-03-16 00:59:29 -0400 |
commit | 9575499dfebc0f0fbbf122223f02e9e92630661d (patch) | |
tree | d43f958bec192f127907ba393762a0a4728fea4c | |
parent | 5a90e5bca96696f1daa0bb0a9db299eb40241ada (diff) |
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: Helge Deller <deller@gmx.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/serio/hil_mlc.c | 2 | ||||
-rw-r--r-- | drivers/input/serio/hp_sdc.c | 22 | ||||
-rw-r--r-- | drivers/input/serio/hp_sdc_mlc.c | 19 | ||||
-rw-r--r-- | include/linux/hp_sdc.h | 1 |
4 files changed, 19 insertions, 25 deletions
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index 485b0742842b..93a1a6ba216a 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -716,7 +716,9 @@ static int hilse_donode(hil_mlc *mlc) break; case HILSE_CTS: + write_lock_irqsave(&mlc->lock, flags); nextidx = mlc->cts(mlc) ? node->bad : node->good; + write_unlock_irqrestore(&mlc->lock, flags); break; default: diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 31826e601fba..6af199805ffc 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -100,6 +100,7 @@ EXPORT_SYMBOL(hp_sdc_release_timer_irq); EXPORT_SYMBOL(hp_sdc_release_hil_irq); EXPORT_SYMBOL(hp_sdc_release_cooked_irq); +EXPORT_SYMBOL(__hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_enqueue_transaction); EXPORT_SYMBOL(hp_sdc_dequeue_transaction); @@ -593,18 +594,15 @@ unsigned long hp_sdc_put(void) } /******* 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; if (this == NULL) { - tasklet_schedule(&hp_sdc.task); + BUG(); return -EINVAL; } - write_lock_irqsave(&hp_sdc.lock, flags); - /* Can't have same transaction on queue twice */ for (i = 0; i < HP_SDC_QUEUE_LEN; i++) if (hp_sdc.tq[i] == this) @@ -617,21 +615,29 @@ int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) for (i = 0; i < HP_SDC_QUEUE_LEN; i++) if (hp_sdc.tq[i] == NULL) { hp_sdc.tq[i] = this; - write_unlock_irqrestore(&hp_sdc.lock, flags); tasklet_schedule(&hp_sdc.task); return 0; } - write_unlock_irqrestore(&hp_sdc.lock, flags); printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); return -EBUSY; fail: - write_unlock_irqrestore(&hp_sdc.lock,flags); printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); 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) { unsigned long flags; diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index cb0b28877e05..c45ea74d53e4 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -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) { - unsigned long flags; struct hp_sdc_mlc_priv_s *priv; int rc = 2; priv = mlc->priv; - write_lock_irqsave(&mlc->lock, flags); - /* Try to down the semaphore */ if (down_trylock(&mlc->isem)) { struct timeval tv; @@ -178,21 +175,16 @@ static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) wasup: up(&mlc->isem); rc = 0; - goto done; done: - write_unlock_irqrestore(&mlc->lock, flags); return rc; } static int hp_sdc_mlc_cts(hil_mlc *mlc) { struct hp_sdc_mlc_priv_s *priv; - unsigned long flags; priv = mlc->priv; - write_lock_irqsave(&mlc->lock, flags); - /* Try to down the semaphores -- they should be up. */ BUG_ON(down_trylock(&mlc->isem)); BUG_ON(down_trylock(&mlc->osem)); @@ -221,26 +213,21 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) priv->tseq[2] = 1; priv->tseq[3] = 0; priv->tseq[4] = 0; - hp_sdc_enqueue_transaction(&priv->trans); + __hp_sdc_enqueue_transaction(&priv->trans); busy: - write_unlock_irqrestore(&mlc->lock, flags); return 1; done: priv->trans.act.semaphore = &mlc->osem; up(&mlc->csem); - write_unlock_irqrestore(&mlc->lock, flags); return 0; } static void hp_sdc_mlc_out(hil_mlc *mlc) { struct hp_sdc_mlc_priv_s *priv; - unsigned long flags; priv = mlc->priv; - write_lock_irqsave(&mlc->lock, flags); - /* Try to down the semaphore -- it should be up. */ BUG_ON(down_trylock(&mlc->osem)); @@ -250,7 +237,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) do_data: if (priv->emtestmode) { up(&mlc->osem); - goto done; + return; } /* Shouldn't be sending commands when loop may be busy */ BUG_ON(down_trylock(&mlc->csem)); @@ -313,8 +300,6 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) } enqueue: hp_sdc_enqueue_transaction(&priv->trans); - done: - write_unlock_irqrestore(&mlc->lock, flags); } static int __init hp_sdc_mlc_init(void) diff --git a/include/linux/hp_sdc.h b/include/linux/hp_sdc.h index debd71515312..9db3d454887f 100644 --- a/include/linux/hp_sdc.h +++ b/include/linux/hp_sdc.h @@ -71,6 +71,7 @@ typedef struct { struct semaphore *semaphore; /* Semaphore to sleep on. */ } act; } hp_sdc_transaction; +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); |