Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Helge Deller and committed by
Dmitry Torokhov
9575499d 5a90e5bc

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