diff mbox

[1/5] drm/i915: Improve PSR activation timing

Message ID 20180213232632.2521-1-rodrigo.vivi@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rodrigo Vivi Feb. 13, 2018, 11:26 p.m. UTC
From: Andy Lutomirski <luto@kernel.org>

The current PSR code has a two call sites that each schedule delayed
work to activate PSR.  As far as I can tell, each call site intends
to keep PSR inactive for the given amount of time and then allow it
to be activated.

The call sites are:

 - intel_psr_enable(), which explicitly states in a comment that
   it's trying to keep PSR off a short time after the dispay is
   initialized as a workaround.

 - intel_psr_flush().  There isn't an explcit explanation, but the
   intent is presumably to keep PSR off until the display has been
   idle for 100ms.

The current code doesn't actually accomplish either of these goals.
Rather than keeping PSR inactive for the given amount of time, it
will schedule PSR for activation after the given time, with the
earliest target time in such a request winning.

In other words, if intel_psr_enable() is immediately followed by
intel_psr_flush(), then PSR will be activated after 100ms even if
intel_psr_enable() wanted a longer delay.  And, if the screen is
being constantly updated so that intel_psr_flush() is called once
per frame at 60Hz, PSR will still be activated once every 100ms.

Rewrite the code so that it does what was intended.  This adds
a new function intel_psr_schedule(), which will enable PSR after
the requested time but no sooner.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Tested-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

---
 drivers/gpu/drm/i915/i915_debugfs.c |  8 +++--
 drivers/gpu/drm/i915/i915_drv.h     |  3 +-
 drivers/gpu/drm/i915/intel_psr.c    | 66 ++++++++++++++++++++++++++++++++-----
 3 files changed, 66 insertions(+), 11 deletions(-)

Comments

Dhinakaran Pandiyan Feb. 14, 2018, 12:18 a.m. UTC | #1
On Tue, 2018-02-13 at 15:26 -0800, Rodrigo Vivi wrote:
> From: Andy Lutomirski <luto@kernel.org>

> 

> The current PSR code has a two call sites that each schedule delayed

> work to activate PSR.  As far as I can tell, each call site intends

> to keep PSR inactive for the given amount of time and then allow it

> to be activated.

> 

> The call sites are:

> 

>  - intel_psr_enable(), which explicitly states in a comment that

>    it's trying to keep PSR off a short time after the dispay is

>    initialized as a workaround.

> 

>  - intel_psr_flush().  There isn't an explcit explanation, but the

>    intent is presumably to keep PSR off until the display has been

>    idle for 100ms.

> 

> The current code doesn't actually accomplish either of these goals.

> Rather than keeping PSR inactive for the given amount of time, it

> will schedule PSR for activation after the given time, with the

> earliest target time in such a request winning.

> 

> In other words, if intel_psr_enable() is immediately followed by

> intel_psr_flush(), then PSR will be activated after 100ms even if

> intel_psr_enable() wanted a longer delay.  And, if the screen is

> being constantly updated so that intel_psr_flush() is called once

> per frame at 60Hz, PSR will still be activated once every 100ms.

> 

> Rewrite the code so that it does what was intended.  This adds

> a new function intel_psr_schedule(), which will enable PSR after

> the requested time but no sooner.

> 

> Signed-off-by: Andy Lutomirski <luto@kernel.org>

> Tested-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

> Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

> Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

> 

> ---

>  drivers/gpu/drm/i915/i915_debugfs.c |  8 +++--

>  drivers/gpu/drm/i915/i915_drv.h     |  3 +-

>  drivers/gpu/drm/i915/intel_psr.c    | 66 ++++++++++++++++++++++++++++++++-----

>  3 files changed, 66 insertions(+), 11 deletions(-)

> 

> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c

> index 960302668649..da80ee16a3cf 100644

> --- a/drivers/gpu/drm/i915/i915_debugfs.c

> +++ b/drivers/gpu/drm/i915/i915_debugfs.c

> @@ -2521,8 +2521,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)

>  	seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active));

>  	seq_printf(m, "Busy frontbuffer bits: 0x%03x\n",

>  		   dev_priv->psr.busy_frontbuffer_bits);

> -	seq_printf(m, "Re-enable work scheduled: %s\n",

> -		   yesno(work_busy(&dev_priv->psr.work.work)));

> +

> +	if (timer_pending(&dev_priv->psr.activate_timer))

> +		seq_printf(m, "Activate scheduled: yes, in %dms\n",

> +			   jiffies_to_msecs(dev_priv->psr.activate_timer.expires - jiffies));

> +	else

> +		seq_printf(m, "Activate scheduled: no\n");

>  

>  	if (HAS_DDI(dev_priv)) {

>  		if (dev_priv->psr.psr2_support)

> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h

> index c06d4126c447..2afa5c05a79b 100644

> --- a/drivers/gpu/drm/i915/i915_drv.h

> +++ b/drivers/gpu/drm/i915/i915_drv.h

> @@ -762,7 +762,8 @@ struct i915_psr {

>  	bool sink_support;

>  	struct intel_dp *enabled;

>  	bool active;

> -	struct delayed_work work;

> +	struct timer_list activate_timer;

> +	struct work_struct activate_work;

>  	unsigned busy_frontbuffer_bits;

>  	bool psr2_support;

>  	bool aux_frame_sync;

> diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c

> index 2ef374f936b9..826b480841ac 100644

> --- a/drivers/gpu/drm/i915/intel_psr.c

> +++ b/drivers/gpu/drm/i915/intel_psr.c

> @@ -450,6 +450,28 @@ static void intel_psr_activate(struct intel_dp *intel_dp)

>  	dev_priv->psr.active = true;

>  }

>  

> +static void intel_psr_schedule(struct drm_i915_private *i915,

> +			       unsigned long min_wait_ms)

> +{

> +	unsigned long next;

> +

> +	lockdep_assert_held(&i915->psr.lock);

> +

> +	/*

> +	 * We update next enable and call mod_timer() because it's

> +	 * possible that intel_psr_wrk() has already been called and is

> +	 * waiting for psr.lock. If that's the case, we don't want it

> +	 * to immediately enable PSR.

> +	 *

> +	 * We also need to make sure that PSR is never activated earlier

> +	 * than requested to avoid breaking intel_psr_enable()'s workaround

> +	 * for pre-gen9 hardware.

> +	 */

> +	next = jiffies + msecs_to_jiffies(min_wait_ms);

> +	if (time_after(next, i915->psr.activate_timer.expires))


.expires is an internal member, does not seem like a good idea to read
it outside of the exported interfaces.


-DK
Rodrigo Vivi Feb. 23, 2018, 11:12 p.m. UTC | #2
"Pandiyan, Dhinakaran" <dhinakaran.pandiyan@intel.com> writes:

> On Tue, 2018-02-13 at 15:26 -0800, Rodrigo Vivi wrote:
>> From: Andy Lutomirski <luto@kernel.org>
>> 
>> The current PSR code has a two call sites that each schedule delayed
>> work to activate PSR.  As far as I can tell, each call site intends
>> to keep PSR inactive for the given amount of time and then allow it
>> to be activated.
>> 
>> The call sites are:
>> 
>>  - intel_psr_enable(), which explicitly states in a comment that
>>    it's trying to keep PSR off a short time after the dispay is
>>    initialized as a workaround.
>> 
>>  - intel_psr_flush().  There isn't an explcit explanation, but the
>>    intent is presumably to keep PSR off until the display has been
>>    idle for 100ms.
>> 
>> The current code doesn't actually accomplish either of these goals.
>> Rather than keeping PSR inactive for the given amount of time, it
>> will schedule PSR for activation after the given time, with the
>> earliest target time in such a request winning.
>> 
>> In other words, if intel_psr_enable() is immediately followed by
>> intel_psr_flush(), then PSR will be activated after 100ms even if
>> intel_psr_enable() wanted a longer delay.  And, if the screen is
>> being constantly updated so that intel_psr_flush() is called once
>> per frame at 60Hz, PSR will still be activated once every 100ms.
>> 
>> Rewrite the code so that it does what was intended.  This adds
>> a new function intel_psr_schedule(), which will enable PSR after
>> the requested time but no sooner.
>> 
>> Signed-off-by: Andy Lutomirski <luto@kernel.org>
>> Tested-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> 
>> ---
>>  drivers/gpu/drm/i915/i915_debugfs.c |  8 +++--
>>  drivers/gpu/drm/i915/i915_drv.h     |  3 +-
>>  drivers/gpu/drm/i915/intel_psr.c    | 66 ++++++++++++++++++++++++++++++++-----
>>  3 files changed, 66 insertions(+), 11 deletions(-)
>> 
>> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
>> index 960302668649..da80ee16a3cf 100644
>> --- a/drivers/gpu/drm/i915/i915_debugfs.c
>> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
>> @@ -2521,8 +2521,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
>>  	seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active));
>>  	seq_printf(m, "Busy frontbuffer bits: 0x%03x\n",
>>  		   dev_priv->psr.busy_frontbuffer_bits);
>> -	seq_printf(m, "Re-enable work scheduled: %s\n",
>> -		   yesno(work_busy(&dev_priv->psr.work.work)));
>> +
>> +	if (timer_pending(&dev_priv->psr.activate_timer))
>> +		seq_printf(m, "Activate scheduled: yes, in %dms\n",
>> +			   jiffies_to_msecs(dev_priv->psr.activate_timer.expires - jiffies));
>> +	else
>> +		seq_printf(m, "Activate scheduled: no\n");
>>  
>>  	if (HAS_DDI(dev_priv)) {
>>  		if (dev_priv->psr.psr2_support)
>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>> index c06d4126c447..2afa5c05a79b 100644
>> --- a/drivers/gpu/drm/i915/i915_drv.h
>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>> @@ -762,7 +762,8 @@ struct i915_psr {
>>  	bool sink_support;
>>  	struct intel_dp *enabled;
>>  	bool active;
>> -	struct delayed_work work;
>> +	struct timer_list activate_timer;
>> +	struct work_struct activate_work;
>>  	unsigned busy_frontbuffer_bits;
>>  	bool psr2_support;
>>  	bool aux_frame_sync;
>> diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
>> index 2ef374f936b9..826b480841ac 100644
>> --- a/drivers/gpu/drm/i915/intel_psr.c
>> +++ b/drivers/gpu/drm/i915/intel_psr.c
>> @@ -450,6 +450,28 @@ static void intel_psr_activate(struct intel_dp *intel_dp)
>>  	dev_priv->psr.active = true;
>>  }
>>  
>> +static void intel_psr_schedule(struct drm_i915_private *i915,
>> +			       unsigned long min_wait_ms)
>> +{
>> +	unsigned long next;
>> +
>> +	lockdep_assert_held(&i915->psr.lock);
>> +
>> +	/*
>> +	 * We update next enable and call mod_timer() because it's
>> +	 * possible that intel_psr_wrk() has already been called and is
>> +	 * waiting for psr.lock. If that's the case, we don't want it
>> +	 * to immediately enable PSR.
>> +	 *
>> +	 * We also need to make sure that PSR is never activated earlier
>> +	 * than requested to avoid breaking intel_psr_enable()'s workaround
>> +	 * for pre-gen9 hardware.
>> +	 */
>> +	next = jiffies + msecs_to_jiffies(min_wait_ms);
>> +	if (time_after(next, i915->psr.activate_timer.expires))
>
> .expires is an internal member, does not seem like a good idea to read
> it outside of the exported interfaces.

Chris I believe this question is for you.

I just ignored for a while because I thought it was for Andy,
but now I saw that you modified the original patch on exactly this point.

Btw I believe this is a modification that should had been clear
highlighted when you sent and with your Signed-off-by.

So, could you please explain and give the sign-off here?
Or should we rebase original Andy's version without this change?

Thanks,
Rodrigo.

>
>
> -DK
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Andy Lutomirski Feb. 24, 2018, 12:07 a.m. UTC | #3
On Tue, Feb 13, 2018 at 11:26 PM, Rodrigo Vivi <rodrigo.vivi@intel.com> wrote:
> From: Andy Lutomirski <luto@kernel.org>
>
> +
> +       dev_priv->psr.activate_timer.expires = jiffies - 1;

That can't possibly be okay.
Chris Wilson Feb. 28, 2018, 12:26 a.m. UTC | #4
Quoting Andy Lutomirski (2018-02-24 00:07:23)
> On Tue, Feb 13, 2018 at 11:26 PM, Rodrigo Vivi <rodrigo.vivi@intel.com> wrote:
> > From: Andy Lutomirski <luto@kernel.org>
> >
> > +
> > +       dev_priv->psr.activate_timer.expires = jiffies - 1;
> 
> That can't possibly be okay.

As an initialisation value, set to the previous jiffie? You can set it
to the current jiffie, but then you have the issue of not noticing the
update to the current jiffie.

So how is this any more incorrect?
-Chris
Andy Lutomirski Feb. 28, 2018, 1:35 a.m. UTC | #5
On Wed, Feb 28, 2018 at 12:26 AM, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> Quoting Andy Lutomirski (2018-02-24 00:07:23)
>> On Tue, Feb 13, 2018 at 11:26 PM, Rodrigo Vivi <rodrigo.vivi@intel.com> wrote:
>> > From: Andy Lutomirski <luto@kernel.org>
>> >
>> > +
>> > +       dev_priv->psr.activate_timer.expires = jiffies - 1;
>>
>> That can't possibly be okay.
>
> As an initialisation value, set to the previous jiffie? You can set it
> to the current jiffie, but then you have the issue of not noticing the
> update to the current jiffie.
>
> So how is this any more incorrect?

I don't think you can just write to fields in struct timer_list like that.
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 960302668649..da80ee16a3cf 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -2521,8 +2521,12 @@  static int i915_edp_psr_status(struct seq_file *m, void *data)
 	seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active));
 	seq_printf(m, "Busy frontbuffer bits: 0x%03x\n",
 		   dev_priv->psr.busy_frontbuffer_bits);
-	seq_printf(m, "Re-enable work scheduled: %s\n",
-		   yesno(work_busy(&dev_priv->psr.work.work)));
+
+	if (timer_pending(&dev_priv->psr.activate_timer))
+		seq_printf(m, "Activate scheduled: yes, in %dms\n",
+			   jiffies_to_msecs(dev_priv->psr.activate_timer.expires - jiffies));
+	else
+		seq_printf(m, "Activate scheduled: no\n");
 
 	if (HAS_DDI(dev_priv)) {
 		if (dev_priv->psr.psr2_support)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c06d4126c447..2afa5c05a79b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -762,7 +762,8 @@  struct i915_psr {
 	bool sink_support;
 	struct intel_dp *enabled;
 	bool active;
-	struct delayed_work work;
+	struct timer_list activate_timer;
+	struct work_struct activate_work;
 	unsigned busy_frontbuffer_bits;
 	bool psr2_support;
 	bool aux_frame_sync;
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
index 2ef374f936b9..826b480841ac 100644
--- a/drivers/gpu/drm/i915/intel_psr.c
+++ b/drivers/gpu/drm/i915/intel_psr.c
@@ -450,6 +450,28 @@  static void intel_psr_activate(struct intel_dp *intel_dp)
 	dev_priv->psr.active = true;
 }
 
+static void intel_psr_schedule(struct drm_i915_private *i915,
+			       unsigned long min_wait_ms)
+{
+	unsigned long next;
+
+	lockdep_assert_held(&i915->psr.lock);
+
+	/*
+	 * We update next enable and call mod_timer() because it's
+	 * possible that intel_psr_wrk() has already been called and is
+	 * waiting for psr.lock. If that's the case, we don't want it
+	 * to immediately enable PSR.
+	 *
+	 * We also need to make sure that PSR is never activated earlier
+	 * than requested to avoid breaking intel_psr_enable()'s workaround
+	 * for pre-gen9 hardware.
+	 */
+	next = jiffies + msecs_to_jiffies(min_wait_ms);
+	if (time_after(next, i915->psr.activate_timer.expires))
+		mod_timer(&i915->psr.activate_timer, next);
+}
+
 static void hsw_psr_enable_source(struct intel_dp *intel_dp,
 				  const struct intel_crtc_state *crtc_state)
 {
@@ -534,8 +556,8 @@  void intel_psr_enable(struct intel_dp *intel_dp,
 		 *     - On HSW/BDW we get a recoverable frozen screen until
 		 *       next exit-activate sequence.
 		 */
-		schedule_delayed_work(&dev_priv->psr.work,
-				      msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5));
+		intel_psr_schedule(dev_priv,
+				   intel_dp->panel_power_cycle_delay * 5);
 	}
 
 unlock:
@@ -653,13 +675,14 @@  void intel_psr_disable(struct intel_dp *intel_dp,
 	dev_priv->psr.enabled = NULL;
 	mutex_unlock(&dev_priv->psr.lock);
 
-	cancel_delayed_work_sync(&dev_priv->psr.work);
+	del_timer_sync(&dev_priv->psr.activate_timer);
+	cancel_work_sync(&dev_priv->psr.activate_work);
 }
 
 static void intel_psr_work(struct work_struct *work)
 {
 	struct drm_i915_private *dev_priv =
-		container_of(work, typeof(*dev_priv), psr.work.work);
+		container_of(work, typeof(*dev_priv), psr.activate_work);
 	struct intel_dp *intel_dp = dev_priv->psr.enabled;
 	struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc;
 	enum pipe pipe = to_intel_crtc(crtc)->pipe;
@@ -705,6 +728,17 @@  static void intel_psr_work(struct work_struct *work)
 	if (!intel_dp)
 		goto unlock;
 
+	if (time_before(jiffies, dev_priv->psr.activate_timer.expires)) {
+		/*
+		 * We raced: intel_psr_schedule() tried to delay us, but
+		 * we were already in intel_psr_timer_fn() or already in
+		 * the workqueue. We can safely return -- the
+		 * intel_psr_schedule() call that put the activate_timer
+		 * into the future will also have called mod_timer().
+		 */
+		goto unlock;
+	}
+
 	/*
 	 * The delayed work can race with an invalidate hence we need to
 	 * recheck. Since psr_flush first clears this and then reschedules we
@@ -718,6 +752,20 @@  static void intel_psr_work(struct work_struct *work)
 	mutex_unlock(&dev_priv->psr.lock);
 }
 
+static void intel_psr_timer_fn(struct timer_list *timer)
+{
+	struct drm_i915_private *i915 =
+		from_timer(i915, timer, psr.activate_timer);
+
+	/*
+	 * We need a non-atomic context to activate PSR.  Using
+	 * delayed_work wouldn't be an improvement -- delayed_work is
+	 * just the same timer that schedules work when it fires, but
+	 * there's no equivalent of mod_timer() for delayed_work.
+	 */
+	schedule_work(&i915->psr.activate_work);
+}
+
 static void intel_psr_exit(struct drm_i915_private *dev_priv)
 {
 	struct intel_dp *intel_dp = dev_priv->psr.enabled;
@@ -898,9 +946,8 @@  void intel_psr_flush(struct drm_i915_private *dev_priv,
 		intel_psr_exit(dev_priv);
 
 	if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
-		if (!work_busy(&dev_priv->psr.work.work))
-			schedule_delayed_work(&dev_priv->psr.work,
-					      msecs_to_jiffies(100));
+		intel_psr_schedule(dev_priv, 100);
+
 	mutex_unlock(&dev_priv->psr.lock);
 }
 
@@ -947,7 +994,8 @@  void intel_psr_init(struct drm_i915_private *dev_priv)
 		dev_priv->psr.link_standby = false;
 	}
 
-	INIT_DELAYED_WORK(&dev_priv->psr.work, intel_psr_work);
+	timer_setup(&dev_priv->psr.activate_timer, intel_psr_timer_fn, 0);
+	INIT_WORK(&dev_priv->psr.activate_work, intel_psr_work);
 	mutex_init(&dev_priv->psr.lock);
 
 	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
@@ -963,4 +1011,6 @@  void intel_psr_init(struct drm_i915_private *dev_priv)
 		dev_priv->psr.activate = hsw_psr_activate;
 		dev_priv->psr.setup_vsc = hsw_psr_setup_vsc;
 	}
+
+	dev_priv->psr.activate_timer.expires = jiffies - 1;
 }