All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] drm: Aggressively disable vblanks
@ 2010-12-20 19:00 Andy Lutomirski
  2010-12-21  3:23 ` Keith Packard
  0 siblings, 1 reply; 7+ messages in thread
From: Andy Lutomirski @ 2010-12-20 19:00 UTC (permalink / raw)
  To: Jesse Barnes, Chris Wilson, David Airlie
  Cc: intel-gfx, Andy Lutomirski, dri-devel

Enabling and disabling the vblank interrupt (on devices that
support it) is cheap.  So disable it quickly after each
interrupt.

To avoid constantly enabling and disabling vblank when
animations are running, after a predefined number (3) of
consecutive enabled vblanks that someone cared about, just
leave the interrupt on until an interrupt that no one cares
about.

The full heuristic is:

There's a per-CRTC counter called vblank_consecutive.  It
starts at zero and counts consecutive useful vblanks.  A
vblank is useful if the refcount is nonzero when the interrupt
comes in.

Whenever drm_vblank_get enables the interrupt, it stays on at
least until the next vblank (*).  If drm_vblank_put is called
and vblank_consecutive is less than a threshold (currently 3),
then the interrupt is disabled.  If a vblank interrupt happens
with refcount == 0, then the interrupt is disabled and
vblank_consecutive is reset to 0.  If vblank_get is called
with the interrupt disabled and no interrupts were missed,
then vblank_consecutive is incremented.

(*) I tried letting it turn off before the next interrupt, but
compiz on my laptop seems to call drm_wait_vblank twice with
relative waits of 0 (!) before actually waiting.

Signed-off-by: Andy Lutomirski <luto@mit.edu>
---

Jesse, you asked for the deletion of the timer to be separate
from reducing the timeout, but that seemed silly because I'm ripping
out the entire old mechanism.  If you're worried about the added time
spent in the interrupt handler, I could move it to a tasklet.  That
being said, disable_vblank should be very fast (it's at most a couple
of register accesses in all in-tree drivers).

I've tested this on i915, where it works nicely and reduces my wakeups
with a second hand showing on the clock but an otherwise idle system.

This changes the requirements on enable_vblank, disable_vblank and
get_vblank_counter: they can now be called from an IRQ.  They already
had to work with IRQs off and a spinlock held, but now a driver has to
watch out for recursive calls from drm_handle_vblank.  The vbl_lock is
still held.

I've audited the in-tree drivers:

mga, r128: get_vblank_counter just reads an atomic_t.  enable_vblank
just pokes registers without a lock.  disable_vblank does nothing, so
turning off vblanks is pointless.

via: get_vblank_counter just returns a counter.  enable_vblank and
disable_vblank just poke registers without locks.  (This looks wrong:
get_vblank_count does the wrong thing and will confuse my heuristic,
but it should be any worse than it already is.  I can comment out
enable_vblank if anyone prefers that approach.)

vmwgfx: get_vblank_counter does nothing and the other hooks aren't
implemented.

radeon: Everything looks safe.

i915: Looks good and tested!

nouveau: Not implemented at all.  I'm not sure why either the old code
or my code doesn't try to call a null pointer, but it doesn't.  That
being said, sync-to-vblank doesn't work on nouveau for me (glxgears
gets over 600fps while claiming to be synced to vblank).

As a future improvement, all of the vblank-disabling code could be
skipped if there is no disable_vblank callback.

 drivers/gpu/drm/drm_irq.c |  103 +++++++++++++++++++++++++++++++++-----------
 include/drm/drmP.h        |    5 ++-
 2 files changed, 81 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 9d3a503..2f107c5 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -77,45 +77,59 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 	return 0;
 }
 
-static void vblank_disable_fn(unsigned long arg)
+/* After VBLANK_CONSEC_THRESHOLD consecutive non-ignored vblank interrupts,
+ * vblanks will be left on. */
+#define VBLANK_CONSEC_THRESHOLD 3
+
+static void __vblank_disable_now(struct drm_device *dev, int crtc, int force)
+{
+	if (!dev->vblank_disable_allowed)
+		return;
+
+	if (atomic_read(&dev->vblank_refcount[crtc]) == 0 && dev->vblank_enabled[crtc] &&
+		(dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD || force))
+	{
+		DRM_DEBUG("disabling vblank on crtc %d\n", crtc);
+		dev->last_vblank[crtc] =
+			dev->driver->get_vblank_counter(dev, crtc);
+		dev->driver->disable_vblank(dev, crtc);
+		dev->vblank_enabled[crtc] = 0;
+		if (force)
+			dev->vblank_consecutive[crtc] = 0;
+	}
+}
+
+static void vblank_disable_now(struct drm_device *dev, int crtc, int force)
 {
-	struct drm_device *dev = (struct drm_device *)arg;
 	unsigned long irqflags;
-	int i;
 
 	if (!dev->vblank_disable_allowed)
 		return;
 
-	for (i = 0; i < dev->num_crtcs; i++) {
-		spin_lock_irqsave(&dev->vbl_lock, irqflags);
-		if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
-		    dev->vblank_enabled[i]) {
-			DRM_DEBUG("disabling vblank on crtc %d\n", i);
-			dev->last_vblank[i] =
-				dev->driver->get_vblank_counter(dev, i);
-			dev->driver->disable_vblank(dev, i);
-			dev->vblank_enabled[i] = 0;
-		}
-		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-	}
+	spin_lock_irqsave(&dev->vbl_lock, irqflags);
+	__vblank_disable_now(dev, crtc, force);
+	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 }
 
 void drm_vblank_cleanup(struct drm_device *dev)
 {
+	int i;
+
 	/* Bail if the driver didn't call drm_vblank_init() */
 	if (dev->num_crtcs == 0)
 		return;
 
-	del_timer(&dev->vblank_disable_timer);
-
-	vblank_disable_fn((unsigned long)dev);
+	for (i = 0; i < dev->num_crtcs; i++)
+		vblank_disable_now(dev, i, 1);
 
 	kfree(dev->vbl_queue);
 	kfree(dev->_vblank_count);
 	kfree(dev->vblank_refcount);
 	kfree(dev->vblank_enabled);
 	kfree(dev->last_vblank);
+	kfree(dev->last_vblank_enable);
 	kfree(dev->last_vblank_wait);
+	kfree(dev->vblank_consecutive);
 	kfree(dev->vblank_inmodeset);
 
 	dev->num_crtcs = 0;
@@ -126,8 +140,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 {
 	int i, ret = -ENOMEM;
 
-	setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
-		    (unsigned long)dev);
 	spin_lock_init(&dev->vbl_lock);
 	dev->num_crtcs = num_crtcs;
 
@@ -153,10 +165,18 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 	if (!dev->last_vblank)
 		goto err;
 
+	dev->last_vblank_enable = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL);
+	if (!dev->last_vblank)
+		goto err;
+
 	dev->last_vblank_wait = kcalloc(num_crtcs, sizeof(u32), GFP_KERNEL);
 	if (!dev->last_vblank_wait)
 		goto err;
 
+	dev->vblank_consecutive = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL);
+	if (!dev->vblank_consecutive)
+		goto err;
+
 	dev->vblank_inmodeset = kcalloc(num_crtcs, sizeof(int), GFP_KERNEL);
 	if (!dev->vblank_inmodeset)
 		goto err;
@@ -387,10 +407,12 @@ EXPORT_SYMBOL(drm_vblank_count);
  * Only necessary when going from off->on, to account for frames we
  * didn't get an interrupt for.
  *
+ * Returns the number of vblanks missed.
+ *
  * Note: caller must hold dev->vbl_lock since this reads & writes
  * device vblank fields.
  */
-static void drm_update_vblank_count(struct drm_device *dev, int crtc)
+static u32 drm_update_vblank_count(struct drm_device *dev, int crtc)
 {
 	u32 cur_vblank, diff;
 
@@ -414,6 +436,17 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
 		  crtc, diff);
 
 	atomic_add(diff, &dev->_vblank_count[crtc]);
+	return diff;
+}
+
+static void vblank_enabled_consecutively(struct drm_device *dev, int crtc)
+{
+	if (dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD) {
+		dev->vblank_consecutive[crtc]++;
+		if (dev->vblank_consecutive[crtc] == VBLANK_CONSEC_THRESHOLD)
+			DRM_DEBUG("consecutive vblank usage on crtc %d\n", crtc);
+	}
+
 }
 
 /**
@@ -433,7 +466,6 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 	int ret = 0;
 
 	spin_lock_irqsave(&dev->vbl_lock, irqflags);
-	/* Going from 0->1 means we have to enable interrupts again */
 	if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) {
 		if (!dev->vblank_enabled[crtc]) {
 			ret = dev->driver->enable_vblank(dev, crtc);
@@ -442,7 +474,12 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 				atomic_dec(&dev->vblank_refcount[crtc]);
 			else {
 				dev->vblank_enabled[crtc] = 1;
-				drm_update_vblank_count(dev, crtc);
+				if (drm_update_vblank_count(dev, crtc) == 0)
+					vblank_enabled_consecutively(dev, crtc);
+				else
+					dev->vblank_consecutive[crtc] = 0;
+				dev->last_vblank_enable[crtc] =
+					drm_vblank_count(dev, crtc);
 			}
 		}
 	} else {
@@ -467,11 +504,18 @@ EXPORT_SYMBOL(drm_vblank_get);
  */
 void drm_vblank_put(struct drm_device *dev, int crtc)
 {
+	unsigned long irqflags;
+
 	BUG_ON (atomic_read (&dev->vblank_refcount[crtc]) == 0);
 
-	/* Last user schedules interrupt disable */
-	if (atomic_dec_and_test(&dev->vblank_refcount[crtc]))
-		mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ);
+	if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) {
+		spin_lock_irqsave(&dev->vbl_lock, irqflags);
+		if (atomic_read(&dev->_vblank_count[crtc]) !=
+			dev->last_vblank_enable[crtc]
+		    && dev->vblank_consecutive[crtc] < VBLANK_CONSEC_THRESHOLD)
+			__vblank_disable_now(dev, crtc, 0);
+		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+	}
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
@@ -780,11 +824,18 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc)
  */
 void drm_handle_vblank(struct drm_device *dev, int crtc)
 {
+	int vblank_refcount;
 	if (!dev->num_crtcs)
 		return;
 
+	vblank_refcount = atomic_read(&dev->vblank_refcount[crtc]);
 	atomic_inc(&dev->_vblank_count[crtc]);
 	DRM_WAKEUP(&dev->vbl_queue[crtc]);
 	drm_handle_vblank_events(dev, crtc);
+
+	if (vblank_refcount == 0) {
+		/* This interrupt was unnecessary. */
+		vblank_disable_now(dev, crtc, 1);
+	}
 }
 EXPORT_SYMBOL(drm_handle_vblank);
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 4c9461a..c15918b 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -997,11 +997,14 @@ struct drm_device {
 	atomic_t *vblank_refcount;      /* number of users of vblank interruptsper crtc */
 	u32 *last_vblank;               /* protected by dev->vbl_lock, used */
 					/* for wraparound handling */
+	u32 *last_vblank_enable;        /* protected by dev->vbl_lock, used */
+					/* for early disable path */
 	int *vblank_enabled;            /* so we don't call enable more than
 					   once per disable */
 	int *vblank_inmodeset;          /* Display driver is setting mode */
 	u32 *last_vblank_wait;		/* Last vblank seqno waited per CRTC */
-	struct timer_list vblank_disable_timer;
+	int *vblank_consecutive;        /* protected by dev->vbl_lock, counts
+					   consecutive interesting vblanks */
 
 	u32 max_vblank_count;           /**< size of vblank counter register */
 
-- 
1.7.3.3

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] drm: Aggressively disable vblanks
  2010-12-20 19:00 [PATCH] drm: Aggressively disable vblanks Andy Lutomirski
@ 2010-12-21  3:23 ` Keith Packard
  2010-12-21  3:34   ` [Intel-gfx] " Andrew Lutomirski
  0 siblings, 1 reply; 7+ messages in thread
From: Keith Packard @ 2010-12-21  3:23 UTC (permalink / raw)
  To: Andy Lutomirski, Jesse Barnes, Chris Wilson, David Airlie
  Cc: intel-gfx, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 626 bytes --]

On Mon, 20 Dec 2010 14:00:54 -0500, Andy Lutomirski <luto@MIT.EDU> wrote:

> Enabling and disabling the vblank interrupt (on devices that
> support it) is cheap.  So disable it quickly after each
> interrupt.

So, the concern (and reason for the original design) was that you might
lose count of vblank interrupts as vblanks are counted differently while
off than while on. If vblank interrupts get enabled near the interrupt
time, is it possible that you'll end up off by one somehow?

Leaving them enabled for 'a while' was supposed to reduce the impact of
this potential error.

-- 
keith.packard@intel.com

[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Intel-gfx] [PATCH] drm: Aggressively disable vblanks
  2010-12-21  3:23 ` Keith Packard
@ 2010-12-21  3:34   ` Andrew Lutomirski
  2010-12-21  3:47     ` Keith Packard
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Lutomirski @ 2010-12-21  3:34 UTC (permalink / raw)
  To: Keith Packard; +Cc: intel-gfx, dri-devel

On Mon, Dec 20, 2010 at 10:23 PM, Keith Packard <keithp@keithp.com> wrote:
> On Mon, 20 Dec 2010 14:00:54 -0500, Andy Lutomirski <luto@MIT.EDU> wrote:
>
>> Enabling and disabling the vblank interrupt (on devices that
>> support it) is cheap.  So disable it quickly after each
>> interrupt.
>
> So, the concern (and reason for the original design) was that you might
> lose count of vblank interrupts as vblanks are counted differently while
> off than while on. If vblank interrupts get enabled near the interrupt
> time, is it possible that you'll end up off by one somehow?

So the race is:

1. Vblank happens.
2. vblank_get runs, reads hardware counter, and enables the interrupt
(and maybe not quite in that order)
3. Interrupt fires and increments the counter.  Now the counter is one too high.

What if we read the hardware counter from the IRQ the first time that
it fires after being enabled?  That way, if the hardware and software
counters match (mod 2^23 or whatever the magic number is), we don't
increment the counter.

>
> Leaving them enabled for 'a while' was supposed to reduce the impact of
> this potential error.
>

Fair enough,

But five seconds is a *long* time, and anything short enough that the
interrupt actually gets turned off in normal use risks the same race.

--Andy

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Intel-gfx] [PATCH] drm: Aggressively disable vblanks
  2010-12-21  3:34   ` [Intel-gfx] " Andrew Lutomirski
@ 2010-12-21  3:47     ` Keith Packard
  2010-12-21  3:55       ` Andrew Lutomirski
  0 siblings, 1 reply; 7+ messages in thread
From: Keith Packard @ 2010-12-21  3:47 UTC (permalink / raw)
  To: Andrew Lutomirski; +Cc: intel-gfx, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 344 bytes --]

On Mon, 20 Dec 2010 22:34:12 -0500, Andrew Lutomirski <luto@mit.edu> wrote:

> But five seconds is a *long* time, and anything short enough that the
> interrupt actually gets turned off in normal use risks the same race.

Right, so eliminating any race seems like the basic requirement. Can
that be done?

-- 
keith.packard@intel.com

[-- Attachment #1.2: Type: application/pgp-signature, Size: 189 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Intel-gfx] [PATCH] drm: Aggressively disable vblanks
  2010-12-21  3:47     ` Keith Packard
@ 2010-12-21  3:55       ` Andrew Lutomirski
  2010-12-21  4:03         ` Jesse Barnes
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Lutomirski @ 2010-12-21  3:55 UTC (permalink / raw)
  To: Keith Packard; +Cc: intel-gfx, dri-devel

On Mon, Dec 20, 2010 at 10:47 PM, Keith Packard <keithp@keithp.com> wrote:
> On Mon, 20 Dec 2010 22:34:12 -0500, Andrew Lutomirski <luto@mit.edu> wrote:
>
>> But five seconds is a *long* time, and anything short enough that the
>> interrupt actually gets turned off in normal use risks the same race.
>
> Right, so eliminating any race seems like the basic requirement. Can
> that be done?

I'll give it a shot.

Do you know if there's an existing tool to call drm_wait_vblank from
userspace for testing?  I know approximately nothing about libdrm or
any userspace graphics stuff whatsoever.

--Andy

>
> --
> keith.packard@intel.com
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drm: Aggressively disable vblanks
  2010-12-21  3:55       ` Andrew Lutomirski
@ 2010-12-21  4:03         ` Jesse Barnes
  0 siblings, 0 replies; 7+ messages in thread
From: Jesse Barnes @ 2010-12-21  4:03 UTC (permalink / raw)
  To: Andrew Lutomirski; +Cc: David Airlie, intel-gfx, dri-devel

On Mon, 20 Dec 2010 22:55:46 -0500
Andrew Lutomirski <luto@mit.edu> wrote:

> On Mon, Dec 20, 2010 at 10:47 PM, Keith Packard <keithp@keithp.com> wrote:
> > On Mon, 20 Dec 2010 22:34:12 -0500, Andrew Lutomirski <luto@mit.edu> wrote:
> >
> >> But five seconds is a *long* time, and anything short enough that the
> >> interrupt actually gets turned off in normal use risks the same race.
> >
> > Right, so eliminating any race seems like the basic requirement. Can
> > that be done?
> 
> I'll give it a shot.
> 
> Do you know if there's an existing tool to call drm_wait_vblank from
> userspace for testing?  I know approximately nothing about libdrm or
> any userspace graphics stuff whatsoever.

Yeah, libdrm has a test program called vbltest; it should do what you
need.

-- 
Jesse Barnes, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drm: Aggressively disable vblanks
  2010-12-26 14:53       ` Andrew Lutomirski
@ 2010-12-27 10:52         ` Michel Dänzer
  0 siblings, 0 replies; 7+ messages in thread
From: Michel Dänzer @ 2010-12-27 10:52 UTC (permalink / raw)
  To: Andrew Lutomirski; +Cc: dri-devel, Mario Kleiner

On Son, 2010-12-26 at 09:53 -0500, Andrew Lutomirski wrote: 
> On Wed, Dec 22, 2010 at 4:06 PM, Mario Kleiner
> <mario.kleiner@tuebingen.mpg.de> wrote:
> >
> > There's a new drm module parameter for selecting the timeout: echo 50 >
> > /sys/module/drm/parameters/vblankoffdelay
> > would set the timeout to 50 msecs. A setting of zero will disable the timer,
> > so vblank irq's would stay on all the time.
> >
> > The default setting is still 5000 msecs as before, but reducing this to 100
> > msecs wouldn't be a real problem imho. At least i didn't observe any
> > miscounting during extensive testing with 100 msecs.
> >
> > The patches in drm-next fix a couple of races that i observed on intel and
> > radeon during testing and a few that i didn't see but that i could imagine
> > happening. It tries to make sure that the saved final count at vblank irq
> > disable of the software vblank_count and the gpu counter are consistent - no
> > off by one errors. They also try to detect and filter out spurious vblank
> > interrupts at vblank enable time, e.g., on the radeon.
> >
> > There's still one possible race in the disable path which i will try to fix:
> > We don't know when exactly the hardware counter increments wrt. the
> > processing of the vblank interrupt - it could increment a few
> > (dozen/hundred) microseconds before or after the irq handler runs, so if you
> > happen to query the hardware counter while the gpu is inside the vblank you
> > can't be sure if you picked up the old count or the new count for that
> > vblank.
> 
> That's disgusting.  Does this affect many GPUs?  (I can't imagine why
> any sensible implementation wouldn't guarantee that the counter
> increments just before the IRQ.)

Actually, while we want the interrupt to trigger at the beginning of
vblank (so we get a chance to do work within the vblank period), at
least on Radeons, the hardware frame counter increments at the beginning
of scanout, i.e. at the end of vblank.

At some point we tried to compensate for that by adding 1 to the
hardware frame counter while inside vblank, but IIRC that wasn't 100%
reliable either but would sometimes result in the counter being
considered too high by 1. So instead we've tried to keep the
infrastructure robust against the interrupt and hardware frame counter
increment not necessarily occurring at the same time.

(Even if both events always occurred at the same time on the GPU, we
might not be able to take advantage of that due to interrupt latencies)


-- 
Earthling Michel Dänzer           |                http://www.vmware.com
Libre software enthusiast         |          Debian, X and DRI developer
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-12-27 10:52 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-20 19:00 [PATCH] drm: Aggressively disable vblanks Andy Lutomirski
2010-12-21  3:23 ` Keith Packard
2010-12-21  3:34   ` [Intel-gfx] " Andrew Lutomirski
2010-12-21  3:47     ` Keith Packard
2010-12-21  3:55       ` Andrew Lutomirski
2010-12-21  4:03         ` Jesse Barnes
     [not found] <mailman.28.1292917668.19577.dri-devel@lists.freedesktop.org>
2010-12-22  4:36 ` [Intel-gfx] " Mario Kleiner
2010-12-22 17:23   ` Jesse Barnes
2010-12-22 21:06     ` Mario Kleiner
2010-12-26 14:53       ` Andrew Lutomirski
2010-12-27 10:52         ` Michel Dänzer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.