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>
This commit is contained in:
parent
5a90e5bca9
commit
9575499dfe
|
@ -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);
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/******* 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)
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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 {
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue