drm/i915: Rework struct phys attachment handling

Instead of creating a separate object type, we make changes to
the shmem type, to clear struct page backing. This will allow us to
ensure we never run into a race when we exchange obj->ops with other
function pointers.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20210323155059.628690-9-maarten.lankhorst@linux.intel.com
This commit is contained in:
Maarten Lankhorst 2021-03-23 16:49:57 +01:00 committed by Daniel Vetter
parent c471748dc7
commit a611709757
4 changed files with 88 additions and 70 deletions

View File

@ -37,7 +37,15 @@ void __i915_gem_object_release_shmem(struct drm_i915_gem_object *obj,
struct sg_table *pages, struct sg_table *pages,
bool needs_clflush); bool needs_clflush);
int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj,
const struct drm_i915_gem_pwrite *args);
int i915_gem_object_pread_phys(struct drm_i915_gem_object *obj,
const struct drm_i915_gem_pread *args);
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align); int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align);
void i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
struct sg_table *pages);
void i915_gem_flush_free_objects(struct drm_i915_private *i915); void i915_gem_flush_free_objects(struct drm_i915_private *i915);

View File

@ -76,6 +76,8 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt); intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt);
/* We're no longer struct page backed */
obj->flags &= ~I915_BO_ALLOC_STRUCT_PAGE;
__i915_gem_object_set_pages(obj, st, sg->length); __i915_gem_object_set_pages(obj, st, sg->length);
return 0; return 0;
@ -89,7 +91,7 @@ err_pci:
return -ENOMEM; return -ENOMEM;
} }
static void void
i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj, i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
struct sg_table *pages) struct sg_table *pages)
{ {
@ -134,9 +136,8 @@ i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
vaddr, dma); vaddr, dma);
} }
static int int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj,
phys_pwrite(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pwrite *args)
const struct drm_i915_gem_pwrite *args)
{ {
void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
char __user *user_data = u64_to_user_ptr(args->data_ptr); char __user *user_data = u64_to_user_ptr(args->data_ptr);
@ -165,9 +166,8 @@ phys_pwrite(struct drm_i915_gem_object *obj,
return 0; return 0;
} }
static int int i915_gem_object_pread_phys(struct drm_i915_gem_object *obj,
phys_pread(struct drm_i915_gem_object *obj, const struct drm_i915_gem_pread *args)
const struct drm_i915_gem_pread *args)
{ {
void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset; void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
char __user *user_data = u64_to_user_ptr(args->data_ptr); char __user *user_data = u64_to_user_ptr(args->data_ptr);
@ -186,63 +186,14 @@ phys_pread(struct drm_i915_gem_object *obj,
return 0; return 0;
} }
static void phys_release(struct drm_i915_gem_object *obj) static int i915_gem_object_shmem_to_phys(struct drm_i915_gem_object *obj)
{
fput(obj->base.filp);
}
static const struct drm_i915_gem_object_ops i915_gem_phys_ops = {
.name = "i915_gem_object_phys",
.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,
};
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
{ {
struct sg_table *pages; struct sg_table *pages;
int err; int err;
if (align > obj->base.size)
return -EINVAL;
if (obj->ops == &i915_gem_phys_ops)
return 0;
if (!i915_gem_object_is_shmem(obj))
return -EINVAL;
err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
if (err)
return err;
mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
if (obj->mm.madv != I915_MADV_WILLNEED) {
err = -EFAULT;
goto err_unlock;
}
if (i915_gem_object_has_tiling_quirk(obj)) {
err = -EFAULT;
goto err_unlock;
}
if (obj->mm.mapping) {
err = -EBUSY;
goto err_unlock;
}
pages = __i915_gem_object_unset_pages(obj); pages = __i915_gem_object_unset_pages(obj);
obj->ops = &i915_gem_phys_ops; err = i915_gem_object_get_pages_phys(obj);
obj->flags &= ~I915_BO_ALLOC_STRUCT_PAGE;
err = ____i915_gem_object_get_pages(obj);
if (err) if (err)
goto err_xfer; goto err_xfer;
@ -253,19 +204,64 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
i915_gem_shmem_ops.put_pages(obj, pages); i915_gem_shmem_ops.put_pages(obj, pages);
i915_gem_object_release_memory_region(obj); i915_gem_object_release_memory_region(obj);
mutex_unlock(&obj->mm.lock);
return 0; return 0;
err_xfer: err_xfer:
obj->ops = &i915_gem_shmem_ops;
obj->flags |= I915_BO_ALLOC_STRUCT_PAGE;
if (!IS_ERR_OR_NULL(pages)) { if (!IS_ERR_OR_NULL(pages)) {
unsigned int sg_page_sizes = i915_sg_page_sizes(pages->sgl); unsigned int sg_page_sizes = i915_sg_page_sizes(pages->sgl);
__i915_gem_object_set_pages(obj, pages, sg_page_sizes); __i915_gem_object_set_pages(obj, pages, sg_page_sizes);
} }
err_unlock: return err;
}
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
{
int err;
if (align > obj->base.size)
return -EINVAL;
if (!i915_gem_object_is_shmem(obj))
return -EINVAL;
if (!i915_gem_object_has_struct_page(obj))
return 0;
err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
if (err)
return err;
mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
if (unlikely(!i915_gem_object_has_struct_page(obj)))
goto out;
if (obj->mm.madv != I915_MADV_WILLNEED) {
err = -EFAULT;
goto out;
}
if (i915_gem_object_has_tiling_quirk(obj)) {
err = -EFAULT;
goto out;
}
if (obj->mm.mapping || i915_gem_object_has_pinned_pages(obj)) {
err = -EBUSY;
goto out;
}
if (unlikely(obj->mm.madv != I915_MADV_WILLNEED)) {
drm_dbg(obj->base.dev,
"Attempting to obtain a purgeable object\n");
err = -EFAULT;
goto out;
}
err = i915_gem_object_shmem_to_phys(obj);
out:
mutex_unlock(&obj->mm.lock); mutex_unlock(&obj->mm.lock);
return err; return err;
} }

View File

@ -303,6 +303,11 @@ shmem_put_pages(struct drm_i915_gem_object *obj, struct sg_table *pages)
struct pagevec pvec; struct pagevec pvec;
struct page *page; struct page *page;
if (unlikely(!i915_gem_object_has_struct_page(obj))) {
i915_gem_object_put_pages_phys(obj, pages);
return;
}
__i915_gem_object_release_shmem(obj, pages, true); __i915_gem_object_release_shmem(obj, pages, true);
i915_gem_gtt_finish_pages(obj, pages); i915_gem_gtt_finish_pages(obj, pages);
@ -343,6 +348,9 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
/* Caller already validated user args */ /* Caller already validated user args */
GEM_BUG_ON(!access_ok(user_data, arg->size)); GEM_BUG_ON(!access_ok(user_data, arg->size));
if (!i915_gem_object_has_struct_page(obj))
return i915_gem_object_pwrite_phys(obj, arg);
/* /*
* Before we instantiate/pin the backing store for our use, we * Before we instantiate/pin the backing store for our use, we
* can prepopulate the shmemfs filp efficiently using a write into * can prepopulate the shmemfs filp efficiently using a write into
@ -421,9 +429,20 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
return 0; return 0;
} }
static int
shmem_pread(struct drm_i915_gem_object *obj,
const struct drm_i915_gem_pread *arg)
{
if (!i915_gem_object_has_struct_page(obj))
return i915_gem_object_pread_phys(obj, arg);
return -ENODEV;
}
static void shmem_release(struct drm_i915_gem_object *obj) static void shmem_release(struct drm_i915_gem_object *obj)
{ {
i915_gem_object_release_memory_region(obj); if (obj->flags & I915_BO_ALLOC_STRUCT_PAGE)
i915_gem_object_release_memory_region(obj);
fput(obj->base.filp); fput(obj->base.filp);
} }
@ -438,6 +457,7 @@ const struct drm_i915_gem_object_ops i915_gem_shmem_ops = {
.writeback = shmem_writeback, .writeback = shmem_writeback,
.pwrite = shmem_pwrite, .pwrite = shmem_pwrite,
.pread = shmem_pread,
.release = shmem_release, .release = shmem_release,
}; };

View File

@ -38,12 +38,6 @@ static int mock_phys_object(void *arg)
} }
if (i915_gem_object_has_struct_page(obj)) { if (i915_gem_object_has_struct_page(obj)) {
err = -EINVAL;
pr_err("shmem has a struct page\n");
goto out_obj;
}
if (obj->ops != &i915_gem_phys_ops) {
pr_err("i915_gem_object_attach_phys did not create a phys object\n"); pr_err("i915_gem_object_attach_phys did not create a phys object\n");
err = -EINVAL; err = -EINVAL;
goto out_obj; goto out_obj;