Accurately describe what each call to ptp_disable_pinfunc() is doing, rather than the misleading comment above the first disable. This helps to make the code more readable. Signed-off-by: Russell King (Oracle) --- drivers/ptp/ptp_chardev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index e9719f365aab..eb4f6d1b1460 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -91,12 +91,18 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, return -EOPNOTSUPP; } - /* Disable whatever function was previously assigned. */ + /* Disable whichever pin was previously assigned to this function and + * channel. + */ if (pin1) { ptp_disable_pinfunc(info, func, chan); pin1->func = PTP_PF_NONE; pin1->chan = 0; } + + /* Disable whatever function was previously assigned to the requested + * pin. + */ ptp_disable_pinfunc(info, pin2->func, pin2->chan); pin2->func = func; pin2->chan = chan; -- 2.47.3 the ordering of ptp_clock_unregister() is not ideal, as the chardev remains published while state is being torn down. There is also no cleanup of enabled pin settings, which means enabled events can still forward into the core. Rework the ordering of cleanup in ptp_clock_unregister() so that we unpublish the posix clock (and user chardev), disable any pins that have events enabled, and then clean up the aux work and PPS source. This avoids potential use-after-free and races in PTP clock driver teardown. Signed-off-by: Russell King (Oracle) --- drivers/ptp/ptp_chardev.c | 13 +++++++++++++ drivers/ptp/ptp_clock.c | 17 ++++++++++++++++- drivers/ptp/ptp_private.h | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index eb4f6d1b1460..640a98f17739 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -47,6 +47,19 @@ static int ptp_disable_pinfunc(struct ptp_clock_info *ops, return err; } +void ptp_disable_all_pins(struct ptp_clock *ptp) +{ + struct ptp_clock_info *info = ptp->info; + unsigned int i; + + mutex_lock(&ptp->pincfg_mux); + for (i = 0; i < info->n_pins; i++) + if (info->pin_config[i].func != PTP_PF_NONE) + ptp_disable_pinfunc(info, info->pin_config[i].func, + info->pin_config[i].chan); + mutex_unlock(&ptp->pincfg_mux); +} + int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, enum ptp_pin_function func, unsigned int chan) { diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 1d920f8e20a8..d2eb77081071 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -498,9 +498,23 @@ int ptp_clock_unregister(struct ptp_clock *ptp) device_for_each_child(&ptp->dev, NULL, unregister_vclock); } + /* Get the device to stop posix_clock_unregister() doing the last put + * and freeing the structure(s) + */ + get_device(&ptp->dev); + + /* Wake up any userspace waiting for an event. */ ptp->defunct = 1; wake_up_interruptible(&ptp->tsev_wq); + /* Tear down the POSIX clock, which removes the user interface. */ + posix_clock_unregister(&ptp->clock); + + /* Disable any pin functions that the user may have setup, quiescing + * all incoming events. + */ + ptp_disable_all_pins(ptp); + if (ptp->kworker) { kthread_cancel_delayed_work_sync(&ptp->aux_work); kthread_destroy_worker(ptp->kworker); @@ -510,7 +524,8 @@ int ptp_clock_unregister(struct ptp_clock *ptp) if (ptp->pps_source) pps_unregister_source(ptp->pps_source); - posix_clock_unregister(&ptp->clock); + /* The final put, normally here, will invoke ptp_clock_release(). */ + put_device(&ptp->dev); return 0; } diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h index b352df4cd3f9..9d90aace7e64 100644 --- a/drivers/ptp/ptp_private.h +++ b/drivers/ptp/ptp_private.h @@ -141,6 +141,8 @@ extern const struct class ptp_class; * see ptp_chardev.c */ +void ptp_disable_all_pins(struct ptp_clock *ptp); + /* caller must hold pincfg_mux */ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, enum ptp_pin_function func, unsigned int chan); -- 2.47.3