diff options
-rw-r--r-- | linux-core/i915_gem.c | 137 | ||||
-rw-r--r-- | shared-core/i915_dma.c | 2 | ||||
-rw-r--r-- | shared-core/i915_drv.h | 9 |
3 files changed, 111 insertions, 37 deletions
diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index e91cae30..0cacc65b 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -48,7 +48,7 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, return -EINVAL; drm_memrange_init(&dev_priv->mm.gtt_space, args->gtt_start, - args->gtt_end); + args->gtt_end - args->gtt_start); return 0; } @@ -150,6 +150,7 @@ static int i915_gem_object_wait_rendering(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; int ret; @@ -163,9 +164,19 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) #endif i915_gem_flush(dev, 0, obj->write_domain); obj->write_domain = 0; + + /* Add a reference since we're gaining a cookie. */ if (obj_priv->last_rendering_cookie == 0) drm_gem_object_reference(obj); + /* Move from whatever list we were on to the tail of execution. + */ + list_move_tail(&obj_priv->gtt_lru_entry, + &dev_priv->mm.execution_list); obj_priv->last_rendering_cookie = i915_emit_irq(dev); + BUG_ON(obj_priv->last_rendering_cookie == 0); +#if WATCH_LRU + DRM_INFO("%s: flush moves to exec list %p\n", __func__, obj); +#endif } /* If there is rendering queued on the buffer being evicted, wait for * it. @@ -178,12 +189,19 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) ret = i915_wait_irq(dev, obj_priv->last_rendering_cookie); if (ret != 0) return ret; + /* Clear it now that we know it's passed. */ obj_priv->last_rendering_cookie = 0; - - /* - * The cookie held a reference to the object, - * release that now + /* We were on the execution list since we had a cookie. + * Move to the tail of the LRU list now since we're done. + */ + list_move_tail(&obj_priv->gtt_lru_entry, + &dev_priv->mm.gtt_lru); +#if WATCH_LRU + DRM_INFO("%s: wait moves to lru list %p\n", __func__, obj); +#endif + /* The cookie held a reference to the object, release that + * now */ drm_gem_object_unreference(obj); } @@ -206,19 +224,33 @@ i915_gem_object_unbind(struct drm_gem_object *obj) if (obj_priv->gtt_space == NULL) return; - i915_gem_object_wait_rendering(obj); + /* Ignore the return value of wait_rendering. If we're here but + * a wait_rendering hasn't completed, we're in the freeing process, + * and we want the buffer to go away even if the command queue is hung. + */ + (void)i915_gem_object_wait_rendering(obj); if (obj_priv->agp_mem != NULL) { drm_unbind_agp(obj_priv->agp_mem); drm_free_agp(obj_priv->agp_mem, obj->size / PAGE_SIZE); + obj_priv->agp_mem = NULL; } i915_gem_object_free_page_list(obj); drm_memrange_put_block(obj_priv->gtt_space); obj_priv->gtt_space = NULL; - if (!list_empty(&obj_priv->gtt_lru_entry)) + + /* Remove ourselves from the LRU list if present. */ + if (!list_empty(&obj_priv->gtt_lru_entry)) { list_del_init(&obj_priv->gtt_lru_entry); + if (obj_priv->last_rendering_cookie) { + DRM_ERROR("Failed to wait on buffer when unbinding, " + "continued anyway.\n"); + obj_priv->last_rendering_cookie = 0; + drm_gem_object_unreference(obj); + } + } } #if WATCH_BUF | WATCH_EXEC @@ -273,6 +305,13 @@ i915_dump_lru(struct drm_device *dev, const char *where) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; + DRM_INFO("GTT execution list %s {\n", where); + list_for_each_entry(obj_priv, &dev_priv->mm.execution_list, + gtt_lru_entry) + { + DRM_INFO(" %p: %08x\n", obj_priv, + obj_priv->last_rendering_cookie); + } DRM_INFO("GTT LRU %s {\n", where); list_for_each_entry(obj_priv, &dev_priv->mm.gtt_lru, gtt_lru_entry) { DRM_INFO(" %p: %08x\n", obj_priv, @@ -291,11 +330,22 @@ i915_gem_evict_something(struct drm_device *dev) int ret; /* Find the LRU buffer. */ - BUG_ON(list_empty(&dev_priv->mm.gtt_lru)); - obj_priv = list_first_entry(&dev_priv->mm.gtt_lru, - struct drm_i915_gem_object, - gtt_lru_entry); + if (!list_empty(&dev_priv->mm.gtt_lru)) { + obj_priv = list_first_entry(&dev_priv->mm.gtt_lru, + struct drm_i915_gem_object, + gtt_lru_entry); + } else if (!list_empty(&dev_priv->mm.execution_list)) { + /* If there's nothing unused and ready, grab the LRU + * from the currently executing list. + */ + obj_priv = list_first_entry(&dev_priv->mm.execution_list, + struct drm_i915_gem_object, + gtt_lru_entry); + } else { + return -ENOMEM; + } obj = obj_priv->obj; + drm_gem_object_reference(obj); #if WATCH_LRU DRM_INFO("%s: evicting %p\n", __func__, obj); #endif @@ -316,6 +366,7 @@ i915_gem_evict_something(struct drm_device *dev) #if WATCH_LRU DRM_INFO("%s: evicted %p\n", __func__, obj); #endif + drm_gem_object_unreference(obj); return 0; } @@ -359,7 +410,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) #if WATCH_LRU DRM_INFO("%s: GTT full, evicting something\n", __func__); #endif - if (list_empty(&dev_priv->mm.gtt_lru)) { + if (list_empty(&dev_priv->mm.gtt_lru) && + list_empty(&dev_priv->mm.execution_list)) { DRM_ERROR("GTT full, but LRU list empty\n"); return -ENOMEM; } @@ -528,7 +580,6 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj, struct drm_i915_gem_exec_object *entry) { struct drm_device *dev = obj->dev; - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_relocation_entry reloc; struct drm_i915_gem_relocation_entry __user *relocs; struct drm_i915_gem_object *obj_priv = obj->driver_private; @@ -545,19 +596,6 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj, entry->offset = obj_priv->gtt_offset; - if (obj_priv->pin_count == 0) { - /* Move our buffer to the head of the LRU. */ - if (list_empty(&obj_priv->gtt_lru_entry)) - list_add_tail(&obj_priv->gtt_lru_entry, - &dev_priv->mm.gtt_lru); - else - list_move_tail(&obj_priv->gtt_lru_entry, - &dev_priv->mm.gtt_lru); -#if WATCH_LRU && 0 - i915_dump_lru(dev, __func__); -#endif - } - relocs = (struct drm_i915_gem_relocation_entry __user *) (uintptr_t) entry->relocs_ptr; /* Apply the relocations, using the GTT aperture to avoid cache @@ -640,8 +678,10 @@ i915_gem_reloc_and_validate_object(struct drm_gem_object *obj, /* If the relocation already has the right value in it, no * more work needs to be done. */ - if (target_obj_priv->gtt_offset == reloc.presumed_offset) + if (target_obj_priv->gtt_offset == reloc.presumed_offset) { + drm_gem_object_unreference(target_obj); continue; + } /* Now that we're going to actually write some data in, * make sure that any rendering using this buffer's contents @@ -767,6 +807,7 @@ int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_execbuffer *args = data; struct drm_i915_gem_exec_object *validate_list; struct drm_gem_object **object_list; @@ -890,7 +931,17 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, if (obj_priv->last_rendering_cookie == 0) drm_gem_object_reference(obj); obj_priv->last_rendering_cookie = cookie; + BUG_ON(obj_priv->last_rendering_cookie == 0); + /* Move our buffer to the tail of the execution list. */ + list_move_tail(&obj_priv->gtt_lru_entry, + &dev_priv->mm.execution_list); +#if WATCH_LRU + DRM_INFO("%s: move to exec list %p\n", __func__, obj); +#endif } +#if WATCH_LRU && 0 + i915_dump_lru(dev, __func__); +#endif /* Copy the new buffer offsets back to the user's validate list. */ ret = copy_to_user((struct drm_i915_relocation_entry __user *) @@ -907,14 +958,6 @@ err: drm_gem_object_unreference(object_list[i]); } -#if 1 - /* XXX kludge for now as we don't clean the exec ring yet */ - if (object_list != NULL) { - for (i = 0; i < args->buffer_count; i++) - i915_gem_object_wait_rendering(object_list[i]); - } -#endif - drm_free(object_list, sizeof(*object_list) * args->buffer_count, DRM_MEM_DRIVER); drm_free(validate_list, sizeof(*validate_list) * args->buffer_count, @@ -1010,3 +1053,27 @@ i915_gem_set_domain_ioctl(struct drm_gem_object *obj, i915_gem_dev_set_domain(obj->dev); return 0; } + +void +i915_gem_lastclose(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* 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. + */ + + while (!list_empty(&dev_priv->mm.execution_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_first_entry(&dev_priv->mm.execution_list, + struct drm_i915_gem_object, + gtt_lru_entry); + + list_del_init(&obj_priv->gtt_lru_entry); + obj_priv->last_rendering_cookie = 0; + obj_priv->obj->write_domain = 0; + drm_gem_object_unreference(obj_priv->obj); + } +} diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 0601b89c..24f361e0 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -1052,6 +1052,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) _DRM_KERNEL | _DRM_DRIVER, &dev_priv->mmio_map); INIT_LIST_HEAD(&dev_priv->mm.gtt_lru); + INIT_LIST_HEAD(&dev_priv->mm.execution_list); #ifdef __linux__ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) @@ -1093,6 +1094,7 @@ void i915_driver_lastclose(struct drm_device * dev) dev_priv->val_bufs = NULL; } #endif + i915_gem_lastclose(dev); if (drm_getsarea(dev) && dev_priv->sarea_priv) i915_do_cleanup_pageflip(dev); diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 96257ab6..413fca89 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -244,7 +244,12 @@ typedef struct drm_i915_private { struct { struct drm_memrange gtt_space; - /** LRU List of unpinned objects in the GTT. */ + /** + * List of objects currently involved in rendering from the + * ringbuffer. + */ + struct list_head execution_list; + /** LRU List of non-executing objects still in the GTT. */ struct list_head gtt_lru; } mm; } drm_i915_private_t; @@ -386,7 +391,7 @@ void i915_gem_free_object(struct drm_gem_object *obj); int i915_gem_set_domain_ioctl (struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain); - +void i915_gem_lastclose(struct drm_device *dev); #endif #ifdef __linux__ |