DSA core's dsa_switch_shutdown() only closes CPU port conduits and unlinks DSA uppers; it does not invoke ds->ops->teardown(). On .shutdown (reboot), the chip's mv88e6xxx_teardown() therefore never runs, and the PTP overflow / TAI event delayed works are not cancelled. They continue to fire as the conduit netdev is torn down by its own driver, racing the teardown and crashing in the RMU request path when conduit->dev_addr is released. Factor the work cancellation out of mv88e6xxx_ptp_free() into a new mv88e6xxx_ptp_shutdown() helper that leaves the PTP clock registered, and call it from mv88e6xxx_shutdown_common() before dsa_switch_shutdown(). mv88e6xxx_ptp_free() now calls the helper itself, so the regular .remove path is unchanged. Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 6 ++++++ drivers/net/dsa/mv88e6xxx/ptp.c | 14 ++++++++++++-- drivers/net/dsa/mv88e6xxx/ptp.h | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 531a59093ef97..3d92c59de7b5f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -7543,6 +7543,12 @@ static void mv88e6xxx_shutdown(struct mdio_device *mdiodev) if (!ds) return; + /* DSA core does not call ->teardown on .shutdown, so stop PTP + * polling here before the conduit goes away. Otherwise the work + * can race teardown and dereference a stale conduit->dev_addr. + */ + mv88e6xxx_ptp_shutdown(ds->priv); + dsa_switch_shutdown(ds); dev_set_drvdata(&mdiodev->dev, NULL); diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c index f7603573d3a98..f6ce3232d10ff 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.c +++ b/drivers/net/dsa/mv88e6xxx/ptp.c @@ -551,14 +551,24 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) return 0; } -/* This must never be called holding the register lock */ -void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) +/* Cancel any pending PTP polling work. Safe to call from .shutdown + * paths where the PTP clock itself stays registered. + * This must never be called holding the register lock. + */ +void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip) { if (chip->ptp_clock) { cancel_delayed_work_sync(&chip->overflow_work); if (chip->info->ops->ptp_ops->event_work) cancel_delayed_work_sync(&chip->tai_event_work); + } +} +/* This must never be called holding the register lock */ +void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) +{ + if (chip->ptp_clock) { + mv88e6xxx_ptp_shutdown(chip); ptp_clock_unregister(chip->ptp_clock); chip->ptp_clock = NULL; } diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h index 95bdddb0bf39f..90031b05d2514 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.h +++ b/drivers/net/dsa/mv88e6xxx/ptp.h @@ -68,6 +68,7 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip); +void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip); #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \ ptp_clock_info) @@ -87,6 +88,10 @@ static inline void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) { } +static inline void mv88e6xxx_ptp_shutdown(struct mv88e6xxx_chip *chip) +{ +} + static const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {}; static const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {}; static const struct mv88e6xxx_ptp_ops mv88e6390_ptp_ops = {}; -- 2.43.0