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. --- libdrm/intel/intel_bufmgr_gem.c | 39 ++++--- linux-core/i915_gem.c | 245 +++++++++++++++++++++++++--------------- shared-core/i915_dma.c | 1 + shared-core/i915_drm.h | 9 ++ shared-core/i915_drv.h | 2 + 5 files changed, 192 insertions(+), 104 deletions(-) diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c index 5a28bd14..b970eacf 100644 --- a/libdrm/intel/intel_bufmgr_gem.c +++ b/libdrm/intel/intel_bufmgr_gem.c @@ -109,11 +109,11 @@ struct _dri_bo_gem { int validate_index; /** - * Boolean whether set_domain to CPU is current - * Set when set_domain has been called - * Cleared when a batch has been submitted + * Boolean whether we've started swrast + * Set when the buffer has been mapped + * Cleared when the buffer is unmapped */ - int cpu_domain_set; + int swrast; /** Array passed to the DRM containing relocation information. */ struct drm_i915_gem_relocation_entry *relocs; @@ -485,25 +485,27 @@ dri_gem_bo_map(dri_bo *bo, int write_enable) bo_gem->virtual = (void *)(uintptr_t)mmap_arg.addr_ptr; } bo->virtual = bo_gem->virtual; + bo_gem->swrast = 0; bo_gem->mapped = 1; DBG("bo_map: %d (%s) -> %p\n", bo_gem->gem_handle, bo_gem->name, bo_gem->virtual); } - if (!bo_gem->cpu_domain_set) { + if (!bo_gem->swrast) { set_domain.handle = bo_gem->gem_handle; set_domain.read_domains = I915_GEM_DOMAIN_CPU; - set_domain.write_domain = write_enable ? I915_GEM_DOMAIN_CPU : 0; + if (write_enable) + set_domain.write_domain = I915_GEM_DOMAIN_CPU; + else + set_domain.write_domain = 0; do { ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); } while (ret == -1 && errno == EINTR); if (ret != 0) { - fprintf (stderr, "%s:%d: Error setting memory domains %d (%08x %08x): %s .\n", - __FILE__, __LINE__, - bo_gem->gem_handle, set_domain.read_domains, set_domain.write_domain, - strerror (errno)); + fprintf (stderr, "%s:%d: Error setting swrast %d: %s\n", + __FILE__, __LINE__, bo_gem->gem_handle, strerror (errno)); } - bo_gem->cpu_domain_set = 1; + bo_gem->swrast = 1; } return 0; @@ -512,13 +514,24 @@ dri_gem_bo_map(dri_bo *bo, int write_enable) static int dri_gem_bo_unmap(dri_bo *bo) { + dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr; dri_bo_gem *bo_gem = (dri_bo_gem *)bo; + struct drm_i915_gem_sw_finish sw_finish; + int ret; if (bo == NULL) return 0; assert(bo_gem->mapped); + if (bo_gem->swrast) { + sw_finish.handle = bo_gem->gem_handle; + do { + ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SW_FINISH, + &sw_finish); + } while (ret == -1 && errno == EINTR); + bo_gem->swrast = 0; + } return 0; } @@ -744,8 +757,8 @@ dri_gem_post_submit(dri_bo *batch_buf) dri_bo *bo = bufmgr_gem->exec_bos[i]; dri_bo_gem *bo_gem = (dri_bo_gem *)bo; - /* Need to call set_domain on next bo_map */ - bo_gem->cpu_domain_set = 0; + /* Need to call swrast on next bo_map */ + bo_gem->swrast = 0; /* Disconnect the buffer from the validate list */ bo_gem->validate_index = -1; diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 308674d8..bd4aeaa7 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -47,6 +47,9 @@ i915_gem_set_domain(struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain); +static void +i915_gem_clflush_object(struct drm_gem_object *obj); + int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -225,6 +228,45 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, return ret; } +/** + * Called when user space has done writes to this buffer + */ +int +i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_sw_finish *args = data; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret = 0; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + +#if WATCH_BUF + DRM_INFO("%s: sw_finish %d (%p)\n", + __func__, args->handle, obj); +#endif + obj_priv = obj->driver_private; + + /** Pinned buffers may be scanout, so flush the cache + */ + if ((obj->write_domain & I915_GEM_DOMAIN_CPU) && obj_priv->pin_count) { + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + } + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + /** * Maps the contents of an object, returning the address it is mapped * into. @@ -1180,13 +1222,16 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t write_domain) { struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; int ret; #if WATCH_BUF - DRM_INFO("%s: object %p read %08x write %08x\n", - __func__, obj, read_domains, write_domain); + DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", + __func__, obj, + obj->read_domains, read_domains, + obj->write_domain, write_domain); #endif /* * If the object isn't moving to a new write domain, @@ -1234,6 +1279,12 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, obj->read_domains = read_domains; dev->invalidate_domains |= invalidate_domains; dev->flush_domains |= flush_domains; +#if WATCH_BUF + DRM_INFO("%s: read %08x write %08x invalidate %08x flush %08x\n", + __func__, + obj->read_domains, obj->write_domain, + dev->invalidate_domains, dev->flush_domains); +#endif return 0; } @@ -1899,6 +1950,14 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, return ret; } + /** XXX - flush the CPU caches for pinned objects + * as the X server doesn't manage domains yet + */ + if (obj->write_domain & I915_GEM_DOMAIN_CPU) { + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + obj->write_domain = 0; + } args->offset = obj_priv->gtt_offset; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); @@ -2000,43 +2059,105 @@ i915_gem_set_domain(struct drm_gem_object *obj, { struct drm_device *dev = obj->dev; int ret; + uint32_t flush_domains; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); ret = i915_gem_object_set_domain(obj, read_domains, write_domain); if (ret) return ret; - i915_gem_dev_set_domain(obj->dev); + flush_domains = i915_gem_dev_set_domain(obj->dev); + + if (flush_domains & ~I915_GEM_DOMAIN_CPU) + (void) i915_add_request(dev, flush_domains); return 0; } -void -i915_gem_lastclose(struct drm_device *dev) +/** Unbinds all objects that are on the given buffer list. */ +static int +i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) +{ + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + while (!list_empty(head)) { + obj_priv = list_first_entry(head, + struct drm_i915_gem_object, + list); + obj = obj_priv->obj; + + if (obj_priv->pin_count != 0) { + DRM_ERROR("Pinned object in unbind list\n"); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + ret = i915_gem_object_unbind(obj); + if (ret != 0) { + DRM_ERROR("Error unbinding object in LeaveVT: %d\n", + ret); + mutex_unlock(&dev->struct_mutex); + return ret; + } + } + + + return 0; +} + +static int +i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t seqno; + int ret; - mutex_lock(&dev->struct_mutex); + if (dev_priv->mm.suspended) + return 0; - /* Assume that the chip has been idled at this point. Just pull them - * off the execution list and unref them. Since this is the last - * close, this is also the last ref and they'll go away. + /* Hack! Don't let anybody do execbuf while we don't control the chip. + * We need to replace this with a semaphore, or something. */ + dev_priv->mm.suspended = 1; - while (!list_empty(&dev_priv->mm.active_list)) { - struct drm_i915_gem_object *obj_priv; + i915_kernel_lost_context(dev); - obj_priv = list_first_entry(&dev_priv->mm.active_list, - struct drm_i915_gem_object, - list); + /* Flush the GPU along with all non-CPU write domains + */ + i915_gem_flush(dev, ~I915_GEM_DOMAIN_CPU, ~I915_GEM_DOMAIN_CPU); + seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU); - list_del_init(&obj_priv->list); - obj_priv->active = 0; - obj_priv->obj->write_domain = 0; - drm_gem_object_unreference(obj_priv->obj); + if (seqno == 0) { + mutex_unlock(&dev->struct_mutex); + return -ENOMEM; + } + ret = i915_wait_request(dev, seqno); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ret; } - mutex_unlock(&dev->struct_mutex); + /* Active and flushing should now be empty as we've + * waited for a sequence higher than any pending execbuffer + */ + BUG_ON(!list_empty(&dev_priv->mm.active_list)); + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + + /* Request should now be empty as we've also waited + * for the last request in the list + */ + BUG_ON(!list_empty(&dev_priv->mm.request_list)); + + /* Move all buffers out of the GTT. */ + i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); + + BUG_ON(!list_empty(&dev_priv->mm.active_list)); + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); + BUG_ON(!list_empty(&dev_priv->mm.request_list)); + return 0; } static int @@ -2136,91 +2257,33 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, return 0; } -/** Unbinds all objects that are on the given buffer list. */ -static int -i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) +int +i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) { - struct drm_gem_object *obj; - struct drm_i915_gem_object *obj_priv; int ret; - while (!list_empty(head)) { - obj_priv = list_first_entry(head, - struct drm_i915_gem_object, - list); - obj = obj_priv->obj; - - if (obj_priv->pin_count != 0) { - DRM_ERROR("Pinned object in unbind list\n"); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - ret = i915_gem_object_unbind(obj); - if (ret != 0) { - DRM_ERROR("Error unbinding object in LeaveVT: %d\n", - ret); - mutex_unlock(&dev->struct_mutex); - return ret; - } - } - + mutex_lock(&dev->struct_mutex); + ret = i915_gem_idle(dev); + if (ret == 0) + i915_gem_cleanup_ringbuffer(dev); + mutex_unlock(&dev->struct_mutex); return 0; } -int -i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +void +i915_gem_lastclose(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t seqno; int ret; mutex_lock(&dev->struct_mutex); - /* Hack! Don't let anybody do execbuf while we don't control the chip. - * We need to replace this with a semaphore, or something. - */ - dev_priv->mm.suspended = 1; - - i915_kernel_lost_context(dev); - - /* Flush the GPU along with all non-CPU write domains - */ - i915_gem_flush(dev, ~I915_GEM_DOMAIN_CPU, ~I915_GEM_DOMAIN_CPU); - seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU); - if (seqno == 0) { - mutex_unlock(&dev->struct_mutex); - return -ENOMEM; - } - ret = i915_wait_request(dev, seqno); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - /* Active and flushing should now be empty as we've - * waited for a sequence higher than any pending execbuffer - */ - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - - /* Request should now be empty as we've also waited - * for the last request in the list - */ - BUG_ON(!list_empty(&dev_priv->mm.request_list)); - - /* Move all buffers out of the GTT. */ - i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); - - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); - BUG_ON(!list_empty(&dev_priv->mm.request_list)); + ret = i915_gem_idle(dev); + if (ret) + DRM_ERROR("failed to idle hardware: %d\n", ret); i915_gem_cleanup_ringbuffer(dev); - + mutex_unlock(&dev->struct_mutex); - - return 0; } 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. --- linux-core/i915_gem.c | 44 +++++++++++++++++++++++++++++++++++--------- shared-core/i915_drv.h | 6 ++++++ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index bd4aeaa7..129c9f3e 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -320,8 +320,13 @@ i915_gem_object_free_page_list(struct drm_gem_object *obj) for (i = 0; i < page_count; i++) - if (obj_priv->page_list[i] != NULL) + if (obj_priv->page_list[i] != NULL) { + if (obj_priv->dirty) + set_page_dirty(obj_priv->page_list[i]); + mark_page_accessed(obj_priv->page_list[i]); page_cache_release(obj_priv->page_list[i]); + } + obj_priv->dirty = 0; drm_free(obj_priv->page_list, page_count * sizeof(struct page *), @@ -969,6 +974,11 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj) { struct drm_i915_gem_object *obj_priv = obj->driver_private; int page_count, i; + struct address_space *mapping; + struct inode *inode; + struct page *page; + int ret; + if (obj_priv->page_list) return 0; @@ -984,16 +994,25 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj) return -ENOMEM; } + inode = obj->filp->f_path.dentry->d_inode; + mapping = inode->i_mapping; for (i = 0; i < page_count; i++) { - obj_priv->page_list[i] = - find_or_create_page(obj->filp->f_mapping, i, GFP_HIGHUSER); - - if (obj_priv->page_list[i] == NULL) { - DRM_ERROR("Failed to find_or_create_page()\n"); - i915_gem_object_free_page_list(obj); - return -ENOMEM; + page = find_get_page(mapping, i); + if (page == NULL || !PageUptodate(page)) { + if (page) { + page_cache_release(page); + page = NULL; + } + ret = shmem_getpage(inode, i, &page, SGP_DIRTY, NULL); + + if (ret) { + DRM_ERROR("shmem_getpage failed: %d\n", ret); + i915_gem_object_free_page_list(obj); + return ret; + } + unlock_page(page); } - unlock_page(obj_priv->page_list[i]); + obj_priv->page_list[i] = page; } return 0; } @@ -1239,6 +1258,8 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, */ if (write_domain == 0) read_domains |= obj->read_domains; + else + obj_priv->dirty = 1; /* * Flush the current write domain if @@ -2046,6 +2067,11 @@ int i915_gem_init_object(struct drm_gem_object *obj) void i915_gem_free_object(struct drm_gem_object *obj) { + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + while (obj_priv->pin_count > 0) + i915_gem_object_unpin(obj); + i915_gem_object_unbind(obj); drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); 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 2bd9799e4cf0d778e46453422157143e36274062 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 16:40:14 -0700 Subject: Add device-specific proc_init and proc_cleanup hooks This allows device drivers to add proc files --- linux-core/drmP.h | 5 ++++- linux-core/drm_drv.c | 2 +- linux-core/drm_stub.c | 22 ++++++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 392d2ace..ec8a61d4 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -770,6 +770,9 @@ struct drm_driver { void (*set_version) (struct drm_device *dev, struct drm_set_version *sv); + int (*proc_init)(struct drm_minor *minor); + void (*proc_cleanup)(struct drm_minor *minor); + /** * Driver-specific constructor for drm_gem_objects, to set up * obj->driver_private. @@ -1287,7 +1290,7 @@ extern void drm_agp_chipset_flush(struct drm_device *dev); extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver); extern int drm_put_dev(struct drm_device *dev); -extern int drm_put_minor(struct drm_minor **minor); +extern int drm_put_minor(struct drm_device *dev); extern unsigned int drm_debug; /* 1 to enable debug output */ extern struct class *drm_class; diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 980752c2..bc32ed50 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -422,7 +422,7 @@ static void drm_cleanup(struct drm_device * dev) drm_memrange_takedown(&dev->offset_manager); drm_ht_remove(&dev->object_hash); - drm_put_minor(&dev->primary); + drm_put_minor(dev); if (drm_put_dev(dev)) DRM_ERROR("Cannot unload module\n"); } diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index 55841826..1aacd4ff 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -222,6 +222,13 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t DRM_ERROR("DRM: Failed to initialize /proc/dri.\n"); goto err_mem; } + if (dev->driver->proc_init) { + ret = dev->driver->proc_init(new_minor); + if (ret) { + DRM_ERROR("DRM: Driver failed to initialize /proc/dri.\n"); + goto err_mem; + } + } } else new_minor->dev_root = NULL; @@ -238,8 +245,11 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t err_g2: - if (new_minor->type == DRM_MINOR_LEGACY) + if (new_minor->type == DRM_MINOR_LEGACY) { + if (dev->driver->proc_cleanup) + dev->driver->proc_cleanup(new_minor); drm_proc_cleanup(new_minor, drm_proc_root); + } err_mem: kfree(new_minor); err_idr: @@ -302,7 +312,7 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, return 0; err_g4: - drm_put_minor(&dev->primary); + drm_put_minor(dev); err_g3: if (!drm_fb_loaded) pci_disable_device(pdev); @@ -358,13 +368,17 @@ int drm_put_dev(struct drm_device * dev) * last minor released. * */ -int drm_put_minor(struct drm_minor **minor_p) +int drm_put_minor(struct drm_device *dev) { + struct drm_minor **minor_p = &dev->primary; struct drm_minor *minor = *minor_p; DRM_DEBUG("release secondary minor %d\n", minor->index); - if (minor->type == DRM_MINOR_LEGACY) + if (minor->type == DRM_MINOR_LEGACY) { + if (dev->driver->proc_cleanup) + dev->driver->proc_cleanup(minor); drm_proc_cleanup(minor, drm_proc_root); + } drm_sysfs_device_remove(minor); idr_remove(&drm_minors_idr, minor->index); -- 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. --- linux-core/i915_drv.c | 2 + linux-core/i915_gem.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++- shared-core/i915_drv.h | 13 +++ shared-core/i915_irq.c | 1 + 4 files changed, 236 insertions(+), 1 deletion(-) diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index e399f374..8eec336b 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -589,6 +589,8 @@ static struct drm_driver driver = { .reclaim_buffers = drm_core_reclaim_buffers, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, + .proc_init = i915_gem_proc_init, + .proc_cleanup = i915_gem_proc_cleanup, .ioctls = i915_ioctls, .gem_init_object = i915_gem_init_object, .gem_free_object = i915_gem_free_object, diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 129c9f3e..e086bc1b 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -536,7 +536,7 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1 - seq2) >= 0; } -static uint32_t +uint32_t i915_get_gem_seqno(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -604,11 +604,13 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) BUG_ON(seqno == 0); if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { + dev_priv->mm.waiting_gem_seqno = seqno; i915_user_irq_on(dev_priv); ret = wait_event_interruptible(dev_priv->irq_queue, i915_seqno_passed(i915_get_gem_seqno(dev), seqno)); i915_user_irq_off(dev_priv); + dev_priv->mm.waiting_gem_seqno = 0; } if (ret) DRM_ERROR("%s returns %d (awaiting %d at %d)\n", @@ -2298,6 +2300,223 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, return 0; } +static int i915_gem_active_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Active:\n"); + list_for_each_entry(obj_priv, &dev_priv->mm.active_list, + list) + { + struct drm_gem_object *obj = obj_priv->obj; + if (obj->name) { + DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n", + obj, obj->name, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } else { + DRM_PROC_PRINT(" %p: %08x %08x %d\n", + obj, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } + } + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static int i915_gem_flushing_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Flushing:\n"); + list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, + list) + { + struct drm_gem_object *obj = obj_priv->obj; + if (obj->name) { + DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n", + obj, obj->name, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } else { + DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } + } + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static int i915_gem_inactive_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Inactive:\n"); + list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, + list) + { + struct drm_gem_object *obj = obj_priv->obj; + if (obj->name) { + DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n", + obj, obj->name, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } else { + DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj, + obj->read_domains, obj->write_domain, + obj_priv->last_rendering_seqno); + } + } + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static int i915_gem_request_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_request *gem_request; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Request:\n"); + list_for_each_entry(gem_request, &dev_priv->mm.request_list, + list) + { + DRM_PROC_PRINT (" %d @ %d %08x\n", + gem_request->seqno, + (int) (jiffies - gem_request->emitted_jiffies), + gem_request->flush_domains); + } + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + +static int i915_gem_seqno_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Current sequence: %d\n", i915_get_gem_seqno(dev)); + DRM_PROC_PRINT("Waiter sequence: %d\n", dev_priv->mm.waiting_gem_seqno); + DRM_PROC_PRINT("IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno); + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + + +static struct drm_proc_list { + const char *name; /**< file name */ + int (*f) (char *, char **, off_t, int, int *, void *); /**< proc callback*/ +} i915_gem_proc_list[] = { + {"gem_active", i915_gem_active_info}, + {"gem_flushing", i915_gem_flushing_info}, + {"gem_inactive", i915_gem_inactive_info}, + {"gem_request", i915_gem_request_info}, + {"gem_seqno", i915_gem_seqno_info}, +}; + +#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list) + +int i915_gem_proc_init(struct drm_minor *minor) +{ + struct proc_dir_entry *ent; + int i, j; + + for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) { + ent = create_proc_entry(i915_gem_proc_list[i].name, + S_IFREG | S_IRUGO, minor->dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/dri/.../%s\n", + i915_gem_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(i915_gem_proc_list[i].name, + minor->dev_root); + return -1; + } + ent->read_proc = i915_gem_proc_list[i].f; + ent->data = minor; + } + return 0; +} + +void i915_gem_proc_cleanup(struct drm_minor *minor) +{ + int i; + + if (!minor->dev_root) + return; + + for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) + remove_proc_entry(i915_gem_proc_list[i].name, minor->dev_root); +} + void i915_gem_lastclose(struct drm_device *dev) { 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 54817317e9dd8a791418f97503fe574038dbe4b9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 20 Jun 2008 21:10:42 -0700 Subject: [intel-gem] Use polling in i915_gem_idle instead of interrupts. While waiting for the hardware to idle on leavevt or lastclose, poll for the sync sequence number instead of waiting for an interrupt. This allows the code to bail if the hardware hangs for some reason. Also, this avoids issues with signals as the exisiting wait function is interruptible. --- linux-core/i915_gem.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index e086bc1b..27b6ddbb 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -2139,8 +2139,9 @@ static int i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t seqno; + uint32_t seqno, cur_seqno, last_seqno; int ret; + int stuck; if (dev_priv->mm.suspended) return 0; @@ -2161,11 +2162,26 @@ i915_gem_idle(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); return -ENOMEM; } - ret = i915_wait_request(dev, seqno); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; + + dev_priv->mm.waiting_gem_seqno = seqno; + last_seqno = 0; + stuck = 0; + for (;;) { + cur_seqno = i915_get_gem_seqno(dev); + if (i915_seqno_passed(cur_seqno, seqno)) + break; + if (last_seqno == cur_seqno) { + if (stuck++ > 100) { + DRM_ERROR("hardware wedged\n"); + break; + } + } + msleep(10); + last_seqno = cur_seqno; } + dev_priv->mm.waiting_gem_seqno = 0; + + i915_gem_retire_requests(dev); /* Active and flushing should now be empty as we've * waited for a sequence higher than any pending execbuffer @@ -2521,14 +2537,17 @@ void i915_gem_lastclose(struct drm_device *dev) { int ret; + drm_i915_private_t *dev_priv = dev->dev_private; mutex_lock(&dev->struct_mutex); - ret = i915_gem_idle(dev); - if (ret) - DRM_ERROR("failed to idle hardware: %d\n", ret); - - i915_gem_cleanup_ringbuffer(dev); + if (dev_priv->ring.ring_obj != NULL) { + ret = i915_gem_idle(dev); + if (ret) + DRM_ERROR("failed to idle hardware: %d\n", ret); + + i915_gem_cleanup_ringbuffer(dev); + } mutex_unlock(&dev->struct_mutex); } -- cgit v1.2.3 From f4bd566e0bead0904c38bb3a526eb9b35b215ff5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 21 Jun 2008 00:10:10 -0700 Subject: [intel-gem] Remove unused variable. --- linux-core/i915_gem.c | 1 - 1 file changed, 1 deletion(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 27b6ddbb..dc88df58 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -2140,7 +2140,6 @@ i915_gem_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; uint32_t seqno, cur_seqno, last_seqno; - int ret; int stuck; if (dev_priv->mm.suspended) -- 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(-) 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 8be6ec491f7b9c633a426a34006ea4ff5a3f8392 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 21 Jun 2008 00:13:18 -0700 Subject: [intel-gem] Add /proc/dri/*/i915_gem_interrupt This tracks most of the interrupt-related status, including the interrupt registers in the chip and the sequence number variables. --- linux-core/i915_gem.c | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index dc88df58..4361b060 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -2486,15 +2486,55 @@ static int i915_gem_seqno_info(char *buf, char **start, off_t offset, } +static int i915_interrupt_info(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + struct drm_minor *minor = (struct drm_minor *) data; + struct drm_device *dev = minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *start = &buf[offset]; + *eof = 0; + DRM_PROC_PRINT("Interrupt enable: %08x\n", + I915_READ(I915REG_INT_ENABLE_R)); + DRM_PROC_PRINT("Interrupt identity: %08x\n", + I915_READ(I915REG_INT_IDENTITY_R)); + DRM_PROC_PRINT("Interrupt mask: %08x\n", + I915_READ(I915REG_INT_MASK_R)); + DRM_PROC_PRINT("Pipe A stat: %08x\n", + I915_READ(I915REG_PIPEASTAT)); + DRM_PROC_PRINT("Pipe B stat: %08x\n", + I915_READ(I915REG_PIPEBSTAT)); + DRM_PROC_PRINT("Interrupts received: %d\n", + atomic_read(&dev_priv->irq_received)); + DRM_PROC_PRINT("Current sequence: %d\n", + i915_get_gem_seqno(dev)); + DRM_PROC_PRINT("Waiter sequence: %d\n", + dev_priv->mm.waiting_gem_seqno); + DRM_PROC_PRINT("IRQ sequence: %d\n", + dev_priv->mm.irq_gem_seqno); + if (len > request + offset) + return request; + *eof = 1; + return len - offset; +} + static struct drm_proc_list { const char *name; /**< file name */ int (*f) (char *, char **, off_t, int, int *, void *); /**< proc callback*/ } i915_gem_proc_list[] = { - {"gem_active", i915_gem_active_info}, - {"gem_flushing", i915_gem_flushing_info}, - {"gem_inactive", i915_gem_inactive_info}, - {"gem_request", i915_gem_request_info}, - {"gem_seqno", i915_gem_seqno_info}, + {"i915_gem_active", i915_gem_active_info}, + {"i915_gem_flushing", i915_gem_flushing_info}, + {"i915_gem_inactive", i915_gem_inactive_info}, + {"i915_gem_request", i915_gem_request_info}, + {"i915_gem_seqno", i915_gem_seqno_info}, + {"i915_gem_interrupt", i915_interrupt_info}, }; #define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list) -- 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(-) 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 a0ebcbe9d490c3e1fb8212d52e6783b7d9bd9a70 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 00:53:04 -0700 Subject: [intel] allow the irq code to use either enable or mask registers still not sure which works best on which hardware; this will make it easier to experiment. --- shared-core/i915_drv.h | 2 + shared-core/i915_irq.c | 128 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 9f500330..82d2c50c 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -129,6 +129,8 @@ typedef struct drm_i915_private { int user_irq_refcount; int fence_irq_on; uint32_t irq_mask_reg; + uint32_t irq_enable_reg; + int irq_use_mask; int irq_enabled; #ifdef I915_HAVE_FENCE diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index a55497a8..6dbc12e4 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -40,6 +40,63 @@ 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_use_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); + } + } else { + if ((dev_priv->irq_enable_reg & mask) != mask) { + dev_priv->irq_enable_reg |= mask; + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + (void) I915_READ(I915REG_INT_ENABLE_R); + } + } +} + +static inline void +i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) +{ + if (dev_priv->irq_use_mask) { + if ((dev_priv->irq_enable_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); + } + } else { + if ((dev_priv->irq_enable_reg & mask) != 0) { + dev_priv->irq_enable_reg &= ~mask; + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + (void) I915_READ(I915REG_INT_ENABLE_R); + } + } +} + +static inline void +i915_enable_irqs(drm_i915_private_t *dev_priv) +{ + if (dev_priv->irq_use_mask) { + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ(I915REG_INT_MASK_R); + } else { + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + (void) I915_READ(I915REG_INT_ENABLE_R); + } +} + +static inline void +i915_disable_irqs(drm_i915_private_t *dev_priv) +{ + if (dev_priv->irq_use_mask) + I915_WRITE(I915REG_INT_MASK_R, I915_INTERRUPT_ENABLE_MASK); + else + I915_WRITE(I915REG_INT_ENABLE_R, 0); +} + /** * i915_get_pipe - return the the pipe associated with a given plane * @dev: DRM device @@ -450,8 +507,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) u32 pipea_stats = 0, pipeb_stats = 0; int vblank = 0; - if (dev->pdev->msi_enabled) - I915_WRITE(I915REG_INT_MASK_R, ~0); + DRM_SPINLOCK(&dev_priv->user_irq_lock); +// if (dev->pdev->msi_enabled) + i915_disable_irqs(dev_priv); iir = I915_READ(I915REG_INT_IDENTITY_R); #if 0 DRM_DEBUG("flag=%08x\n", iir); @@ -464,11 +522,10 @@ 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_MASK_R, - dev_priv->irq_mask_reg); - (void) I915_READ(I915REG_INT_MASK_R); - } +// if (dev->pdev->msi_enabled) + i915_enable_irqs(dev_priv); + + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); return IRQ_NONE; } @@ -486,10 +543,13 @@ 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->pdev->msi_enabled) + i915_enable_irqs(dev_priv); + + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); @@ -541,14 +601,8 @@ 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); } @@ -557,13 +611,8 @@ 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 +737,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,10 +767,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); - DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + i915_disable_irq(dev_priv, mask_reg); if (pipestat_reg) { pipestat = I915_READ (pipestat_reg); @@ -737,15 +781,24 @@ void i915_disable_vblank(struct drm_device *dev, int plane) I915_WRITE(pipestat_reg, pipestat); (void) I915_READ(pipestat_reg); } + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } 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 = ~0; + dev_priv->irq_use_mask = 0; + if (dev_priv->irq_use_mask) { + dev_priv->irq_mask_reg = I915_INTERRUPT_ENABLE_MASK; + dev_priv->irq_enable_reg = I915_INTERRUPT_ENABLE_MASK; + } else { + dev_priv->irq_mask_reg = 0; + dev_priv->irq_enable_reg = 0; + } + I915_WRITE(I915REG_INT_IDENTITY_R, I915_READ(I915REG_INT_IDENTITY_R)); I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); - I915_WRITE(I915REG_INT_ENABLE_R, I915_INTERRUPT_ENABLE_MASK); + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); (void) I915_READ (I915REG_INT_ENABLE_R); dev_priv->irq_enabled = 1; } @@ -755,9 +808,9 @@ static void i915_disable_interrupt (struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; I915_WRITE(I915REG_HWSTAM, 0xffffffff); - I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); + I915_WRITE(I915REG_INT_MASK_R, I915_INTERRUPT_ENABLE_MASK); I915_WRITE(I915REG_INT_ENABLE_R, 0); - I915_WRITE(I915REG_INT_IDENTITY_R, 0xffffffff); + I915_WRITE(I915REG_INT_IDENTITY_R, I915_READ(I915REG_INT_IDENTITY_R)); (void) I915_READ (I915REG_INT_IDENTITY_R); dev_priv->irq_enabled = 0; } @@ -797,7 +850,10 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, return -EINVAL; } - flag = I915_READ(I915REG_INT_ENABLE_R); + if (dev_priv->irq_use_mask) + flag = ~dev_priv->irq_mask_reg; + else + flag = dev_priv->irq_enable_reg; pipe->pipe = 0; if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) pipe->pipe |= DRM_I915_VBLANK_PIPE_A; -- cgit v1.2.3 From 61caf797aeb88af42ea0d333ad3f6ba88468d37f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 00:53:53 -0700 Subject: [intel-gem] pwrite through GTT Pin/copy_from_user/unpin through the GTT to eliminate clflush costs. Benchmarks say this helps quite a bit. --- linux-core/i915_gem.c | 158 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 125 insertions(+), 33 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 4361b060..4dfbd2a1 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -36,7 +36,14 @@ #define WATCH_LRU 0 #define WATCH_RELOC 0 #define WATCH_INACTIVE 0 +#define WATCH_PWRITE 0 +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE +static void +i915_gem_dump_object(struct drm_gem_object *obj, int len, + const char *where, uint32_t mark); +#endif + static int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, @@ -154,6 +161,8 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, return 0; } +#include "drm_compat.h" + /** * Writes data to the object referenced by handle. * @@ -165,41 +174,121 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_pwrite *args = data; struct drm_gem_object *obj; - ssize_t written; + struct drm_i915_gem_object *obj_priv; + ssize_t remain; loff_t offset; - int ret; + char __user *user_data; + char *vaddr; + int i, o, l; + int ret = 0; + unsigned long pfn; + unsigned long unwritten; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) return -EINVAL; + /** Bounds check destination. + * + * XXX: This could use review for overflow issues... + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) + return -EFAULT; + + user_data = (char __user *) (uintptr_t) args->data_ptr; + remain = args->size; + if (!access_ok(VERIFY_READ, user_data, remain)) + return -EFAULT; + + mutex_lock(&dev->struct_mutex); - ret = i915_gem_set_domain(obj, file_priv, - I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + ret = i915_gem_object_pin(obj, 0); if (ret) { drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } - offset = args->offset; + ret = i915_gem_set_domain(obj, file_priv, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + if (ret) + goto fail; + + obj_priv = obj->driver_private; + offset = obj_priv->gtt_offset + args->offset; + obj_priv->dirty = 1; + + while (remain > 0) { + + /** Operation in this page + * + * i = page number + * o = offset within page + * l = bytes to copy + */ + i = offset >> PAGE_SHIFT; + o = offset & (PAGE_SIZE-1); + l = remain; + if ((o + l) > PAGE_SIZE) + l = PAGE_SIZE - o; + + pfn = (dev->agp->base >> PAGE_SHIFT) + i; + +#ifdef DRM_KMAP_ATOMIC_PROT_PFN + /* kmap_atomic can't map IO pages on non-HIGHMEM kernels + */ + vaddr = kmap_atomic_prot_pfn(pfn, KM_USER0, + __pgprot(__PAGE_KERNEL)); +#if WATCH_PWRITE + DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", + i, o, l, pfn, vaddr); +#endif + unwritten = __copy_from_user_inatomic_nocache(vaddr + o, user_data, l); + kunmap_atomic(vaddr, KM_USER0); - written = vfs_write(obj->filp, - (char __user *)(uintptr_t) args->data_ptr, - args->size, &offset); + if (unwritten) +#endif + { + vaddr = ioremap(pfn << PAGE_SHIFT, PAGE_SIZE); +#if WATCH_PWRITE + DRM_INFO("pwrite slow i %d o %d l %d pfn %ld vaddr %p\n", + i, o, l, pfn, vaddr); +#endif + if (vaddr == NULL) { + ret = -EFAULT; + goto fail; + } + unwritten = __copy_from_user(vaddr + o, user_data, l); +#if WATCH_PWRITE + DRM_INFO("unwritten %ld\n", unwritten); +#endif + iounmap(vaddr); + if (unwritten) { + ret = -EFAULT; + goto fail; + } + } - if (written != args->size) { - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - if (written < 0) - return written; - else - return -EINVAL; + remain -= l; + user_data += l; + offset += l; } +#if WATCH_PWRITE && 1 + i915_gem_clflush_object(obj); + i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0); + i915_gem_clflush_object(obj); +#endif +fail: + i915_gem_object_unpin (obj); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); - return 0; +#if WATCH_PWRITE + if (ret) + DRM_INFO("pwrite failed %d\n", ret); +#endif + return ret; } /** @@ -361,7 +450,7 @@ i915_verify_inactive(struct drm_device *dev, char *file, int line) list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { obj = obj_priv->obj; - if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~I915_GEM_DOMAIN_CPU)) + if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) DRM_ERROR("inactive %p (p %d a %d w %x) %s:%d\n", obj, obj_priv->pin_count, obj_priv->active, obj->write_domain, file, line); @@ -644,7 +733,7 @@ i915_gem_flush(struct drm_device *dev, if (flush_domains & I915_GEM_DOMAIN_CPU) drm_agp_chipset_flush(dev); - if ((invalidate_domains|flush_domains) & ~I915_GEM_DOMAIN_CPU) { + if ((invalidate_domains|flush_domains) & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { /* * read/write caches: * @@ -712,7 +801,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) /* If there are writes queued to the buffer, flush and * create a new seqno to wait for. */ - if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU)) { + if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { uint32_t write_domain = obj->write_domain; #if WATCH_BUF DRM_INFO("%s: flushing object %p from write domain %08x\n", @@ -798,11 +887,13 @@ i915_gem_object_unbind(struct drm_gem_object *obj) i915_gem_object_free_page_list(obj); - atomic_dec(&dev->gtt_count); - atomic_sub(obj->size, &dev->gtt_memory); - - drm_memrange_put_block(obj_priv->gtt_space); - obj_priv->gtt_space = NULL; + if (obj_priv->gtt_space) { + atomic_dec(&dev->gtt_count); + atomic_sub(obj->size, &dev->gtt_memory); + + drm_memrange_put_block(obj_priv->gtt_space); + obj_priv->gtt_space = NULL; + } /* Remove ourselves from the LRU list if present. */ if (!list_empty(&obj_priv->list)) @@ -811,7 +902,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return 0; } -#if WATCH_BUF | WATCH_EXEC +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE static void i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end, uint32_t bias, uint32_t mark) @@ -1105,8 +1196,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) * wasn't in the GTT, there shouldn't be any way it could have been in * a GPU cache */ - BUG_ON(obj->read_domains & ~I915_GEM_DOMAIN_CPU); - BUG_ON(obj->write_domain & ~I915_GEM_DOMAIN_CPU); + BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); return 0; } @@ -1289,7 +1380,7 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, * flushed before the cpu cache is invalidated */ if ((invalidate_domains & I915_GEM_DOMAIN_CPU) && - (flush_domains & ~I915_GEM_DOMAIN_CPU)) { + (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))) { ret = i915_gem_object_wait_rendering(obj); if (ret) return ret; @@ -1911,7 +2002,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) if (obj_priv->pin_count == 1) { atomic_inc(&dev->pin_count); atomic_add(obj->size, &dev->pin_memory); - if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0 && + if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0 && !list_empty(&obj_priv->list)) list_del_init(&obj_priv->list); } @@ -1937,7 +2028,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj) * the inactive list */ if (obj_priv->pin_count == 0) { - if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0) + if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0) list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); atomic_dec(&dev->pin_count); @@ -2096,7 +2187,7 @@ i915_gem_set_domain(struct drm_gem_object *obj, return ret; flush_domains = i915_gem_dev_set_domain(obj->dev); - if (flush_domains & ~I915_GEM_DOMAIN_CPU) + if (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) (void) i915_add_request(dev, flush_domains); return 0; @@ -2154,8 +2245,9 @@ i915_gem_idle(struct drm_device *dev) /* Flush the GPU along with all non-CPU write domains */ - i915_gem_flush(dev, ~I915_GEM_DOMAIN_CPU, ~I915_GEM_DOMAIN_CPU); - seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU); + i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT), + ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); if (seqno == 0) { mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From 1c2dd9826793579d5ef6f51fb9f5470c4af95548 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 10:07:47 -0700 Subject: [intel] Switch to using IMR instead of IER --- shared-core/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 6dbc12e4..6f1d91b3 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -788,7 +788,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_use_mask = 0; + dev_priv->irq_use_mask = 1; if (dev_priv->irq_use_mask) { dev_priv->irq_mask_reg = I915_INTERRUPT_ENABLE_MASK; dev_priv->irq_enable_reg = I915_INTERRUPT_ENABLE_MASK; -- cgit v1.2.3 From 626e9ba494b46f6e8352c9e461227187f335e229 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... --- linux-core/i915_gem.c | 23 +++++++++++++++++++++-- shared-core/i915_drv.h | 9 +++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 4dfbd2a1..16a1b07e 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -653,7 +653,7 @@ i915_gem_retire_requests(struct drm_device *dev) list); retiring_seqno = request->seqno; - if (i915_seqno_passed(seqno, retiring_seqno)) { + if (i915_seqno_passed(seqno, retiring_seqno) || dev_priv->mm.wedged) { i915_gem_retire_request(dev, request); list_del(&request->list); @@ -697,10 +697,13 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) i915_user_irq_on(dev_priv); ret = wait_event_interruptible(dev_priv->irq_queue, i915_seqno_passed(i915_get_gem_seqno(dev), - seqno)); + seqno) || dev_priv->mm.wedged); i915_user_irq_off(dev_priv); dev_priv->mm.waiting_gem_seqno = 0; } + if (dev_priv->mm.wedged) + ret = -EIO; + if (ret) DRM_ERROR("%s returns %d (awaiting %d at %d)\n", __func__, ret, seqno, i915_get_gem_seqno(dev)); @@ -1019,6 +1022,8 @@ i915_gem_evict_something(struct drm_device *dev) list); ret = i915_wait_request(dev, request->seqno); + if (ret) + break; /* if waiting caused an object to become inactive, * then loop around and wait for it. Otherwise, we @@ -1822,6 +1827,13 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); i915_verify_inactive(dev, __FILE__, __LINE__); + + if (dev_priv->mm.wedged) { + DRM_ERROR("Execbuf while wedged\n"); + mutex_unlock(&dev->struct_mutex); + return -EIO; + } + if (dev_priv->mm.suspended) { DRM_ERROR("Execbuf while VT-switched.\n"); mutex_unlock(&dev->struct_mutex); @@ -2264,6 +2276,8 @@ i915_gem_idle(struct drm_device *dev) if (last_seqno == cur_seqno) { if (stuck++ > 100) { DRM_ERROR("hardware wedged\n"); + dev_priv->mm.wedged = 1; + DRM_WAKEUP(&dev_priv->irq_queue); break; } } @@ -2378,6 +2392,11 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; int ret; + if (dev_priv->mm.wedged) { + DRM_ERROR("Renabling wedged hardware, good luck\n"); + dev_priv->mm.wedged = 0; + } + ret = i915_gem_init_ringbuffer(dev); if (ret != 0) return ret; diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 82d2c50c..a8d5b91e 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -309,6 +309,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 27f61d0c9364bbf8bc88a4ffceb78b645aff6680 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 11:20:17 -0700 Subject: [intel] leave interrupts disabled in ISR only on MSI again While debugging the 915, I tried this trick there and accidentally left it set. --- shared-core/i915_irq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 6f1d91b3..23edddb1 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -508,7 +508,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) int vblank = 0; DRM_SPINLOCK(&dev_priv->user_irq_lock); -// if (dev->pdev->msi_enabled) + if (dev->pdev->msi_enabled) i915_disable_irqs(dev_priv); iir = I915_READ(I915REG_INT_IDENTITY_R); #if 0 @@ -522,7 +522,7 @@ 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) + if (dev->pdev->msi_enabled) i915_enable_irqs(dev_priv); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); @@ -545,7 +545,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_WRITE(I915REG_INT_IDENTITY_R, iir); (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted writes */ -// if (dev->pdev->msi_enabled) + if (dev->pdev->msi_enabled) i915_enable_irqs(dev_priv); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); -- cgit v1.2.3 From 52bf2e77b0ff77ab0c93fec374ccfeb9d214a464 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 11:21:30 -0700 Subject: [intel-gem] Use I915_GEM_DOMAIN_GTT in dri_gem_bo_wait_rendering. I915_GEM_DOMAIN_CPU is very expensive to wait for -- it generally requires clflushing the frame buffer. --- libdrm/intel/intel_bufmgr_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c index b970eacf..cdc2a7ac 100644 --- a/libdrm/intel/intel_bufmgr_gem.c +++ b/libdrm/intel/intel_bufmgr_gem.c @@ -596,7 +596,7 @@ dri_gem_bo_wait_rendering(dri_bo *bo) int ret; set_domain.handle = bo_gem->gem_handle; - set_domain.read_domains = I915_GEM_DOMAIN_CPU; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; set_domain.write_domain = 0; ret = ioctl (bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); if (ret != 0) { -- cgit v1.2.3 From 020a59e46ca1d89c98a3e309b6e5571354115133 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 22:03:06 -0700 Subject: drm_compat: it's CONFIG_HIGHMEM, not CONFIG_HIMEM A mis-spelled config option (was it spelled that way in the past?) eliminated kmap_atomic_prot_pfn from core DRM. --- linux-core/drm_compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index 30834f33..6aa19c55 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -329,7 +329,7 @@ typedef _Bool bool; #endif -#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIMEM)) +#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIGHMEM)) #define DRM_KMAP_ATOMIC_PROT_PFN extern void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t protection); -- cgit v1.2.3 From 472981a4a952e551a581e5296a575a51d4f02e3d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 22:03:33 -0700 Subject: [intel-gem] Include drm_compat.h to get kmap_atomic_prot_pfn --- linux-core/i915_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 16a1b07e..8fd2b8a6 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -27,6 +27,7 @@ #include "drmP.h" #include "drm.h" +#include "drm_compat.h" #include "i915_drm.h" #include "i915_drv.h" -- cgit v1.2.3 From 01a33d742cee55a3df66a3d29c9c55b10cc9221d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 24 Jun 2008 09:46:51 -0700 Subject: Was using irq_enable_reg in the use_mask_reg path --- shared-core/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 23edddb1..f9fed87e 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -62,7 +62,7 @@ static inline void i915_disable_irq(drm_i915_private_t *dev_priv, uint32_t mask) { if (dev_priv->irq_use_mask) { - if ((dev_priv->irq_enable_reg & mask) != 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); -- cgit v1.2.3 From 71d975072cf57507385bdf8e0bf4af4c23b1fceb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 00:53:53 -0700 Subject: [intel-gem] pwrite through GTT Pin/copy_from_user/unpin through the GTT to eliminate clflush costs. Benchmarks say this helps quite a bit. --- linux-core/i915_gem.c | 158 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 125 insertions(+), 33 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 4361b060..4dfbd2a1 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -36,7 +36,14 @@ #define WATCH_LRU 0 #define WATCH_RELOC 0 #define WATCH_INACTIVE 0 +#define WATCH_PWRITE 0 +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE +static void +i915_gem_dump_object(struct drm_gem_object *obj, int len, + const char *where, uint32_t mark); +#endif + static int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, @@ -154,6 +161,8 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, return 0; } +#include "drm_compat.h" + /** * Writes data to the object referenced by handle. * @@ -165,41 +174,121 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_pwrite *args = data; struct drm_gem_object *obj; - ssize_t written; + struct drm_i915_gem_object *obj_priv; + ssize_t remain; loff_t offset; - int ret; + char __user *user_data; + char *vaddr; + int i, o, l; + int ret = 0; + unsigned long pfn; + unsigned long unwritten; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) return -EINVAL; + /** Bounds check destination. + * + * XXX: This could use review for overflow issues... + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) + return -EFAULT; + + user_data = (char __user *) (uintptr_t) args->data_ptr; + remain = args->size; + if (!access_ok(VERIFY_READ, user_data, remain)) + return -EFAULT; + + mutex_lock(&dev->struct_mutex); - ret = i915_gem_set_domain(obj, file_priv, - I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + ret = i915_gem_object_pin(obj, 0); if (ret) { drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } - offset = args->offset; + ret = i915_gem_set_domain(obj, file_priv, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + if (ret) + goto fail; + + obj_priv = obj->driver_private; + offset = obj_priv->gtt_offset + args->offset; + obj_priv->dirty = 1; + + while (remain > 0) { + + /** Operation in this page + * + * i = page number + * o = offset within page + * l = bytes to copy + */ + i = offset >> PAGE_SHIFT; + o = offset & (PAGE_SIZE-1); + l = remain; + if ((o + l) > PAGE_SIZE) + l = PAGE_SIZE - o; + + pfn = (dev->agp->base >> PAGE_SHIFT) + i; + +#ifdef DRM_KMAP_ATOMIC_PROT_PFN + /* kmap_atomic can't map IO pages on non-HIGHMEM kernels + */ + vaddr = kmap_atomic_prot_pfn(pfn, KM_USER0, + __pgprot(__PAGE_KERNEL)); +#if WATCH_PWRITE + DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n", + i, o, l, pfn, vaddr); +#endif + unwritten = __copy_from_user_inatomic_nocache(vaddr + o, user_data, l); + kunmap_atomic(vaddr, KM_USER0); - written = vfs_write(obj->filp, - (char __user *)(uintptr_t) args->data_ptr, - args->size, &offset); + if (unwritten) +#endif + { + vaddr = ioremap(pfn << PAGE_SHIFT, PAGE_SIZE); +#if WATCH_PWRITE + DRM_INFO("pwrite slow i %d o %d l %d pfn %ld vaddr %p\n", + i, o, l, pfn, vaddr); +#endif + if (vaddr == NULL) { + ret = -EFAULT; + goto fail; + } + unwritten = __copy_from_user(vaddr + o, user_data, l); +#if WATCH_PWRITE + DRM_INFO("unwritten %ld\n", unwritten); +#endif + iounmap(vaddr); + if (unwritten) { + ret = -EFAULT; + goto fail; + } + } - if (written != args->size) { - drm_gem_object_unreference(obj); - mutex_unlock(&dev->struct_mutex); - if (written < 0) - return written; - else - return -EINVAL; + remain -= l; + user_data += l; + offset += l; } +#if WATCH_PWRITE && 1 + i915_gem_clflush_object(obj); + i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0); + i915_gem_clflush_object(obj); +#endif +fail: + i915_gem_object_unpin (obj); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); - return 0; +#if WATCH_PWRITE + if (ret) + DRM_INFO("pwrite failed %d\n", ret); +#endif + return ret; } /** @@ -361,7 +450,7 @@ i915_verify_inactive(struct drm_device *dev, char *file, int line) list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { obj = obj_priv->obj; - if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~I915_GEM_DOMAIN_CPU)) + if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) DRM_ERROR("inactive %p (p %d a %d w %x) %s:%d\n", obj, obj_priv->pin_count, obj_priv->active, obj->write_domain, file, line); @@ -644,7 +733,7 @@ i915_gem_flush(struct drm_device *dev, if (flush_domains & I915_GEM_DOMAIN_CPU) drm_agp_chipset_flush(dev); - if ((invalidate_domains|flush_domains) & ~I915_GEM_DOMAIN_CPU) { + if ((invalidate_domains|flush_domains) & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { /* * read/write caches: * @@ -712,7 +801,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) /* If there are writes queued to the buffer, flush and * create a new seqno to wait for. */ - if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU)) { + if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { uint32_t write_domain = obj->write_domain; #if WATCH_BUF DRM_INFO("%s: flushing object %p from write domain %08x\n", @@ -798,11 +887,13 @@ i915_gem_object_unbind(struct drm_gem_object *obj) i915_gem_object_free_page_list(obj); - atomic_dec(&dev->gtt_count); - atomic_sub(obj->size, &dev->gtt_memory); - - drm_memrange_put_block(obj_priv->gtt_space); - obj_priv->gtt_space = NULL; + if (obj_priv->gtt_space) { + atomic_dec(&dev->gtt_count); + atomic_sub(obj->size, &dev->gtt_memory); + + drm_memrange_put_block(obj_priv->gtt_space); + obj_priv->gtt_space = NULL; + } /* Remove ourselves from the LRU list if present. */ if (!list_empty(&obj_priv->list)) @@ -811,7 +902,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return 0; } -#if WATCH_BUF | WATCH_EXEC +#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE static void i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end, uint32_t bias, uint32_t mark) @@ -1105,8 +1196,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) * wasn't in the GTT, there shouldn't be any way it could have been in * a GPU cache */ - BUG_ON(obj->read_domains & ~I915_GEM_DOMAIN_CPU); - BUG_ON(obj->write_domain & ~I915_GEM_DOMAIN_CPU); + BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); return 0; } @@ -1289,7 +1380,7 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, * flushed before the cpu cache is invalidated */ if ((invalidate_domains & I915_GEM_DOMAIN_CPU) && - (flush_domains & ~I915_GEM_DOMAIN_CPU)) { + (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))) { ret = i915_gem_object_wait_rendering(obj); if (ret) return ret; @@ -1911,7 +2002,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) if (obj_priv->pin_count == 1) { atomic_inc(&dev->pin_count); atomic_add(obj->size, &dev->pin_memory); - if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0 && + if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0 && !list_empty(&obj_priv->list)) list_del_init(&obj_priv->list); } @@ -1937,7 +2028,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj) * the inactive list */ if (obj_priv->pin_count == 0) { - if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0) + if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0) list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); atomic_dec(&dev->pin_count); @@ -2096,7 +2187,7 @@ i915_gem_set_domain(struct drm_gem_object *obj, return ret; flush_domains = i915_gem_dev_set_domain(obj->dev); - if (flush_domains & ~I915_GEM_DOMAIN_CPU) + if (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) (void) i915_add_request(dev, flush_domains); return 0; @@ -2154,8 +2245,9 @@ i915_gem_idle(struct drm_device *dev) /* Flush the GPU along with all non-CPU write domains */ - i915_gem_flush(dev, ~I915_GEM_DOMAIN_CPU, ~I915_GEM_DOMAIN_CPU); - seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU); + i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT), + ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); + seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)); if (seqno == 0) { mutex_unlock(&dev->struct_mutex); -- 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... --- linux-core/i915_gem.c | 23 +++++++++++++++++++++-- shared-core/i915_drv.h | 9 +++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 4dfbd2a1..16a1b07e 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -653,7 +653,7 @@ i915_gem_retire_requests(struct drm_device *dev) list); retiring_seqno = request->seqno; - if (i915_seqno_passed(seqno, retiring_seqno)) { + if (i915_seqno_passed(seqno, retiring_seqno) || dev_priv->mm.wedged) { i915_gem_retire_request(dev, request); list_del(&request->list); @@ -697,10 +697,13 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) i915_user_irq_on(dev_priv); ret = wait_event_interruptible(dev_priv->irq_queue, i915_seqno_passed(i915_get_gem_seqno(dev), - seqno)); + seqno) || dev_priv->mm.wedged); i915_user_irq_off(dev_priv); dev_priv->mm.waiting_gem_seqno = 0; } + if (dev_priv->mm.wedged) + ret = -EIO; + if (ret) DRM_ERROR("%s returns %d (awaiting %d at %d)\n", __func__, ret, seqno, i915_get_gem_seqno(dev)); @@ -1019,6 +1022,8 @@ i915_gem_evict_something(struct drm_device *dev) list); ret = i915_wait_request(dev, request->seqno); + if (ret) + break; /* if waiting caused an object to become inactive, * then loop around and wait for it. Otherwise, we @@ -1822,6 +1827,13 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); i915_verify_inactive(dev, __FILE__, __LINE__); + + if (dev_priv->mm.wedged) { + DRM_ERROR("Execbuf while wedged\n"); + mutex_unlock(&dev->struct_mutex); + return -EIO; + } + if (dev_priv->mm.suspended) { DRM_ERROR("Execbuf while VT-switched.\n"); mutex_unlock(&dev->struct_mutex); @@ -2264,6 +2276,8 @@ i915_gem_idle(struct drm_device *dev) if (last_seqno == cur_seqno) { if (stuck++ > 100) { DRM_ERROR("hardware wedged\n"); + dev_priv->mm.wedged = 1; + DRM_WAKEUP(&dev_priv->irq_queue); break; } } @@ -2378,6 +2392,11 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; int ret; + if (dev_priv->mm.wedged) { + DRM_ERROR("Renabling wedged hardware, good luck\n"); + dev_priv->mm.wedged = 0; + } + ret = i915_gem_init_ringbuffer(dev); if (ret != 0) return ret; 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 5540457fa5bf291e88efb23721b5ac71379c6a6e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 11:21:30 -0700 Subject: [intel-gem] Use I915_GEM_DOMAIN_GTT in dri_gem_bo_wait_rendering. I915_GEM_DOMAIN_CPU is very expensive to wait for -- it generally requires clflushing the frame buffer. --- libdrm/intel/intel_bufmgr_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c index b970eacf..cdc2a7ac 100644 --- a/libdrm/intel/intel_bufmgr_gem.c +++ b/libdrm/intel/intel_bufmgr_gem.c @@ -596,7 +596,7 @@ dri_gem_bo_wait_rendering(dri_bo *bo) int ret; set_domain.handle = bo_gem->gem_handle; - set_domain.read_domains = I915_GEM_DOMAIN_CPU; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; set_domain.write_domain = 0; ret = ioctl (bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); if (ret != 0) { -- cgit v1.2.3 From c0043155ad7199835d631e3daed5c641642c314e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 22:03:06 -0700 Subject: drm_compat: it's CONFIG_HIGHMEM, not CONFIG_HIMEM A mis-spelled config option (was it spelled that way in the past?) eliminated kmap_atomic_prot_pfn from core DRM. --- linux-core/drm_compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index 30834f33..6aa19c55 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -329,7 +329,7 @@ typedef _Bool bool; #endif -#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIMEM)) +#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIGHMEM)) #define DRM_KMAP_ATOMIC_PROT_PFN extern void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t protection); -- cgit v1.2.3 From 2c6feb7a5a3fe60ed3961bc133ad5d6e63b8196a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 23 Jun 2008 22:03:33 -0700 Subject: [intel-gem] Include drm_compat.h to get kmap_atomic_prot_pfn --- linux-core/i915_gem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 16a1b07e..8fd2b8a6 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -27,6 +27,7 @@ #include "drmP.h" #include "drm.h" +#include "drm_compat.h" #include "i915_drm.h" #include "i915_drv.h" -- 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(-) 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(-) 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