[PATCH] ptp: remove ptp->n_vclocks check logic in ptp_vclock_in_use()

Jeongjun Park posted 1 patch 7 months ago
There is a newer version of this series
drivers/ptp/ptp_clock.c   |  3 +--
drivers/ptp/ptp_private.h | 12 +-----------
2 files changed, 2 insertions(+), 13 deletions(-)
[PATCH] ptp: remove ptp->n_vclocks check logic in ptp_vclock_in_use()
Posted by Jeongjun Park 7 months ago
There is no disagreement that we should check both ptp->is_virtual_clock
and ptp->n_vclocks to check if the ptp virtual clock is in use.

However, when we acquire ptp->n_vclocks_mux to read ptp->n_vclocks in
ptp_vclock_in_use(), we observe a recursive lock in the call trace
starting from n_vclocks_store().

============================================
WARNING: possible recursive locking detected
6.15.0-rc6 #1 Not tainted
--------------------------------------------
syz.0.1540/13807 is trying to acquire lock:
ffff888035a24868 (&ptp->n_vclocks_mux){+.+.}-{4:4}, at:
 ptp_vclock_in_use drivers/ptp/ptp_private.h:103 [inline]
ffff888035a24868 (&ptp->n_vclocks_mux){+.+.}-{4:4}, at:
 ptp_clock_unregister+0x21/0x250 drivers/ptp/ptp_clock.c:415

but task is already holding lock:
ffff888030704868 (&ptp->n_vclocks_mux){+.+.}-{4:4}, at:
 n_vclocks_store+0xf1/0x6d0 drivers/ptp/ptp_sysfs.c:215

other info that might help us debug this:
 Possible unsafe locking scenario:

       CPU0
       ----
  lock(&ptp->n_vclocks_mux);
  lock(&ptp->n_vclocks_mux);

 *** DEADLOCK ***
....
============================================

The best way to solve this is to remove the logic that checks
ptp->n_vclocks in ptp_vclock_in_use().

The reason why this is appropriate is that any path that uses
ptp->n_vclocks must unconditionally check if ptp->n_vclocks is greater
than 0 before unregistering vclocks, and all functions are already
written this way. And in the function that uses ptp->n_vclocks, we
already get ptp->n_vclocks_mux before unregistering vclocks.

Therefore, we need to remove the redundant check for ptp->n_vclocks in
ptp_vclock_in_use() to prevent recursive locking.

Fixes: 73f37068d540 ("ptp: support ptp physical/virtual clocks conversion")
Signed-off-by: Jeongjun Park <aha310510@gmail.com>
---
 drivers/ptp/ptp_clock.c   |  3 +--
 drivers/ptp/ptp_private.h | 12 +-----------
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 35a5994bf64f..0ae9f074fc52 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -412,9 +412,8 @@ static int unregister_vclock(struct device *dev, void *data)
 
 int ptp_clock_unregister(struct ptp_clock *ptp)
 {
-	if (ptp_vclock_in_use(ptp)) {
+	if (ptp_vclock_in_use(ptp))
 		device_for_each_child(&ptp->dev, NULL, unregister_vclock);
-	}
 
 	ptp->defunct = 1;
 	wake_up_interruptible(&ptp->tsev_wq);
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index 18934e28469e..528d86a33f37 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -98,17 +98,7 @@ static inline int queue_cnt(const struct timestamp_event_queue *q)
 /* Check if ptp virtual clock is in use */
 static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
 {
-	bool in_use = false;
-
-	if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
-		return true;
-
-	if (!ptp->is_virtual_clock && ptp->n_vclocks)
-		in_use = true;
-
-	mutex_unlock(&ptp->n_vclocks_mux);
-
-	return in_use;
+	return !ptp->is_virtual_clock;
 }
 
 /* Check if ptp clock shall be free running */
--
Re: [PATCH] ptp: remove ptp->n_vclocks check logic in ptp_vclock_in_use()
Posted by Richard Cochran 7 months ago
On Tue, May 20, 2025 at 12:37:35AM +0900, Jeongjun Park wrote:

> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> index 35a5994bf64f..0ae9f074fc52 100644
> --- a/drivers/ptp/ptp_clock.c
> +++ b/drivers/ptp/ptp_clock.c
> @@ -412,9 +412,8 @@ static int unregister_vclock(struct device *dev, void *data)
>  
>  int ptp_clock_unregister(struct ptp_clock *ptp)
>  {
> -	if (ptp_vclock_in_use(ptp)) {
> +	if (ptp_vclock_in_use(ptp))
>  		device_for_each_child(&ptp->dev, NULL, unregister_vclock);
> -	}
>  
>  	ptp->defunct = 1;
>  	wake_up_interruptible(&ptp->tsev_wq);

This hunk is not related to the subject of the patch.  Please remove it.

Thanks,
Richard
Re: [PATCH] ptp: remove ptp->n_vclocks check logic in ptp_vclock_in_use()
Posted by Jeongjun Park 7 months ago
Richard Cochran <richardcochran@gmail.com> wrote:
>
> On Tue, May 20, 2025 at 12:37:35AM +0900, Jeongjun Park wrote:
>
> > diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> > index 35a5994bf64f..0ae9f074fc52 100644
> > --- a/drivers/ptp/ptp_clock.c
> > +++ b/drivers/ptp/ptp_clock.c
> > @@ -412,9 +412,8 @@ static int unregister_vclock(struct device *dev, void *data)
> >
> >  int ptp_clock_unregister(struct ptp_clock *ptp)
> >  {
> > -     if (ptp_vclock_in_use(ptp)) {
> > +     if (ptp_vclock_in_use(ptp))
> >               device_for_each_child(&ptp->dev, NULL, unregister_vclock);
> > -     }
> >
> >       ptp->defunct = 1;
> >       wake_up_interruptible(&ptp->tsev_wq);
>
> This hunk is not related to the subject of the patch.  Please remove it.
>
> Thanks,
> Richard
>

While working on the patch, I noticed an unnecessary pair of braces in
ptp_clock_unregister() and included their removal in the patch. Since
you’ve pointed out that this isn’t the right approach, I’ll fix it
immediately and send over the v2 patch.

Regards,

Jeongjun Park