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) | |||
716 | break; | 716 | break; |
717 | 717 | ||
718 | case HILSE_CTS: | 718 | case HILSE_CTS: |
719 | write_lock_irqsave(&mlc->lock, flags); | ||
719 | nextidx = mlc->cts(mlc) ? node->bad : node->good; | 720 | nextidx = mlc->cts(mlc) ? node->bad : node->good; |
721 | write_unlock_irqrestore(&mlc->lock, flags); | ||
720 | break; | 722 | break; |
721 | 723 | ||
722 | default: | 724 | 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); | |||
100 | EXPORT_SYMBOL(hp_sdc_release_hil_irq); | 100 | EXPORT_SYMBOL(hp_sdc_release_hil_irq); |
101 | EXPORT_SYMBOL(hp_sdc_release_cooked_irq); | 101 | EXPORT_SYMBOL(hp_sdc_release_cooked_irq); |
102 | 102 | ||
103 | EXPORT_SYMBOL(__hp_sdc_enqueue_transaction); | ||
103 | EXPORT_SYMBOL(hp_sdc_enqueue_transaction); | 104 | EXPORT_SYMBOL(hp_sdc_enqueue_transaction); |
104 | EXPORT_SYMBOL(hp_sdc_dequeue_transaction); | 105 | EXPORT_SYMBOL(hp_sdc_dequeue_transaction); |
105 | 106 | ||
@@ -593,18 +594,15 @@ unsigned long hp_sdc_put(void) | |||
593 | } | 594 | } |
594 | 595 | ||
595 | /******* Functions called in either user or kernel context ****/ | 596 | /******* Functions called in either user or kernel context ****/ |
596 | int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) | 597 | int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this) |
597 | { | 598 | { |
598 | unsigned long flags; | ||
599 | int i; | 599 | int i; |
600 | 600 | ||
601 | if (this == NULL) { | 601 | if (this == NULL) { |
602 | tasklet_schedule(&hp_sdc.task); | 602 | BUG(); |
603 | return -EINVAL; | 603 | return -EINVAL; |
604 | } | 604 | } |
605 | 605 | ||
606 | write_lock_irqsave(&hp_sdc.lock, flags); | ||
607 | |||
608 | /* Can't have same transaction on queue twice */ | 606 | /* Can't have same transaction on queue twice */ |
609 | for (i = 0; i < HP_SDC_QUEUE_LEN; i++) | 607 | for (i = 0; i < HP_SDC_QUEUE_LEN; i++) |
610 | if (hp_sdc.tq[i] == this) | 608 | if (hp_sdc.tq[i] == this) |
@@ -617,21 +615,29 @@ int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) | |||
617 | for (i = 0; i < HP_SDC_QUEUE_LEN; i++) | 615 | for (i = 0; i < HP_SDC_QUEUE_LEN; i++) |
618 | if (hp_sdc.tq[i] == NULL) { | 616 | if (hp_sdc.tq[i] == NULL) { |
619 | hp_sdc.tq[i] = this; | 617 | hp_sdc.tq[i] = this; |
620 | write_unlock_irqrestore(&hp_sdc.lock, flags); | ||
621 | tasklet_schedule(&hp_sdc.task); | 618 | tasklet_schedule(&hp_sdc.task); |
622 | return 0; | 619 | return 0; |
623 | } | 620 | } |
624 | 621 | ||
625 | write_unlock_irqrestore(&hp_sdc.lock, flags); | ||
626 | printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); | 622 | printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); |
627 | return -EBUSY; | 623 | return -EBUSY; |
628 | 624 | ||
629 | fail: | 625 | fail: |
630 | write_unlock_irqrestore(&hp_sdc.lock,flags); | ||
631 | printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); | 626 | printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); |
632 | return -EINVAL; | 627 | return -EINVAL; |
633 | } | 628 | } |
634 | 629 | ||
630 | int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { | ||
631 | unsigned long flags; | ||
632 | int ret; | ||
633 | |||
634 | write_lock_irqsave(&hp_sdc.lock, flags); | ||
635 | ret = __hp_sdc_enqueue_transaction(this); | ||
636 | write_unlock_irqrestore(&hp_sdc.lock,flags); | ||
637 | |||
638 | return ret; | ||
639 | } | ||
640 | |||
635 | int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) | 641 | int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) |
636 | { | 642 | { |
637 | unsigned long flags; | 643 | 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, | |||
142 | 142 | ||
143 | static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) | 143 | static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) |
144 | { | 144 | { |
145 | unsigned long flags; | ||
146 | struct hp_sdc_mlc_priv_s *priv; | 145 | struct hp_sdc_mlc_priv_s *priv; |
147 | int rc = 2; | 146 | int rc = 2; |
148 | 147 | ||
149 | priv = mlc->priv; | 148 | priv = mlc->priv; |
150 | 149 | ||
151 | write_lock_irqsave(&mlc->lock, flags); | ||
152 | |||
153 | /* Try to down the semaphore */ | 150 | /* Try to down the semaphore */ |
154 | if (down_trylock(&mlc->isem)) { | 151 | if (down_trylock(&mlc->isem)) { |
155 | struct timeval tv; | 152 | struct timeval tv; |
@@ -178,21 +175,16 @@ static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) | |||
178 | wasup: | 175 | wasup: |
179 | up(&mlc->isem); | 176 | up(&mlc->isem); |
180 | rc = 0; | 177 | rc = 0; |
181 | goto done; | ||
182 | done: | 178 | done: |
183 | write_unlock_irqrestore(&mlc->lock, flags); | ||
184 | return rc; | 179 | return rc; |
185 | } | 180 | } |
186 | 181 | ||
187 | static int hp_sdc_mlc_cts(hil_mlc *mlc) | 182 | static int hp_sdc_mlc_cts(hil_mlc *mlc) |
188 | { | 183 | { |
189 | struct hp_sdc_mlc_priv_s *priv; | 184 | struct hp_sdc_mlc_priv_s *priv; |
190 | unsigned long flags; | ||
191 | 185 | ||
192 | priv = mlc->priv; | 186 | priv = mlc->priv; |
193 | 187 | ||
194 | write_lock_irqsave(&mlc->lock, flags); | ||
195 | |||
196 | /* Try to down the semaphores -- they should be up. */ | 188 | /* Try to down the semaphores -- they should be up. */ |
197 | BUG_ON(down_trylock(&mlc->isem)); | 189 | BUG_ON(down_trylock(&mlc->isem)); |
198 | BUG_ON(down_trylock(&mlc->osem)); | 190 | BUG_ON(down_trylock(&mlc->osem)); |
@@ -221,26 +213,21 @@ static int hp_sdc_mlc_cts(hil_mlc *mlc) | |||
221 | priv->tseq[2] = 1; | 213 | priv->tseq[2] = 1; |
222 | priv->tseq[3] = 0; | 214 | priv->tseq[3] = 0; |
223 | priv->tseq[4] = 0; | 215 | priv->tseq[4] = 0; |
224 | hp_sdc_enqueue_transaction(&priv->trans); | 216 | __hp_sdc_enqueue_transaction(&priv->trans); |
225 | busy: | 217 | busy: |
226 | write_unlock_irqrestore(&mlc->lock, flags); | ||
227 | return 1; | 218 | return 1; |
228 | done: | 219 | done: |
229 | priv->trans.act.semaphore = &mlc->osem; | 220 | priv->trans.act.semaphore = &mlc->osem; |
230 | up(&mlc->csem); | 221 | up(&mlc->csem); |
231 | write_unlock_irqrestore(&mlc->lock, flags); | ||
232 | return 0; | 222 | return 0; |
233 | } | 223 | } |
234 | 224 | ||
235 | static void hp_sdc_mlc_out(hil_mlc *mlc) | 225 | static void hp_sdc_mlc_out(hil_mlc *mlc) |
236 | { | 226 | { |
237 | struct hp_sdc_mlc_priv_s *priv; | 227 | struct hp_sdc_mlc_priv_s *priv; |
238 | unsigned long flags; | ||
239 | 228 | ||
240 | priv = mlc->priv; | 229 | priv = mlc->priv; |
241 | 230 | ||
242 | write_lock_irqsave(&mlc->lock, flags); | ||
243 | |||
244 | /* Try to down the semaphore -- it should be up. */ | 231 | /* Try to down the semaphore -- it should be up. */ |
245 | BUG_ON(down_trylock(&mlc->osem)); | 232 | BUG_ON(down_trylock(&mlc->osem)); |
246 | 233 | ||
@@ -250,7 +237,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) | |||
250 | do_data: | 237 | do_data: |
251 | if (priv->emtestmode) { | 238 | if (priv->emtestmode) { |
252 | up(&mlc->osem); | 239 | up(&mlc->osem); |
253 | goto done; | 240 | return; |
254 | } | 241 | } |
255 | /* Shouldn't be sending commands when loop may be busy */ | 242 | /* Shouldn't be sending commands when loop may be busy */ |
256 | BUG_ON(down_trylock(&mlc->csem)); | 243 | BUG_ON(down_trylock(&mlc->csem)); |
@@ -313,8 +300,6 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) | |||
313 | } | 300 | } |
314 | enqueue: | 301 | enqueue: |
315 | hp_sdc_enqueue_transaction(&priv->trans); | 302 | hp_sdc_enqueue_transaction(&priv->trans); |
316 | done: | ||
317 | write_unlock_irqrestore(&mlc->lock, flags); | ||
318 | } | 303 | } |
319 | 304 | ||
320 | static int __init hp_sdc_mlc_init(void) | 305 | 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 { | |||
71 | struct semaphore *semaphore; /* Semaphore to sleep on. */ | 71 | struct semaphore *semaphore; /* Semaphore to sleep on. */ |
72 | } act; | 72 | } act; |
73 | } hp_sdc_transaction; | 73 | } hp_sdc_transaction; |
74 | int __hp_sdc_enqueue_transaction(hp_sdc_transaction *this); | ||
74 | int hp_sdc_enqueue_transaction(hp_sdc_transaction *this); | 75 | int hp_sdc_enqueue_transaction(hp_sdc_transaction *this); |
75 | int hp_sdc_dequeue_transaction(hp_sdc_transaction *this); | 76 | int hp_sdc_dequeue_transaction(hp_sdc_transaction *this); |
76 | 77 | ||