From 52e5d24fae4af6f2f4a5304a516c8c5ab347a11b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 00:19:42 -0700 Subject: [intel-gem] Add DRM_IOCTL_I915_GEM_SW_FINISH to flag CPU writes When a software fallback has completed, usermode must notify the kernel so that any scanout buffers can be synchronized. This ioctl should be called whenever a fallback completes to flush CPU and chipset caches. --- shared-core/i915_dma.c | 1 + shared-core/i915_drm.h | 9 +++++++++ shared-core/i915_drv.h | 2 ++ 3 files changed, 12 insertions(+) (limited to 'shared-core') diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 2db6be07..6ee3d19f 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -1216,6 +1216,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0), + DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h index f16dc99f..fde474d7 100644 --- a/shared-core/i915_drm.h +++ b/shared-core/i915_drm.h @@ -189,6 +189,7 @@ typedef struct drm_i915_sarea { #define DRM_I915_GEM_PWRITE 0x1d #define DRM_I915_GEM_MMAP 0x1e #define DRM_I915_GEM_SET_DOMAIN 0x1f +#define DRM_I915_GEM_SW_FINISH 0x20 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -221,6 +222,7 @@ typedef struct drm_i915_sarea { #define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) #define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) #define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) +#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) /* Asynchronous page flipping: */ @@ -501,6 +503,11 @@ struct drm_i915_gem_set_domain { uint32_t write_domain; }; +struct drm_i915_gem_sw_finish { + /** Handle for the object */ + uint32_t handle; +}; + struct drm_i915_gem_relocation_entry { /** * Handle of the buffer being pointed to by this relocation entry. @@ -565,6 +572,8 @@ struct drm_i915_gem_relocation_entry { #define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 /** Vertex address cache */ #define I915_GEM_DOMAIN_VERTEX 0x00000020 +/** GTT domain - aperture and scanout */ +#define I915_GEM_DOMAIN_GTT 0x00000040 /** @} */ struct drm_i915_gem_exec_object { diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 334bc43f..4d8a40d4 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -477,6 +477,8 @@ int i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_pin_ioctl(struct drm_device *dev, void *data, -- cgit v1.2.3 From 918420deefb978d4e572121b4273d717bdbfde8e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 00:21:37 -0700 Subject: [intel-gem] Use shmem_getpage instead of find_or_create_page find_or_create_page doesn't quite set up pages correctly; any newly created pages aren't hooked into the shmem object quite right; user space mmaps of those pages end up mapping pages full of zeros which then get written to the real pages inappropriately. This patch requires that the kernel export shmem_getpage. --- shared-core/i915_drv.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'shared-core') diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 4d8a40d4..94e80cda 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -332,6 +332,12 @@ struct drm_i915_gem_object { */ int active; + /** + * This is set if the object has been written to since last bound + * to the GTT + */ + int dirty; + /** AGP memory structure for our GTT binding. */ DRM_AGP_MEM *agp_mem; -- cgit v1.2.3 From 71b1623e22c54d42837840a1d0479127a5049caf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 21:07:46 -0700 Subject: [intel-gem] Add intel-specific /proc entries to help monitor gem operation This adds gem_active, gem_flushing, gem_inactive, gem_request and gem_seqno entries to monitor gem operation and help debug issues. --- shared-core/i915_drv.h | 13 +++++++++++++ shared-core/i915_irq.c | 1 + 2 files changed, 14 insertions(+) (limited to 'shared-core') diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 94e80cda..87776504 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -289,6 +289,16 @@ typedef struct drm_i915_private { uint32_t next_gem_seqno; + /** + * Waiting sequence number, if any + */ + uint32_t waiting_gem_seqno; + + /** + * Last seq seen at irq time + */ + uint32_t irq_gem_seqno; + /** * Flag if the X Server, and thus DRM, is not currently in * control of the device. @@ -499,11 +509,14 @@ int i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_proc_init(struct drm_minor *minor); +void i915_gem_proc_cleanup(struct drm_minor *minor); int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_free_object(struct drm_gem_object *obj); int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment); void i915_gem_object_unpin(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); +uint32_t i915_get_gem_seqno(struct drm_device *dev); void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_work_handler(struct work_struct *work); #endif diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 9ba5b00a..767181c2 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -489,6 +489,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); if (iir & I915_USER_INTERRUPT) { + dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); DRM_WAKEUP(&dev_priv->irq_queue); #ifdef I915_HAVE_FENCE i915_fence_handler(dev); -- cgit v1.2.3 From 33114e4a1167ac79cb53043e77c16cc7fe40a640 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 21 Jun 2008 00:12:21 -0700 Subject: [intel] Count received interrupts Another patch adds this to a /proc/dri file for debugging and monitoring. --- shared-core/i915_drv.h | 1 - shared-core/i915_irq.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'shared-core') diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 87776504..9f500330 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -119,7 +119,6 @@ typedef struct drm_i915_private { wait_queue_head_t irq_queue; atomic_t irq_received; - atomic_t irq_emitted; int tex_lru_log_granularity; int allow_batchbuffer; diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 767181c2..c0abcbdd 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -456,6 +456,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) #if 0 DRM_DEBUG("flag=%08x\n", iir); #endif + atomic_inc(&dev_priv->irq_received); if (iir == 0) { DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n", iir, @@ -963,6 +964,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + atomic_set(&dev_priv->irq_received, 0); I915_WRITE(I915REG_HWSTAM, 0xffff); I915_WRITE(I915REG_INT_ENABLE_R, 0x0); I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); -- cgit v1.2.3 From a369bf0e575697308690f532576caf652e42b4cb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 21 Jun 2008 00:33:07 -0700 Subject: [intel] Use IMR instead of IER to pend interrupts during ISR Noting that the interrupt mask register was more reliable than the interrupt enable register for managing interrupts in user_irq_on/user_irq_off, this patch replaces the remaining IER frobbing with IMR instead. The test which exposes IER related failures is: $ glxgears & glxgears & glxgears (reposition the glxgears windows away from the upper left corner) $ while :; do x11perf -rect100 -reps 800 -repeat 1; sleep 1; done & $ while :; do runoa; runet; done & --- shared-core/i915_irq.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'shared-core') diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index c0abcbdd..a55497a8 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -451,7 +451,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) int vblank = 0; if (dev->pdev->msi_enabled) - I915_WRITE(I915REG_INT_ENABLE_R, 0); + I915_WRITE(I915REG_INT_MASK_R, ~0); iir = I915_READ(I915REG_INT_IDENTITY_R); #if 0 DRM_DEBUG("flag=%08x\n", iir); @@ -464,9 +464,11 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_READ(I915REG_INT_ENABLE_R), I915_READ(I915REG_PIPEASTAT), I915_READ(I915REG_PIPEBSTAT)); - if (dev->pdev->msi_enabled) - I915_WRITE(I915REG_INT_ENABLE_R, - I915_INTERRUPT_ENABLE_MASK); + if (dev->pdev->msi_enabled) { + I915_WRITE(I915REG_INT_MASK_R, + dev_priv->irq_mask_reg); + (void) I915_READ(I915REG_INT_MASK_R); + } return IRQ_NONE; } @@ -484,6 +486,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) } I915_WRITE(I915REG_INT_IDENTITY_R, iir); + if (dev->pdev->msi_enabled) + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted writes */ if (dev_priv->sarea_priv) @@ -512,8 +516,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_locked_tasklet(dev, i915_vblank_tasklet); } - if (dev->pdev->msi_enabled) - I915_WRITE(I915REG_INT_ENABLE_R, I915_INTERRUPT_ENABLE_MASK); return IRQ_HANDLED; } @@ -543,6 +545,7 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv) if ((dev_priv->irq_mask_reg & I915_USER_INTERRUPT) != 0) { dev_priv->irq_mask_reg &= ~I915_USER_INTERRUPT; I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + I915_WRITE(I915REG_INT_IDENTITY_R, I915_USER_INTERRUPT); (void) I915_READ (I915REG_INT_MASK_R); } } @@ -740,7 +743,7 @@ static void i915_enable_interrupt (struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->irq_mask_reg = I915_INTERRUPT_ENABLE_MASK; + dev_priv->irq_mask_reg = ~0; I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); I915_WRITE(I915REG_INT_ENABLE_R, I915_INTERRUPT_ENABLE_MASK); (void) I915_READ (I915REG_INT_ENABLE_R); -- cgit v1.2.3 From ed73651d47a5f95c3436207144b70811366e4edd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 10:16:35 -0700 Subject: [intel-gem] Recover resources from wedged hardware. Clean up queues, free objects. On the next entervt, unmark the hardware to let the user try again (presumably after resetting the chip). Someday we'll automatically recover... --- shared-core/i915_drv.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'shared-core') diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 9f500330..0df5af9c 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -307,6 +307,15 @@ typedef struct drm_i915_private { * transitioned away from for kernel modesetting. */ int suspended; + + /** + * Flag if the hardware appears to be wedged. + * + * This is set when attempts to idle the device timeout. + * It prevents command submission from occuring and makes + * every pending request fail + */ + int wedged; } mm; } drm_i915_private_t; -- cgit v1.2.3 From e36da6a133328a4cf9c98d9347c87dc3c3a12d16 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 24 Jun 2008 13:08:04 -0700 Subject: [intel] Create functions to enable/disable interrupts This shares common code sequences for managing the interrupt register bits --- shared-core/i915_irq.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'shared-core') diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index a55497a8..710b2896 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -40,6 +40,26 @@ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +static inline void +i915_enable_irq(drm_i915_private_t *dev_priv, uint32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != 0) { + dev_priv->irq_mask_reg &= ~mask; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ(I915REG_INT_MASK_R); + } +} + +static inline void +i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) +{ + if ((dev_priv->irq_mask_reg & mask) != mask) { + dev_priv->irq_mask_reg |= mask; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ(I915REG_INT_MASK_R); + } +} + /** * i915_get_pipe - return the the pipe associated with a given plane * @dev: DRM device @@ -541,29 +561,17 @@ int i915_emit_irq(struct drm_device *dev) void i915_user_irq_on(drm_i915_private_t *dev_priv) { DRM_SPINLOCK(&dev_priv->user_irq_lock); - if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ - if ((dev_priv->irq_mask_reg & I915_USER_INTERRUPT) != 0) { - dev_priv->irq_mask_reg &= ~I915_USER_INTERRUPT; - I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); - I915_WRITE(I915REG_INT_IDENTITY_R, I915_USER_INTERRUPT); - (void) I915_READ (I915REG_INT_MASK_R); - } - } + if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)) + i915_enable_irq(dev_priv, I915_USER_INTERRUPT); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); - } void i915_user_irq_off(drm_i915_private_t *dev_priv) { DRM_SPINLOCK(&dev_priv->user_irq_lock); BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0); - if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { - if ((dev_priv->irq_mask_reg & I915_USER_INTERRUPT) == 0) { - dev_priv->irq_mask_reg |= I915_USER_INTERRUPT; - I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); - (void) I915_READ(I915REG_INT_MASK_R); - } - } + if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) + i915_disable_irq(dev_priv, I915_USER_INTERRUPT); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } @@ -688,9 +696,7 @@ int i915_enable_vblank(struct drm_device *dev, int plane) I915_WRITE(pipestat_reg, pipestat); } DRM_SPINLOCK(&dev_priv->user_irq_lock); - dev_priv->irq_mask_reg &= ~mask_reg; - I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); - I915_READ(I915REG_INT_MASK_R); + i915_enable_irq(dev_priv, mask_reg); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); return 0; @@ -720,9 +726,7 @@ void i915_disable_vblank(struct drm_device *dev, int plane) } DRM_SPINLOCK(&dev_priv->user_irq_lock); - dev_priv->irq_mask_reg |= mask_reg; - I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); - (void) I915_READ (I915REG_INT_MASK_R); + i915_disable_irq(dev_priv, mask_reg); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); if (pipestat_reg) { -- cgit v1.2.3 From d250a55fc6a726a8bfaf4f871eeb09c895a9ba51 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 24 Jun 2008 13:39:25 -0700 Subject: [intel] Get vblank pipe from irq_mask_reg instead of hardware enable reg With the interrupt enable/disable using only the mask register, it was wrong to use the enable register to detect which pipes had vblank detection turned on. Also, as we keep a local copy of the mask register around, and MSI machines smack the hardware during the interrupt handler, it is more efficient and more correct to use the local copy. --- shared-core/i915_irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'shared-core') diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 710b2896..f09ae5f7 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -794,14 +794,15 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_pipe_t *pipe = data; - u16 flag; + u32 flag = 0; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - flag = I915_READ(I915REG_INT_ENABLE_R); + if (dev_priv->irq_enabled) + flag = ~dev_priv->irq_mask_reg; pipe->pipe = 0; if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) pipe->pipe |= DRM_I915_VBLANK_PIPE_A; -- cgit v1.2.3