diff options
author | Owain Ainsworth <oga@cvs.openbsd.org> | 2008-11-06 14:08:04 +0000 |
---|---|---|
committer | Owain Ainsworth <oga@cvs.openbsd.org> | 2008-11-06 14:08:04 +0000 |
commit | fa394b24c2e8f15ecab7e5c278cb1dfa0169dc47 (patch) | |
tree | 6c9e64716a17c166f1bba273085e5404a7a20e3a /sys | |
parent | 039d94bf4e7571a2a04f51b3370fd5cc6f011d51 (diff) |
The i915 vblank swap ioctl is fundamentally racy.
using it allowed rendering to continue while waiting for a vblank swap,
and often this lead to flickering and rendering a new scene before the
swap. this broke a lot of things.
With the removal of this swap, userland falls back to the old way of
waiting for the vblank then doing the swap itself, this is smooth
enough.
I decided independantly to kill this, but the intel guys recently
concurred. Comment change comes from Eric Anholt at intel.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/drm/i915_dma.c | 2 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915_drv.h | 11 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915_irq.c | 329 |
3 files changed, 15 insertions, 327 deletions
diff --git a/sys/dev/pci/drm/i915_dma.c b/sys/dev/pci/drm/i915_dma.c index 532b95d4067..4abc916e120 100644 --- a/sys/dev/pci/drm/i915_dma.c +++ b/sys/dev/pci/drm/i915_dma.c @@ -823,7 +823,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) return ret; } - mtx_init(&dev_priv->swaps_lock, IPL_NONE); mtx_init(&dev_priv->user_irq_lock, IPL_BIO); return ret; @@ -838,7 +837,6 @@ int i915_driver_unload(struct drm_device *dev) if (dev_priv->mmio_map) drm_rmmap(dev, dev_priv->mmio_map); - DRM_SPINUNINIT(&dev_priv->swaps_lock); DRM_SPINUNINIT(&dev_priv->user_irq_lock); drm_free(dev->dev_private, sizeof(drm_i915_private_t), DRM_MEM_DRIVER); diff --git a/sys/dev/pci/drm/i915_drv.h b/sys/dev/pci/drm/i915_drv.h index 275a1e42e4e..d2078a21642 100644 --- a/sys/dev/pci/drm/i915_drv.h +++ b/sys/dev/pci/drm/i915_drv.h @@ -76,13 +76,6 @@ struct mem_block { struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */ }; -typedef struct _drm_i915_vbl_swap { - struct list_head head; - drm_drawable_t drw_id; - unsigned int pipe; - unsigned int sequence; -} drm_i915_vbl_swap_t; - typedef struct drm_i915_private { drm_local_map_t *sarea; drm_local_map_t *mmio_map; @@ -117,10 +110,6 @@ typedef struct drm_i915_private { struct mem_block *agp_heap; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; - DRM_SPINTYPE swaps_lock; - drm_i915_vbl_swap_t vbl_swaps; - unsigned int swaps_pending; - /* Register state */ u8 saveLBB; u32 saveDSPACNTR; diff --git a/sys/dev/pci/drm/i915_irq.c b/sys/dev/pci/drm/i915_irq.c index e14b70470fd..b0d268960ff 100644 --- a/sys/dev/pci/drm/i915_irq.c +++ b/sys/dev/pci/drm/i915_irq.c @@ -119,206 +119,6 @@ i915_pipe_enabled(struct drm_device *dev, int pipe) return 0; } -/** - * Emit blits for scheduled buffer swaps. - * - * This function will be called with the HW lock held. - */ -static void i915_vblank_tasklet(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct list_head *list, *tmp, hits, *hit; - int nhits, nrects, slice[2], upper[2], lower[2], i; - unsigned counter[2]; - struct drm_drawable_info *drw; - drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; - u32 cpp = dev_priv->cpp; - u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | - XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB) - : XY_SRC_COPY_BLT_CMD; - u32 src_pitch = sarea_priv->pitch * cpp; - u32 dst_pitch = sarea_priv->pitch * cpp; - u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); - RING_LOCALS; - - DRM_LOCK(); - - if (IS_I965G(dev) && sarea_priv->front_tiled) { - cmd |= XY_SRC_COPY_BLT_DST_TILED; - dst_pitch >>= 2; - } - if (IS_I965G(dev) && sarea_priv->back_tiled) { - cmd |= XY_SRC_COPY_BLT_SRC_TILED; - src_pitch >>= 2; - } - - counter[0] = drm_vblank_count(dev, i915_get_plane(dev, 0)); - counter[1] = drm_vblank_count(dev, i915_get_plane(dev, 1)); - - DRM_DEBUG("\n"); - - INIT_LIST_HEAD(&hits); - - nhits = nrects = 0; - - mtx_enter(&dev_priv->swaps_lock); - - /* Find buffer swaps scheduled for this vertical blank */ - list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { - drm_i915_vbl_swap_t *vbl_swap = - list_entry(list, drm_i915_vbl_swap_t, head); - int pipe = vbl_swap->pipe; - - if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) - continue; - - list_del(list); - dev_priv->swaps_pending--; - drm_vblank_put(dev, pipe); - - mtx_leave(&dev_priv->swaps_lock); - mtx_enter(&dev->drw_lock); - - drw = drm_get_drawable_info(dev, vbl_swap->drw_id); - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_cmp = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_drawable_info *drw_cmp = - drm_get_drawable_info(dev, swap_cmp->drw_id); - - /* - * Make sure both drawables are still around and have - * cliprects before we poke around in them - */ - if (drw_cmp && drw_cmp->num_rects > 0 && - drw != NULL && drw->num_rects > 0 && - drw_cmp->rects[0].y1 > drw->rects[0].y1) { - list_add_tail(list, hit); - break; - } - } - - mtx_leave(&dev->drw_lock); - - /* List of hits was empty, or we reached the end of it */ - if (hit == &hits) - list_add_tail(list, hits.prev); - - nhits++; - - mtx_enter(&dev_priv->swaps_lock); - } - - mtx_leave(&dev_priv->swaps_lock); - - if (nhits == 0) { - DRM_UNLOCK(); - return; - } - - i915_kernel_lost_context(dev); - - if (IS_I965G(dev)) { - BEGIN_LP_RING(4); - - OUT_RING(GFX_OP_DRAWRECT_INFO_I965); - OUT_RING(0); - OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16)); - OUT_RING(0); - ADVANCE_LP_RING(); - } else { - BEGIN_LP_RING(6); - - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(0); - OUT_RING(0); - OUT_RING(sarea_priv->width | sarea_priv->height << 16); - OUT_RING(sarea_priv->width | sarea_priv->height << 16); - OUT_RING(0); - - ADVANCE_LP_RING(); - } - - sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; - - upper[0] = upper[1] = 0; - slice[0] = max(sarea_priv->planeA_h / nhits, 1); - slice[1] = max(sarea_priv->planeB_h / nhits, 1); - lower[0] = sarea_priv->planeA_y + slice[0]; - lower[1] = sarea_priv->planeB_y + slice[0]; - - mtx_enter(&dev->drw_lock); - - /* Emit blits for buffer swaps, partitioning both outputs into as many - * slices as there are buffer swaps scheduled in order to avoid tearing - * (based on the assumption that a single buffer swap would always - * complete before scanout starts). - */ - for (i = 0; i++ < nhits; - upper[0] = lower[0], lower[0] += slice[0], - upper[1] = lower[1], lower[1] += slice[1]) { - if (i == nhits) - lower[0] = lower[1] = sarea_priv->height; - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_clip_rect *rect; - int num_rects, pipe; - unsigned short top, bottom; - - drw = drm_get_drawable_info(dev, swap_hit->drw_id); - - /* - * Drawable may have disappeared since this swap was - * queued - */ - if (!drw) - continue; - - rect = drw->rects; - pipe = swap_hit->pipe; - top = upper[pipe]; - bottom = lower[pipe]; - - for (num_rects = drw->num_rects; num_rects--; rect++) { - int y1 = max(rect->y1, top); - int y2 = min(rect->y2, bottom); - - if (y1 >= y2) - continue; - - BEGIN_LP_RING(8); - - OUT_RING(cmd); - OUT_RING(ropcpp | dst_pitch); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING((y2 << 16) | rect->x2); - OUT_RING(sarea_priv->front_offset); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING(src_pitch); - OUT_RING(sarea_priv->back_offset); - - ADVANCE_LP_RING(); - } - } - } - - mtx_leave(&dev->drw_lock); - DRM_UNLOCK(); - - list_for_each_safe(hit, tmp, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - - list_del(hit); - - drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); - } -} - u32 i915_get_vblank_counter(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -407,11 +207,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev_priv->irq_queue); } - if (vblank) { - if (dev_priv->swaps_pending > 0) - drm_locked_tasklet(dev, i915_vblank_tasklet); - } - return IRQ_HANDLED; } @@ -632,118 +427,27 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, return 0; } -/** +/* * Schedule buffer swap at given vertical blank. */ int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_swap_t *swap = data; - drm_i915_vbl_swap_t *vbl_swap, *vbl_old; - unsigned int pipe, seqtype, curseq, plane; - struct list_head *list; - int ret; - - if (!dev_priv || !dev_priv->sarea_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return EINVAL; - } - - if (dev_priv->sarea_priv->rotation) { - DRM_DEBUG("Rotation not supported\n"); - return EINVAL; - } - - if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | - _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { - DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); - return EINVAL; - } - - plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; - pipe = i915_get_pipe(dev, plane); - - seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); - - if ((1 << pipe && DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B) == 0) { - DRM_ERROR("Invalid pipe %d\n", pipe); - return EINVAL; - } - - mtx_enter(&dev->drw_lock); - - if (!drm_get_drawable_info(dev, swap->drawable)) { - mtx_leave(&dev->drw_lock); - DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); - return EINVAL; - } - - mtx_leave(&dev->drw_lock); - - /* - * We take the ref here and put it when the swap actually completes - * in the tasklet. + /* The delayed swap mechanism was fundamentally racy, and has been + * removed. The model was that the client requested a delayed flip/swap + * from the kernel, then waited for vblank before continuing to perform + * rendering. The problem was that the kernel might wake the client + * up before it dispatched the vblank swap (since the lock has to be + * held while touching the ringbuffer), in which case the client would + * clear and start the next frame before the swap occurred, and + * flicker would occur in addition to likely missing the vblank. + * + * In the absence of this ioctl, userland falls back to a correct path + * of waiting for a vblank, then dispatching the swap on its own. + * Context switching to userland and back is plenty fast enough for + * meeting the requirements of vblank swapping. */ - ret = drm_vblank_get(dev, pipe); - if (ret) - return ret; - curseq = drm_vblank_count(dev, pipe); - - if (seqtype == _DRM_VBLANK_RELATIVE) - swap->sequence += curseq; - - if ((curseq - swap->sequence) <= (1<<23)) { - if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) { - swap->sequence = curseq + 1; - } else { - DRM_DEBUG("Missed target sequence\n"); - drm_vblank_put(dev, pipe); - return EINVAL; - } - } - - vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER); - if (vbl_swap == NULL) { - DRM_ERROR("Failed to allocate memory to queue swap\n"); - drm_vblank_put(dev, pipe); - return (ENOMEM); - } - - vbl_swap->drw_id = swap->drawable; - vbl_swap->pipe = pipe; - vbl_swap->sequence = swap->sequence; - - mtx_enter(&dev_priv->swaps_lock); - - list_for_each(list, &dev_priv->vbl_swaps.head) { - vbl_old = list_entry(list, drm_i915_vbl_swap_t, head); - - if (vbl_old->drw_id == swap->drawable && - vbl_old->pipe == pipe && - vbl_old->sequence == swap->sequence) { - mtx_leave(&dev_priv->swaps_lock); - drm_vblank_put(dev, pipe); - drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); - DRM_DEBUG("Already scheduled\n"); - return 0; - } - } - - if (dev_priv->swaps_pending >= 10) { - DRM_DEBUG("Too many swaps queued\n"); - mtx_leave(&dev_priv->swaps_lock); - drm_vblank_put(dev, pipe); - drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); - return EBUSY; - } - - list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); - dev_priv->swaps_pending++; - - mtx_leave(&dev_priv->swaps_lock); - - return 0; + return (EINVAL); } /* drm_dma.h hooks @@ -762,9 +466,6 @@ int i915_driver_irq_postinstall(struct drm_device * dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret, num_pipes = 2; - INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); - dev_priv->swaps_pending = 0; - dev_priv->irq_mask_reg = ~0; ret = drm_vblank_init(dev, num_pipes); |