From 8e41ce17b4ab72f526cc6e9acd75c3fa81a60433 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 4 Aug 2008 00:34:08 -0700 Subject: [PATCH 1/4] Expose pin/unpin/set_tiling/flink APIs --- libdrm/intel/intel_bufmgr.h | 35 +++++++++ libdrm/intel/intel_bufmgr_gem.c | 131 ++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/libdrm/intel/intel_bufmgr.h b/libdrm/intel/intel_bufmgr.h index 1cf0d518..4d335210 100644 --- a/libdrm/intel/intel_bufmgr.h +++ b/libdrm/intel/intel_bufmgr.h @@ -61,6 +61,33 @@ struct intel_bufmgr { int (*emit_reloc)(dri_bo *reloc_buf, uint32_t read_domains, uint32_t write_domain, uint32_t delta, uint32_t offset, dri_bo *target); + /** + * Pin a buffer to the aperture and fix the offset until unpinned + * + * \param buf Buffer to pin + * \param alignment Required alignment for aperture, in bytes + */ + int (*pin) (dri_bo *buf, uint32_t alignment); + /** + * Unpin a buffer from the aperture, allowing it to be removed + * + * \param buf Buffer to unpin + */ + int (*unpin) (dri_bo *buf); + /** + * Ask that the buffer be placed in tiling mode + * + * \param buf Buffer to set tiling mode for + * \param tiling_mode desired, and returned tiling mode + */ + int (*set_tiling) (dri_bo *bo, uint32_t *tiling_mode); + /** + * Create a visible name for a buffer which can be used by other apps + * + * \param buf Buffer to create a name for + * \param name Returned name + */ + int (*flink) (dri_bo *buf, uint32_t *name); }; /* intel_bufmgr_gem.c */ @@ -91,5 +118,13 @@ int intel_bo_emit_reloc(dri_bo *reloc_buf, uint32_t read_domains, uint32_t write_domain, uint32_t delta, uint32_t offset, dri_bo *target_buf); +int intel_bo_pin(dri_bo *buf, uint32_t alignment); + +int intel_bo_unpin(dri_bo *buf); + +int intel_bo_set_tiling(dri_bo *buf, uint32_t *tiling_mode); + +int intel_bo_flink(dri_bo *buf, uint32_t *name); + #endif /* INTEL_BUFMGR_GEM_H */ diff --git a/libdrm/intel/intel_bufmgr_gem.c b/libdrm/intel/intel_bufmgr_gem.c index cdc2a7ac..22f8695d 100644 --- a/libdrm/intel/intel_bufmgr_gem.c +++ b/libdrm/intel/intel_bufmgr_gem.c @@ -768,6 +768,81 @@ dri_gem_post_submit(dri_bo *batch_buf) bufmgr_gem->exec_count = 0; } +static int +dri_gem_pin(dri_bo *bo, uint32_t alignment) +{ + dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr; + dri_bo_gem *bo_gem = (dri_bo_gem *)bo; + struct drm_i915_gem_pin pin; + int ret; + + pin.handle = bo_gem->gem_handle; + pin.alignment = alignment; + + ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_PIN, &pin); + if (ret != 0) + return -errno; + + bo->offset = pin.offset; + return 0; +} + +static int +dri_gem_unpin(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_unpin unpin; + int ret; + + unpin.handle = bo_gem->gem_handle; + + ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_UNPIN, &unpin); + if (ret != 0) + return -errno; + + return 0; +} + +static int +dri_gem_set_tiling(dri_bo *bo, uint32_t *tiling_mode) +{ + dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr; + dri_bo_gem *bo_gem = (dri_bo_gem *)bo; + struct drm_i915_gem_set_tiling set_tiling; + int ret; + + set_tiling.handle = bo_gem->gem_handle; + set_tiling.tiling_mode = *tiling_mode; + + ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling); + if (ret != 0) { + *tiling_mode = I915_TILING_NONE; + return -errno; + } + + *tiling_mode = set_tiling.tiling_mode; + return 0; +} + +static int +dri_gem_flink(dri_bo *bo, uint32_t *name) +{ + dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr; + dri_bo_gem *bo_gem = (dri_bo_gem *)bo; + struct drm_gem_flink flink; + int ret; + + flink.handle = bo_gem->gem_handle; + + ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_GEM_FLINK, &flink); + if (ret != 0) + return -errno; + + *name = flink.name; + return 0; +} + /** * Enables unlimited caching of buffer objects for reuse. * @@ -832,6 +907,10 @@ intel_bufmgr_gem_init(int fd, int batch_size) bufmgr_gem->bufmgr.debug = 0; bufmgr_gem->bufmgr.check_aperture_space = dri_gem_check_aperture_space; bufmgr_gem->intel_bufmgr.emit_reloc = dri_gem_emit_reloc; + bufmgr_gem->intel_bufmgr.pin = dri_gem_pin; + bufmgr_gem->intel_bufmgr.unpin = dri_gem_unpin; + bufmgr_gem->intel_bufmgr.set_tiling = dri_gem_set_tiling; + bufmgr_gem->intel_bufmgr.flink = dri_gem_flink; /* Initialize the linked lists for BO reuse cache. */ for (i = 0; i < INTEL_GEM_BO_BUCKETS; i++) bufmgr_gem->cache_bucket[i].tail = &bufmgr_gem->cache_bucket[i].head; @@ -851,3 +930,55 @@ intel_bo_emit_reloc(dri_bo *reloc_buf, return intel_bufmgr->emit_reloc(reloc_buf, read_domains, write_domain, delta, offset, target_buf); } + +int +intel_bo_pin(dri_bo *bo, uint32_t alignment) +{ + struct intel_bufmgr *intel_bufmgr; + + intel_bufmgr = (struct intel_bufmgr *)(bo->bufmgr + 1); + + if (intel_bufmgr->pin) + return intel_bufmgr->pin(bo, alignment); + + return 0; +} + +int +intel_bo_unpin(dri_bo *bo) +{ + struct intel_bufmgr *intel_bufmgr; + + intel_bufmgr = (struct intel_bufmgr *)(bo->bufmgr + 1); + + if (intel_bufmgr->unpin) + return intel_bufmgr->unpin(bo); + + return 0; +} + +int intel_bo_set_tiling(dri_bo *bo, uint32_t *tiling_mode) +{ + struct intel_bufmgr *intel_bufmgr; + + intel_bufmgr = (struct intel_bufmgr *)(bo->bufmgr + 1); + + if (intel_bufmgr->set_tiling) + return intel_bufmgr->set_tiling (bo, tiling_mode); + + *tiling_mode = I915_TILING_NONE; + return 0; +} + +int intel_bo_flink(dri_bo *bo, uint32_t *name) +{ + struct intel_bufmgr *intel_bufmgr; + + intel_bufmgr = (struct intel_bufmgr *)(bo->bufmgr + 1); + + if (intel_bufmgr->flink) + return intel_bufmgr->flink (bo, name); + + return -ENODEV; +} + From ceb3d5e3834452f9d54f974b8066f90168467443 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 5 Aug 2008 14:44:53 -0700 Subject: [PATCH 2/4] [gem-intel] Don't clear write_domain until flush completes In i915_gem_object_wait_rendering, if the object write domain is being written by the GPU, the appropriate flushing commands are written to the device and an additional request queued to mark that flush. Finally, the function blocks on that new request. The bug was that the write_domain in the object was cleared before the function blocked. If the wait is interrupted by a signal, the flushing commands may still be pending. With the current write_domain information lost, the restarted syscall will drop right through the write_domain test as that value was lost, and so the function will not block at all. Oops. Fixed by simply moving the write_domain clear until after the wait_request succeeds. Note that the restarted system call will generate an additional flush sequence and request, but that should be 'harmless', aside from a slight performance impact. Someday we'll track flushing more accurately and clear write_domains more efficiently, but for now, this should suffice. This bug was discovered in the 2d gem development by running x11perf -copypixwin500 and noticing that the window got cleared accidentally. --- linux-core/i915_gem.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 70f11515..acded2e8 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -381,6 +381,10 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, return -EBADF; mutex_lock(&dev->struct_mutex); +#if WATCH_BUF + DRM_INFO("set_domain_ioctl %p(%d), %08x %08x\n", + obj, obj->size, args->read_domains, args->write_domain); +#endif ret = i915_gem_set_domain(obj, file_priv, args->read_domains, args->write_domain); drm_gem_object_unreference(obj); @@ -411,8 +415,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, } #if WATCH_BUF - DRM_INFO("%s: sw_finish %d (%p)\n", - __func__, args->handle, obj); + DRM_INFO("%s: sw_finish %d (%p %d)\n", + __func__, args->handle, obj, obj->size); #endif obj_priv = obj->driver_private; @@ -853,18 +857,18 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; int ret; + uint32_t write_domain; /* 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|I915_GEM_DOMAIN_GTT)) { - uint32_t write_domain = obj->write_domain; + write_domain = obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT); + if (write_domain) { #if WATCH_BUF DRM_INFO("%s: flushing object %p from write domain %08x\n", __func__, obj, write_domain); #endif i915_gem_flush(dev, 0, write_domain); - obj->write_domain = 0; i915_gem_object_move_to_active(obj); obj_priv->last_rendering_seqno = i915_add_request(dev, @@ -874,6 +878,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) 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. */ @@ -885,6 +890,13 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); if (ret != 0) return ret; + if (write_domain) { +#if WATCH_BUF + DRM_INFO("%s: flushed object %p from write domain %08x\n", + __func__, obj, write_domain); +#endif + obj->write_domain = 0; + } } return 0; From dc0546c87ffc6701802d6141810c24954274e1ac Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 5 Aug 2008 16:06:40 -0700 Subject: [PATCH 3/4] [gem-intel] Retiring flush requests should clear flushed write_domains When i915_gem_retire_request has a flush which matches an object write domain, clear the write domain. This will move the object to the inactive list rather than the flushing list, avoiding trouble with objects left stuck on the flushing list. --- linux-core/i915_gem.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index acded2e8..354bd0db 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -661,6 +661,12 @@ i915_gem_retire_request(struct drm_device *dev, __func__, request->seqno, obj); #endif + /* If this request flushes the write domain, + * clear the write domain from the object now + */ + if (request->flush_domains & obj->write_domain) + obj->write_domain = 0; + if (obj->write_domain != 0) { list_move_tail(&obj_priv->list, &dev_priv->mm.flushing_list); @@ -760,7 +766,7 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno) if (dev_priv->mm.wedged) ret = -EIO; - if (ret) + if (ret && ret != -ERESTARTSYS) DRM_ERROR("%s returns %d (awaiting %d at %d)\n", __func__, ret, seqno, i915_get_gem_seqno(dev)); @@ -890,13 +896,6 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); if (ret != 0) return ret; - if (write_domain) { -#if WATCH_BUF - DRM_INFO("%s: flushed object %p from write domain %08x\n", - __func__, obj, write_domain); -#endif - obj->write_domain = 0; - } } return 0; From ac20e14d2361160cf199dc31c3fe1ffbacdf5bb7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 4 Aug 2008 23:33:03 -0700 Subject: [PATCH 4/4] Switch from shmem_getpage to read_mapping_page --- linux-core/i915_gem.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c index 354bd0db..35dc5bd7 100644 --- a/linux-core/i915_gem.c +++ b/linux-core/i915_gem.c @@ -1083,20 +1083,12 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj) inode = obj->filp->f_path.dentry->d_inode; mapping = inode->i_mapping; for (i = 0; i < page_count; i++) { - 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); + page = read_mapping_page(mapping, i, NULL); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + DRM_ERROR("read_mapping_page failed: %d\n", ret); + i915_gem_object_free_page_list(obj); + return ret; } obj_priv->page_list[i] = page; }