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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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:
+14 −8
Original line number Diff line number Diff line
@@ -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;
+2 −17
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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);