net: dsa: mv88e6xxx: fix races between lock and irq freeing
free_irq() waits until all handlers for this IRQ have completed. As the relevant handler (mv88e6xxx_g1_irq_thread_fn()) takes the chip's reg_lock it might never return if the thread calling free_irq() holds this lock. For the same reason kthread_cancel_delayed_work_sync() in the polling case must not hold this lock. Also first free the irq (or stop the worker respectively) such that mv88e6xxx_g1_irq_thread_work() isn't called any more before the irq mappings are dropped in mv88e6xxx_g1_irq_free_common() to prevent the worker thread to call handle_nested_irq(0) which results in a NULL-pointer exception. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
ff907a11a0
Коммит
3d82475ad4
|
@ -343,6 +343,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
|
|||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
/* To be called with reg_lock held */
|
||||
static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
int irq, virq;
|
||||
|
@ -362,9 +363,15 @@ static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
|
|||
|
||||
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
|
||||
/*
|
||||
* free_irq must be called without reg_lock taken because the irq
|
||||
* handler takes this lock, too.
|
||||
*/
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip)
|
||||
|
@ -469,10 +476,12 @@ static int mv88e6xxx_irq_poll_setup(struct mv88e6xxx_chip *chip)
|
|||
|
||||
static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip)
|
||||
{
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
|
||||
kthread_cancel_delayed_work_sync(&chip->irq_poll_work);
|
||||
kthread_destroy_worker(chip->kworker);
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
mv88e6xxx_g1_irq_free_common(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
|
||||
|
@ -4506,12 +4515,10 @@ out_g2_irq:
|
|||
if (chip->info->g2_irqs > 0)
|
||||
mv88e6xxx_g2_irq_free(chip);
|
||||
out_g1_irq:
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_irq_free(chip);
|
||||
else
|
||||
mv88e6xxx_irq_poll_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
out:
|
||||
if (pdata)
|
||||
dev_put(pdata->netdev);
|
||||
|
@ -4539,12 +4546,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
|
|||
if (chip->info->g2_irqs > 0)
|
||||
mv88e6xxx_g2_irq_free(chip);
|
||||
|
||||
mutex_lock(&chip->reg_lock);
|
||||
if (chip->irq > 0)
|
||||
mv88e6xxx_g1_irq_free(chip);
|
||||
else
|
||||
mv88e6xxx_irq_poll_free(chip);
|
||||
mutex_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static const struct of_device_id mv88e6xxx_of_match[] = {
|
||||
|
|
Загрузка…
Ссылка в новой задаче