Commit 3d82475a authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by David S. Miller

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: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ff907a11
...@@ -343,6 +343,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = { ...@@ -343,6 +343,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell, .xlate = irq_domain_xlate_twocell,
}; };
/* To be called with reg_lock held */
static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip) static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
{ {
int irq, virq; int irq, virq;
...@@ -362,9 +363,15 @@ static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip) ...@@ -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) 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); 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) 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) ...@@ -469,10 +476,12 @@ static int mv88e6xxx_irq_poll_setup(struct mv88e6xxx_chip *chip)
static void mv88e6xxx_irq_poll_free(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_cancel_delayed_work_sync(&chip->irq_poll_work);
kthread_destroy_worker(chip->kworker); 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) int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
...@@ -4506,12 +4515,10 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) ...@@ -4506,12 +4515,10 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
if (chip->info->g2_irqs > 0) if (chip->info->g2_irqs > 0)
mv88e6xxx_g2_irq_free(chip); mv88e6xxx_g2_irq_free(chip);
out_g1_irq: out_g1_irq:
mutex_lock(&chip->reg_lock);
if (chip->irq > 0) if (chip->irq > 0)
mv88e6xxx_g1_irq_free(chip); mv88e6xxx_g1_irq_free(chip);
else else
mv88e6xxx_irq_poll_free(chip); mv88e6xxx_irq_poll_free(chip);
mutex_unlock(&chip->reg_lock);
out: out:
if (pdata) if (pdata)
dev_put(pdata->netdev); dev_put(pdata->netdev);
...@@ -4539,12 +4546,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) ...@@ -4539,12 +4546,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
if (chip->info->g2_irqs > 0) if (chip->info->g2_irqs > 0)
mv88e6xxx_g2_irq_free(chip); mv88e6xxx_g2_irq_free(chip);
mutex_lock(&chip->reg_lock);
if (chip->irq > 0) if (chip->irq > 0)
mv88e6xxx_g1_irq_free(chip); mv88e6xxx_g1_irq_free(chip);
else else
mv88e6xxx_irq_poll_free(chip); mv88e6xxx_irq_poll_free(chip);
mutex_unlock(&chip->reg_lock);
} }
static const struct of_device_id mv88e6xxx_of_match[] = { static const struct of_device_id mv88e6xxx_of_match[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment