diff mbox series

[05/15] drm/panfrost: use spinlock instead of atomic

Message ID 20200510165538.19720-6-peron.clem@gmail.com (mailing list archive)
State New, archived
Headers show
Series Add regulator devfreq support to Panfrost | expand

Commit Message

Clément Péron May 10, 2020, 4:55 p.m. UTC
Convert busy_count to a simple int protected by spinlock.

Signed-off-by: Clément Péron <peron.clem@gmail.com>
---
 drivers/gpu/drm/panfrost/panfrost_devfreq.c | 43 +++++++++++++++------
 drivers/gpu/drm/panfrost/panfrost_devfreq.h | 10 ++++-
 2 files changed, 41 insertions(+), 12 deletions(-)

Comments

Steven Price May 28, 2020, 1:22 p.m. UTC | #1
On 10/05/2020 17:55, Clément Péron wrote:
> Convert busy_count to a simple int protected by spinlock.
> 
> Signed-off-by: Clément Péron <peron.clem@gmail.com>

Looks like a fairly mechanical cleanup.

Reviewed-by: Steven Price <steven.price@arm.com>

> ---
>   drivers/gpu/drm/panfrost/panfrost_devfreq.c | 43 +++++++++++++++------
>   drivers/gpu/drm/panfrost/panfrost_devfreq.h | 10 ++++-
>   2 files changed, 41 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
> index 962550363391..78753cfb59fb 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
> @@ -12,16 +12,12 @@
>   
>   static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfreq)
>   {
> -	ktime_t now;
> -	ktime_t last;
> -
> -	if (!pfdevfreq->devfreq)
> -		return;
> +	ktime_t now, last;
>   
>   	now = ktime_get();
>   	last = pfdevfreq->time_last_update;
>   
> -	if (atomic_read(&pfdevfreq->busy_count) > 0)
> +	if (pfdevfreq->busy_count > 0)
>   		pfdevfreq->busy_time += ktime_sub(now, last);
>   	else
>   		pfdevfreq->idle_time += ktime_sub(now, last);
> @@ -59,10 +55,14 @@ static int panfrost_devfreq_get_dev_status(struct device *dev,
>   {
>   	struct panfrost_device *pfdev = dev_get_drvdata(dev);
>   	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
> +	unsigned long irqflags;
> +
> +	status->current_frequency = clk_get_rate(pfdev->clock);
> +
> +	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
>   
>   	panfrost_devfreq_update_utilization(pfdevfreq);
>   
> -	status->current_frequency = clk_get_rate(pfdev->clock);
>   	status->total_time = ktime_to_ns(ktime_add(pfdevfreq->busy_time,
>   						   pfdevfreq->idle_time));
>   
> @@ -70,6 +70,8 @@ static int panfrost_devfreq_get_dev_status(struct device *dev,
>   
>   	panfrost_devfreq_reset(pfdevfreq);
>   
> +	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
> +
>   	dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
>   		status->busy_time, status->total_time,
>   		status->busy_time / (status->total_time / 100),
> @@ -100,6 +102,8 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
>   	else if (ret)
>   		return ret;
>   
> +	spin_lock_init(&pfdevfreq->lock);
> +
>   	panfrost_devfreq_reset(pfdevfreq);
>   
>   	cur_freq = clk_get_rate(pfdev->clock);
> @@ -162,15 +166,32 @@ void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
>   
>   void panfrost_devfreq_record_busy(struct panfrost_devfreq *pfdevfreq)
>   {
> +	unsigned long irqflags;
> +
> +	if (!pfdevfreq->devfreq)
> +		return;
> +
> +	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
> +
>   	panfrost_devfreq_update_utilization(pfdevfreq);
> -	atomic_inc(&pfdevfreq->busy_count);
> +
> +	pfdevfreq->busy_count++;
> +
> +	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
>   }
>   
>   void panfrost_devfreq_record_idle(struct panfrost_devfreq *pfdevfreq)
>   {
> -	int count;
> +	unsigned long irqflags;
> +
> +	if (!pfdevfreq->devfreq)
> +		return;
> +
> +	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
>   
>   	panfrost_devfreq_update_utilization(pfdevfreq);
> -	count = atomic_dec_if_positive(&pfdevfreq->busy_count);
> -	WARN_ON(count < 0);
> +
> +	WARN_ON(--pfdevfreq->busy_count < 0);
> +
> +	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
>   }
> diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> index 0697f8d5aa34..e6629900a618 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> @@ -4,6 +4,7 @@
>   #ifndef __PANFROST_DEVFREQ_H__
>   #define __PANFROST_DEVFREQ_H__
>   
> +#include <linux/spinlock.h>
>   #include <linux/ktime.h>
>   
>   struct devfreq;
> @@ -14,10 +15,17 @@ struct panfrost_device;
>   struct panfrost_devfreq {
>   	struct devfreq *devfreq;
>   	struct thermal_cooling_device *cooling;
> +
>   	ktime_t busy_time;
>   	ktime_t idle_time;
>   	ktime_t time_last_update;
> -	atomic_t busy_count;
> +	int busy_count;
> +	/*
> +	 * Protect busy_time, idle_time, time_last_update and busy_count
> +	 * because these can be updated concurrently, for example by the GP
> +	 * and PP interrupts.
> +	 */
> +	spinlock_t lock;
>   };
>   
>   int panfrost_devfreq_init(struct panfrost_device *pfdev);
>
Robin Murphy May 29, 2020, 12:20 p.m. UTC | #2
On 2020-05-10 17:55, Clément Péron wrote:
> Convert busy_count to a simple int protected by spinlock.

A little more reasoning might be nice.

> Signed-off-by: Clément Péron <peron.clem@gmail.com>
> ---
[...]
> diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> index 0697f8d5aa34..e6629900a618 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> @@ -4,6 +4,7 @@
>   #ifndef __PANFROST_DEVFREQ_H__
>   #define __PANFROST_DEVFREQ_H__
>   
> +#include <linux/spinlock.h>
>   #include <linux/ktime.h>
>   
>   struct devfreq;
> @@ -14,10 +15,17 @@ struct panfrost_device;
>   struct panfrost_devfreq {
>   	struct devfreq *devfreq;
>   	struct thermal_cooling_device *cooling;
> +
>   	ktime_t busy_time;
>   	ktime_t idle_time;
>   	ktime_t time_last_update;
> -	atomic_t busy_count;
> +	int busy_count;
> +	/*
> +	 * Protect busy_time, idle_time, time_last_update and busy_count
> +	 * because these can be updated concurrently, for example by the GP
> +	 * and PP interrupts.
> +	 */

Nit: this comment is clearly wrong, since we only have Job, GPU and MMU 
interrupts here. I guess if there is a race it would be between 
submission/completion/timeout on different job slots.

Given that, should this actually be considered a fix for 9e62b885f715 
("drm/panfrost: Simplify devfreq utilisation tracking")?

Robin.
Clément Péron May 29, 2020, 12:35 p.m. UTC | #3
Hi Robin,

On Fri, 29 May 2020 at 14:20, Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 2020-05-10 17:55, Clément Péron wrote:
> > Convert busy_count to a simple int protected by spinlock.
>
> A little more reasoning might be nice.

I have follow the modification requested for lima devfreq and clearly
don't have any argument to switch to spinlock.

The Lima Maintainer asked to change witht the following reason :
"Better make this count a normal int which is also protected by the spinlock,
because current implementation can't protect atomic ops for state change
and busy idle check and we are using spinlock already"

>
> > Signed-off-by: Clément Péron <peron.clem@gmail.com>
> > ---
> [...]
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> > index 0697f8d5aa34..e6629900a618 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> > +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
> > @@ -4,6 +4,7 @@
> >   #ifndef __PANFROST_DEVFREQ_H__
> >   #define __PANFROST_DEVFREQ_H__
> >
> > +#include <linux/spinlock.h>
> >   #include <linux/ktime.h>
> >
> >   struct devfreq;
> > @@ -14,10 +15,17 @@ struct panfrost_device;
> >   struct panfrost_devfreq {
> >       struct devfreq *devfreq;
> >       struct thermal_cooling_device *cooling;
> > +
> >       ktime_t busy_time;
> >       ktime_t idle_time;
> >       ktime_t time_last_update;
> > -     atomic_t busy_count;
> > +     int busy_count;
> > +     /*
> > +      * Protect busy_time, idle_time, time_last_update and busy_count
> > +      * because these can be updated concurrently, for example by the GP
> > +      * and PP interrupts.
> > +      */
>
> Nit: this comment is clearly wrong, since we only have Job, GPU and MMU
> interrupts here. I guess if there is a race it would be between
> submission/completion/timeout on different job slots.

It's copy/paste from lima I will update it,

>
> Given that, should this actually be considered a fix for 9e62b885f715
> ("drm/panfrost: Simplify devfreq utilisation tracking")?

I can't say if it can be considered as a fix, I didn't see any
improvement on my board before and after this patch.
I'm still facing some issue and didn't have time to fully investigate it.

Thanks for you review,


>
> Robin.
Steven Price May 29, 2020, 12:47 p.m. UTC | #4
On 29/05/2020 13:35, Clément Péron wrote:
> Hi Robin,
> 
> On Fri, 29 May 2020 at 14:20, Robin Murphy <robin.murphy@arm.com> wrote:
>>
>> On 2020-05-10 17:55, Clément Péron wrote:
>>> Convert busy_count to a simple int protected by spinlock.
>>
>> A little more reasoning might be nice.
> 
> I have follow the modification requested for lima devfreq and clearly
> don't have any argument to switch to spinlock.
> 
> The Lima Maintainer asked to change witht the following reason :
> "Better make this count a normal int which is also protected by the spinlock,
> because current implementation can't protect atomic ops for state change
> and busy idle check and we are using spinlock already"
> 
>>
>>> Signed-off-by: Clément Péron <peron.clem@gmail.com>
>>> ---
>> [...]
>>> diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
>>> index 0697f8d5aa34..e6629900a618 100644
>>> --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h
>>> +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
>>> @@ -4,6 +4,7 @@
>>>    #ifndef __PANFROST_DEVFREQ_H__
>>>    #define __PANFROST_DEVFREQ_H__
>>>
>>> +#include <linux/spinlock.h>
>>>    #include <linux/ktime.h>
>>>
>>>    struct devfreq;
>>> @@ -14,10 +15,17 @@ struct panfrost_device;
>>>    struct panfrost_devfreq {
>>>        struct devfreq *devfreq;
>>>        struct thermal_cooling_device *cooling;
>>> +
>>>        ktime_t busy_time;
>>>        ktime_t idle_time;
>>>        ktime_t time_last_update;
>>> -     atomic_t busy_count;
>>> +     int busy_count;
>>> +     /*
>>> +      * Protect busy_time, idle_time, time_last_update and busy_count
>>> +      * because these can be updated concurrently, for example by the GP
>>> +      * and PP interrupts.
>>> +      */
>>
>> Nit: this comment is clearly wrong, since we only have Job, GPU and MMU
>> interrupts here. I guess if there is a race it would be between
>> submission/completion/timeout on different job slots.
> 
> It's copy/paste from lima I will update it,

Lima ('Utgard') has separate units for geometry and pixel processing 
(GP/PP). For Panfrost ('Midgard'/'Bifrost') we don't have that 
separation, however there are multiple job slots. which are implemented 
as multiple DRM schedulers. So the same fix is appropriate, but clearly 
I missed this comment because it's referring to GP/PP which don't exist 
for Midgard/Bifrost.

>>
>> Given that, should this actually be considered a fix for 9e62b885f715
>> ("drm/panfrost: Simplify devfreq utilisation tracking")?
> 
> I can't say if it can be considered as a fix, I didn't see any
> improvement on my board before and after this patch.
> I'm still facing some issue and didn't have time to fully investigate it.

Technically this is a fix - there's a small race which could cause the 
devfreq information to become corrupted. However it would resolve itself 
on the next devfreq interval when panfrost_devfreq_reset() is called. So 
the impact is very minor (devfreq gets some bogus figures). The 
important variable (busy_count) was already an atomic so won't be affected.

Steve

> Thanks for you review,
> 
> 
>>
>> Robin.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
index 962550363391..78753cfb59fb 100644
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
@@ -12,16 +12,12 @@ 
 
 static void panfrost_devfreq_update_utilization(struct panfrost_devfreq *pfdevfreq)
 {
-	ktime_t now;
-	ktime_t last;
-
-	if (!pfdevfreq->devfreq)
-		return;
+	ktime_t now, last;
 
 	now = ktime_get();
 	last = pfdevfreq->time_last_update;
 
-	if (atomic_read(&pfdevfreq->busy_count) > 0)
+	if (pfdevfreq->busy_count > 0)
 		pfdevfreq->busy_time += ktime_sub(now, last);
 	else
 		pfdevfreq->idle_time += ktime_sub(now, last);
@@ -59,10 +55,14 @@  static int panfrost_devfreq_get_dev_status(struct device *dev,
 {
 	struct panfrost_device *pfdev = dev_get_drvdata(dev);
 	struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
+	unsigned long irqflags;
+
+	status->current_frequency = clk_get_rate(pfdev->clock);
+
+	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
 
 	panfrost_devfreq_update_utilization(pfdevfreq);
 
-	status->current_frequency = clk_get_rate(pfdev->clock);
 	status->total_time = ktime_to_ns(ktime_add(pfdevfreq->busy_time,
 						   pfdevfreq->idle_time));
 
@@ -70,6 +70,8 @@  static int panfrost_devfreq_get_dev_status(struct device *dev,
 
 	panfrost_devfreq_reset(pfdevfreq);
 
+	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
+
 	dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
 		status->busy_time, status->total_time,
 		status->busy_time / (status->total_time / 100),
@@ -100,6 +102,8 @@  int panfrost_devfreq_init(struct panfrost_device *pfdev)
 	else if (ret)
 		return ret;
 
+	spin_lock_init(&pfdevfreq->lock);
+
 	panfrost_devfreq_reset(pfdevfreq);
 
 	cur_freq = clk_get_rate(pfdev->clock);
@@ -162,15 +166,32 @@  void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
 
 void panfrost_devfreq_record_busy(struct panfrost_devfreq *pfdevfreq)
 {
+	unsigned long irqflags;
+
+	if (!pfdevfreq->devfreq)
+		return;
+
+	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
+
 	panfrost_devfreq_update_utilization(pfdevfreq);
-	atomic_inc(&pfdevfreq->busy_count);
+
+	pfdevfreq->busy_count++;
+
+	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
 }
 
 void panfrost_devfreq_record_idle(struct panfrost_devfreq *pfdevfreq)
 {
-	int count;
+	unsigned long irqflags;
+
+	if (!pfdevfreq->devfreq)
+		return;
+
+	spin_lock_irqsave(&pfdevfreq->lock, irqflags);
 
 	panfrost_devfreq_update_utilization(pfdevfreq);
-	count = atomic_dec_if_positive(&pfdevfreq->busy_count);
-	WARN_ON(count < 0);
+
+	WARN_ON(--pfdevfreq->busy_count < 0);
+
+	spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
 }
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
index 0697f8d5aa34..e6629900a618 100644
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h
@@ -4,6 +4,7 @@ 
 #ifndef __PANFROST_DEVFREQ_H__
 #define __PANFROST_DEVFREQ_H__
 
+#include <linux/spinlock.h>
 #include <linux/ktime.h>
 
 struct devfreq;
@@ -14,10 +15,17 @@  struct panfrost_device;
 struct panfrost_devfreq {
 	struct devfreq *devfreq;
 	struct thermal_cooling_device *cooling;
+
 	ktime_t busy_time;
 	ktime_t idle_time;
 	ktime_t time_last_update;
-	atomic_t busy_count;
+	int busy_count;
+	/*
+	 * Protect busy_time, idle_time, time_last_update and busy_count
+	 * because these can be updated concurrently, for example by the GP
+	 * and PP interrupts.
+	 */
+	spinlock_t lock;
 };
 
 int panfrost_devfreq_init(struct panfrost_device *pfdev);