Commit 852e1b36 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915/gem: Pull phys pread/pwrite implementations to the backend



Move the specialised interactions with the physical GEM object from the
pread/pwrite ioctl handler into the phys backend.

Currently, if one is able to exhaust the entire aperture and then try to
pwrite into an object not backed by struct page, we accidentally invoked
the phys pwrite handler on a non-phys object; calamitous.

Fixes: c6790dc2 ("drm/i915: Wean off drm_pci_alloc/drm_pci_free")
Testcase: igt/gem_pwrite/exhaustion
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Cc: stable@vger.kernel.org
Link: https://patchwork.freedesktop.org/patch/msgid/20201105154934.16022-2-chris@chris-wilson.co.uk
parent 0049b688
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -134,6 +134,58 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
			  vaddr, dma);
}

static int
phys_pwrite(struct drm_i915_gem_object *obj,
	    const struct drm_i915_gem_pwrite *args)
{
	void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
	char __user *user_data = u64_to_user_ptr(args->data_ptr);
	int err;

	err = i915_gem_object_wait(obj,
				   I915_WAIT_INTERRUPTIBLE |
				   I915_WAIT_ALL,
				   MAX_SCHEDULE_TIMEOUT);
	if (err)
		return err;

	/*
	 * We manually control the domain here and pretend that it
	 * remains coherent i.e. in the GTT domain, like shmem_pwrite.
	 */
	i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);

	if (copy_from_user(vaddr, user_data, args->size))
		return -EFAULT;

	drm_clflush_virt_range(vaddr, args->size);
	intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt);

	i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU);
	return 0;
}

static int
phys_pread(struct drm_i915_gem_object *obj,
	   const struct drm_i915_gem_pread *args)
{
	void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
	char __user *user_data = u64_to_user_ptr(args->data_ptr);
	int err;

	err = i915_gem_object_wait(obj,
				   I915_WAIT_INTERRUPTIBLE,
				   MAX_SCHEDULE_TIMEOUT);
	if (err)
		return err;

	drm_clflush_virt_range(vaddr, args->size);
	if (copy_to_user(user_data, vaddr, args->size))
		return -EFAULT;

	return 0;
}

static void phys_release(struct drm_i915_gem_object *obj)
{
	fput(obj->base.filp);
@@ -144,6 +196,9 @@ static const struct drm_i915_gem_object_ops i915_gem_phys_ops = {
	.get_pages = i915_gem_object_get_pages_phys,
	.put_pages = i915_gem_object_put_pages_phys,

	.pread  = phys_pread,
	.pwrite = phys_pwrite,

	.release = phys_release,
};

+0 −26
Original line number Diff line number Diff line
@@ -179,30 +179,6 @@ try_again:
	return ret;
}

static int
i915_gem_phys_pwrite(struct drm_i915_gem_object *obj,
		     struct drm_i915_gem_pwrite *args,
		     struct drm_file *file)
{
	void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
	char __user *user_data = u64_to_user_ptr(args->data_ptr);

	/*
	 * We manually control the domain here and pretend that it
	 * remains coherent i.e. in the GTT domain, like shmem_pwrite.
	 */
	i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);

	if (copy_from_user(vaddr, user_data, args->size))
		return -EFAULT;

	drm_clflush_virt_range(vaddr, args->size);
	intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt);

	i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU);
	return 0;
}

static int
i915_gem_create(struct drm_file *file,
		struct intel_memory_region *mr,
@@ -872,8 +848,6 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
	if (ret == -EFAULT || ret == -ENOSPC) {
		if (i915_gem_object_has_struct_page(obj))
			ret = i915_gem_shmem_pwrite(obj, args);
		else
			ret = i915_gem_phys_pwrite(obj, args, file);
	}

	i915_gem_object_unpin_pages(obj);