Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk> --- drivers/gpu/drm/i915/i915_gem.c | 184 ++++++++++++++++++++++++++++++++++++-- 1 files changed, 174 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 37a8a29..8f60bc5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -542,6 +542,146 @@ out: return ret; } +static int +i915_gem_gtt_pread_fast(struct drm_device *dev, + struct drm_i915_gem_object *obj, + struct drm_i915_gem_pread *args, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + ssize_t remain; + loff_t offset, page_base; + char __user *user_data; + int page_offset, page_length; + + user_data = (char __user *) (uintptr_t) args->data_ptr; + remain = args->size; + + offset = obj->gtt_offset + args->offset; + + while (remain > 0) { + u8 __iomem *vaddr; + unsigned long unwritten; + + /* Operation in this page + * + * page_base = page offset within aperture + * page_offset = offset within page + * page_length = bytes to copy for this page + */ + page_base = offset & PAGE_MASK; + page_offset = offset_in_page(offset); + page_length = remain; + if ((page_offset + remain) > PAGE_SIZE) + page_length = PAGE_SIZE - page_offset; + + /* If we get a fault while copying data, then (presumably) our + * source page isn't available. Return the error and we'll + * retry in the slow path. + */ + + vaddr = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + page_base); + unwritten = __copy_to_user_inatomic(user_data, + vaddr + page_offset, + page_length); + io_mapping_unmap_atomic(vaddr); + + if (unwritten) + return -EFAULT; + + remain -= page_length; + user_data += page_length; + offset += page_length; + } + + return 0; +} + +static int +i915_gem_gtt_pread_slow(struct drm_device *dev, + struct drm_i915_gem_object *obj, + struct drm_i915_gem_pread *args, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + ssize_t remain; + loff_t gtt_page_base, offset; + loff_t first_data_page, last_data_page; + int num_pages, i; + struct page **user_pages; + int gtt_page_offset, user_page_offset, user_page_index, page_length; + int ret; + uint64_t data_ptr = args->data_ptr; + + remain = args->size; + + /* Pin the user pages containing the data. We can't fault while + * holding the struct mutex, and all of the pwrite implementations + * want to hold it while dereferencing the user data. + */ + first_data_page = data_ptr / PAGE_SIZE; + last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; + num_pages = last_data_page - first_data_page + 1; + + ret = i915_gem_get_user_pages(dev, data_ptr, false, + &num_pages, &user_pages); + if (ret) + goto out; + + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + goto out; + + offset = obj->gtt_offset + args->offset; + + while (remain > 0) { + u8 __iomem *src_vaddr; + u8 *dst_vaddr; + + /* Operation in this page + * + * gtt_page_base = page offset within aperture + * gtt_page_offset = offset within page in aperture + * user_page_index = page number in get_user_pages return + * user_page_offset = offset with user_page_index page. + * page_length = bytes to copy for this page + */ + gtt_page_base = offset & PAGE_MASK; + gtt_page_offset = offset_in_page(offset); + user_page_index = data_ptr / PAGE_SIZE - first_data_page; + user_page_offset = offset_in_page(data_ptr); + + page_length = remain; + if ((gtt_page_offset + page_length) > PAGE_SIZE) + page_length = PAGE_SIZE - gtt_page_offset; + if ((user_page_offset + page_length) > PAGE_SIZE) + page_length = PAGE_SIZE - user_page_offset; + + src_vaddr = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + gtt_page_base); + dst_vaddr = kmap_atomic(user_pages[user_page_index]); + + memcpy_fromio(dst_vaddr + user_page_offset, + src_vaddr + gtt_page_offset, + page_length); + + kunmap_atomic(dst_vaddr); + io_mapping_unmap_atomic(src_vaddr); + + remain -= page_length; + offset += page_length; + data_ptr += page_length; + } + +out: + for (i = 0; i < num_pages; i++) + page_cache_release(user_pages[i]); + drm_free_large(user_pages); + + return ret; +} + /** * Reads data from the object referenced by handle. * @@ -587,17 +727,41 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, trace_i915_gem_object_pread(obj, args->offset, args->size); - ret = i915_gem_object_set_cpu_read_domain_range(obj, - args->offset, - args->size); - if (ret) - goto out; + if (obj->gtt_space && + obj->map_and_fenceable && + obj->cache_level == I915_CACHE_NONE && + (obj->base.read_domains & I915_GEM_DOMAIN_CPU) == 0) { + ret = i915_gem_object_pin(obj, 0, true); + if (ret) + goto out; + + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + goto out_unpin; - ret = -EFAULT; - if (!i915_gem_object_needs_bit17_swizzle(obj)) - ret = i915_gem_shmem_pread_fast(dev, obj, args, file); - if (ret == -EFAULT) - ret = i915_gem_shmem_pread_slow(dev, obj, args, file); + ret = i915_gem_object_put_fence(obj); + if (ret) + goto out_unpin; + + ret = i915_gem_gtt_pread_fast(dev, obj, args, file); + if (ret == -EFAULT) + ret = i915_gem_gtt_pread_slow(dev, obj, args, file); + +out_unpin: + i915_gem_object_unpin(obj); + } else { + ret = i915_gem_object_set_cpu_read_domain_range(obj, + args->offset, + args->size); + if (ret) + goto out; + + ret = -EFAULT; + if (!i915_gem_object_needs_bit17_swizzle(obj)) + ret = i915_gem_shmem_pread_fast(dev, obj, args, file); + if (ret == -EFAULT) + ret = i915_gem_shmem_pread_slow(dev, obj, args, file); + } out: drm_gem_object_unreference(&obj->base); -- 1.7.4.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx