From: Thomas Hellström thomas.hellstrom@linux.intel.com
If our exported dma-bufs are imported by another instance of our driver, that instance will typically have the imported dma-bufs locked during dma_buf_map_attachment(). But the exporter also locks the same reservation object in the map_dma_buf() callback, which leads to recursive locking.
So taking the lock inside _pin_pages_unlocked() is incorrect.
Additionally, the current pinning code path is contrary to the defined way that pinning should occur.
Remove the explicit pin/unpin from the map/umap functions and move them to the attach/detach allowing correct locking to occur, and to match the static dma-buf drm_prime pattern.
Add a live selftest to exercise both dynamic and non-dynamic exports.
v2: - Extend the selftest with a fake dynamic importer. - Provide real pin and unpin callbacks to not abuse the interface. v3: (ruhl) - Remove the dynamic export support and move the pinning into the attach/detach path. v4: (ruhl) - Put pages does not need to assert on the dma-resv v5: (jason) - Lock around dma_buf_unmap_attachment() when emulating a dynamic importer in the subtests. - Use pin_pages_unlocked
Reported-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net --- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 43 +++++-- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 118 +++++++++++++++++- 2 files changed, 147 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 616c3a2f1baf0..9a655f69a0671 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -12,6 +12,8 @@ #include "i915_gem_object.h" #include "i915_scatterlist.h"
+I915_SELFTEST_DECLARE(static bool force_different_devices;) + static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf) { return to_intel_bo(buf->priv); @@ -25,15 +27,11 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme struct scatterlist *src, *dst; int ret, i;
- ret = i915_gem_object_pin_pages_unlocked(obj); - if (ret) - goto err; - /* Copy sg so that we make an independent mapping */ st = kmalloc(sizeof(struct sg_table), GFP_KERNEL); if (st == NULL) { ret = -ENOMEM; - goto err_unpin_pages; + goto err; }
ret = sg_alloc_table(st, obj->mm.pages->nents, GFP_KERNEL); @@ -58,8 +56,6 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme sg_free_table(st); err_free: kfree(st); -err_unpin_pages: - i915_gem_object_unpin_pages(obj); err: return ERR_PTR(ret); } @@ -68,13 +64,9 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) { - struct drm_i915_gem_object *obj = dma_buf_to_obj(attachment->dmabuf); - dma_unmap_sgtable(attachment->dev, sg, dir, DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sg); kfree(sg); - - i915_gem_object_unpin_pages(obj); }
static int i915_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct dma_buf_map *map) @@ -168,7 +160,31 @@ static int i915_gem_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direct return err; }
+/** + * i915_gem_dmabuf_attach - Do any extra attach work necessary + * @dmabuf: imported dma-buf + * @attach: new attach to do work on + * + */ +static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf); + + return i915_gem_object_pin_pages_unlocked(obj); +} + +static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf); + + i915_gem_object_unpin_pages(obj); +} + static const struct dma_buf_ops i915_dmabuf_ops = { + .attach = i915_gem_dmabuf_attach, + .detach = i915_gem_dmabuf_detach, .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, @@ -204,6 +220,8 @@ static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) struct sg_table *pages; unsigned int sg_page_sizes;
+ assert_object_held(obj); + pages = dma_buf_map_attachment(obj->base.import_attach, DMA_BIDIRECTIONAL); if (IS_ERR(pages)) @@ -241,7 +259,8 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (dma_buf->ops == &i915_dmabuf_ops) { obj = dma_buf_to_obj(dma_buf); /* is it from our device? */ - if (obj->base.dev == dev) { + if (obj->base.dev == dev && + !I915_SELFTEST_ONLY(force_different_devices)) { /* * Importing dmabuf exported from out own gem increases * refcount on gem itself instead of f_count of dmabuf. diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index dd74bc09ec88d..3dc0f8b3cdab0 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -35,7 +35,7 @@ static int igt_dmabuf_export(void *arg) static int igt_dmabuf_import_self(void *arg) { struct drm_i915_private *i915 = arg; - struct drm_i915_gem_object *obj; + struct drm_i915_gem_object *obj, *import_obj; struct drm_gem_object *import; struct dma_buf *dmabuf; int err; @@ -65,14 +65,127 @@ static int igt_dmabuf_import_self(void *arg) err = -EINVAL; goto out_import; } + import_obj = to_intel_bo(import); + + i915_gem_object_lock(import_obj, NULL); + err = ____i915_gem_object_get_pages(import_obj); + i915_gem_object_unlock(import_obj); + if (err) { + pr_err("Same object dma-buf get_pages failed!\n"); + goto out_import; + }
err = 0; out_import: - i915_gem_object_put(to_intel_bo(import)); + i915_gem_object_put(import_obj); +out_dmabuf: + dma_buf_put(dmabuf); +out: + i915_gem_object_put(obj); + return err; +} + +static void igt_dmabuf_move_notify(struct dma_buf_attachment *attach) +{ + GEM_WARN_ON(1); +} + +static const struct dma_buf_attach_ops igt_dmabuf_attach_ops = { + .move_notify = igt_dmabuf_move_notify, +}; + +static int igt_dmabuf_import_same_driver(void *arg) +{ + struct drm_i915_private *i915 = arg; + struct drm_i915_gem_object *obj, *import_obj; + struct drm_gem_object *import; + struct dma_buf *dmabuf; + struct dma_buf_attachment *import_attach; + struct sg_table *st; + long timeout; + int err; + + force_different_devices = true; + obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); + if (IS_ERR(obj)) + goto out_ret; + + dmabuf = i915_gem_prime_export(&obj->base, 0); + if (IS_ERR(dmabuf)) { + pr_err("i915_gem_prime_export failed with err=%d\n", + (int)PTR_ERR(dmabuf)); + err = PTR_ERR(dmabuf); + goto out; + } + + import = i915_gem_prime_import(&i915->drm, dmabuf); + if (IS_ERR(import)) { + pr_err("i915_gem_prime_import failed with err=%d\n", + (int)PTR_ERR(import)); + err = PTR_ERR(import); + goto out_dmabuf; + } + + if (import == &obj->base) { + pr_err("i915_gem_prime_import reused gem object!\n"); + err = -EINVAL; + goto out_import; + } + + import_obj = to_intel_bo(import); + + i915_gem_object_lock(import_obj, NULL); + err = ____i915_gem_object_get_pages(import_obj); + if (err) { + pr_err("Different objects dma-buf get_pages failed!\n"); + i915_gem_object_unlock(import_obj); + goto out_import; + } + + /* + * If the exported object is not in system memory, something + * weird is going on. TODO: When p2p is supported, this is no + * longer considered weird. + */ + if (obj->mm.region != i915->mm.regions[INTEL_REGION_SMEM]) { + pr_err("Exported dma-buf is not in system memory\n"); + err = -EINVAL; + } + + i915_gem_object_unlock(import_obj); + + /* Now try a fake dynamic importer */ + import_attach = dma_buf_dynamic_attach(dmabuf, obj->base.dev->dev, + &igt_dmabuf_attach_ops, + NULL); + if (IS_ERR(import_attach)) + goto out_import; + + dma_resv_lock(dmabuf->resv, NULL); + st = dma_buf_map_attachment(import_attach, DMA_BIDIRECTIONAL); + dma_resv_unlock(dmabuf->resv); + if (IS_ERR(st)) + goto out_detach; + + timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ); + if (!timeout) { + pr_err("dmabuf wait for exclusive fence timed out.\n"); + timeout = -ETIME; + } + err = timeout > 0 ? 0 : timeout; + dma_resv_lock(dmabuf->resv, NULL); + dma_buf_unmap_attachment(import_attach, st, DMA_BIDIRECTIONAL); + dma_resv_unlock(dmabuf->resv); +out_detach: + dma_buf_detach(dmabuf, import_attach); +out_import: + i915_gem_object_put(import_obj); out_dmabuf: dma_buf_put(dmabuf); out: i915_gem_object_put(obj); +out_ret: + force_different_devices = false; return err; }
@@ -286,6 +399,7 @@ int i915_gem_dmabuf_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(igt_dmabuf_export), + SUBTEST(igt_dmabuf_import_same_driver), };
return i915_subtests(tests, i915);
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2: - Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable. v3: - Migrate also in the pin() callback. v4: - Migrate in attach v5: (jason) - Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net --- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf); + struct i915_gem_ww_ctx ww; + int err; + + for_i915_gem_ww(&ww, err, true) { + err = i915_gem_object_lock(obj, &ww); + if (err) + continue; + + if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) { + err = -EOPNOTSUPP; + continue; + } + + err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM); + if (err) + continue;
- return i915_gem_object_pin_pages_unlocked(obj); + err = i915_gem_object_wait_migration(obj, 0); + if (err) + continue; + + err = i915_gem_object_pin_pages(obj); + } + + return err; }
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true; - obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); + obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0); + if (IS_ERR(obj)) + obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); if (IS_ERR(obj)) goto out_ret;
On Mon, Jul 12, 2021 at 06:12:34PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2:
- Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable.
v3:
- Migrate also in the pin() callback.
v4:
- Migrate in attach
v5: (jason)
- Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
- struct i915_gem_ww_ctx ww;
- int err;
- for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) {
err = -EOPNOTSUPP;
continue;
}
err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM);
if (err)
continue;
- return i915_gem_object_pin_pages_unlocked(obj);
err = i915_gem_object_wait_migration(obj, 0);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
- }
- return err;
}
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true;
- obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
- obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0);
I'm wondering (and couldn't answer) whether this creates an lmem+smem buffer, since if we create an lmem-only buffer then the migration above should fail.
Which I'm also not sure we have a testcase for that testcase either ...
I tried to read some code here, but got a bit lost. Ideas? -Daniel
- if (IS_ERR(obj))
if (IS_ERR(obj)) goto out_ret;obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
-- 2.31.1
On Tue, 13 Jul 2021 at 15:44, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Jul 12, 2021 at 06:12:34PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2:
- Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable.
v3:
- Migrate also in the pin() callback.
v4:
- Migrate in attach
v5: (jason)
- Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
struct i915_gem_ww_ctx ww;
int err;
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) {
err = -EOPNOTSUPP;
continue;
}
err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM);
if (err)
continue;
return i915_gem_object_pin_pages_unlocked(obj);
err = i915_gem_object_wait_migration(obj, 0);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
}
return err;
}
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0);
I'm wondering (and couldn't answer) whether this creates an lmem+smem buffer, since if we create an lmem-only buffer then the migration above should fail.
It's lmem-only, but it's also a kernel internal object, so the migration path will still happily migrate it if asked. On the other hand if it's a userspace object then we always have to respect the placements.
I think for now the only usecase for that is in the selftests.
Which I'm also not sure we have a testcase for that testcase either ...
I tried to read some code here, but got a bit lost. Ideas? -Daniel
if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); if (IS_ERR(obj)) goto out_ret;
-- 2.31.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Tue, Jul 13, 2021 at 04:06:13PM +0100, Matthew Auld wrote:
On Tue, 13 Jul 2021 at 15:44, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Jul 12, 2021 at 06:12:34PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2:
- Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable.
v3:
- Migrate also in the pin() callback.
v4:
- Migrate in attach
v5: (jason)
- Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
struct i915_gem_ww_ctx ww;
int err;
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) {
err = -EOPNOTSUPP;
continue;
}
err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM);
if (err)
continue;
return i915_gem_object_pin_pages_unlocked(obj);
err = i915_gem_object_wait_migration(obj, 0);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
}
return err;
}
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0);
I'm wondering (and couldn't answer) whether this creates an lmem+smem buffer, since if we create an lmem-only buffer then the migration above should fail.
It's lmem-only, but it's also a kernel internal object, so the migration path will still happily migrate it if asked. On the other hand if it's a userspace object then we always have to respect the placements.
I think for now the only usecase for that is in the selftests.
Yeah I've read the kerneldoc, it's all nicely documented but feels a bit dangerous. What I proposed on irc: - i915_gem_object_migrate does the placement check, i.e. as strict as can_migrate. - A new __i915_gem_object_migrate is for selftest that do special stuff. - In the import selftest we check that lmem-only fails (because we can't pin it into smem) for a non-dynamic importer, but lmem+smem works and gets migrated. - Once we have dynamic dma-buf for p2p pci, then we'll have another selftest which checks that things work for lmem only if and only if the importer is dynamic and has set the allow_p2p flag.
We could also add the can_migrate check everywhere (including dma_buf->attach), but that feels like the less save api. -Daniel
Which I'm also not sure we have a testcase for that testcase either ...
I tried to read some code here, but got a bit lost. Ideas? -Daniel
if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); if (IS_ERR(obj)) goto out_ret;
-- 2.31.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Tue, Jul 13, 2021 at 10:23 AM Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Jul 13, 2021 at 04:06:13PM +0100, Matthew Auld wrote:
On Tue, 13 Jul 2021 at 15:44, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Jul 12, 2021 at 06:12:34PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2:
- Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable.
v3:
- Migrate also in the pin() callback.
v4:
- Migrate in attach
v5: (jason)
- Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
struct i915_gem_ww_ctx ww;
int err;
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) {
err = -EOPNOTSUPP;
continue;
}
err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM);
if (err)
continue;
return i915_gem_object_pin_pages_unlocked(obj);
err = i915_gem_object_wait_migration(obj, 0);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
}
return err;
}
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0);
I'm wondering (and couldn't answer) whether this creates an lmem+smem buffer, since if we create an lmem-only buffer then the migration above should fail.
It's lmem-only, but it's also a kernel internal object, so the migration path will still happily migrate it if asked. On the other hand if it's a userspace object then we always have to respect the placements.
I think for now the only usecase for that is in the selftests.
Yeah I've read the kerneldoc, it's all nicely documented but feels a bit dangerous. What I proposed on irc:
- i915_gem_object_migrate does the placement check, i.e. as strict as can_migrate.
- A new __i915_gem_object_migrate is for selftest that do special stuff.
I just sent out a patch which does this except we don't actually need the __ version because there are no self-tests that want to do a dangerous migrate. We could add such a helper later if we needed.
- In the import selftest we check that lmem-only fails (because we can't pin it into smem) for a non-dynamic importer, but lmem+smem works and gets migrated.
I think we maybe want multiple things here? The test we have right now is useful because, by creating an internal LMEM buffer we ensure that the migration actually happens. If we create LMEM+SMEM, then it's possible it'll start off in SMEM and the migration would be a no-op. Not sure how likely that is in reality in a self-test environment, though.
--Jason
- Once we have dynamic dma-buf for p2p pci, then we'll have another selftest which checks that things work for lmem only if and only if the importer is dynamic and has set the allow_p2p flag.
We could also add the can_migrate check everywhere (including dma_buf->attach), but that feels like the less save api. -Daniel
Which I'm also not sure we have a testcase for that testcase either ...
I tried to read some code here, but got a bit lost. Ideas? -Daniel
if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); if (IS_ERR(obj)) goto out_ret;
-- 2.31.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Wed, Jul 14, 2021 at 11:01 PM Jason Ekstrand jason@jlekstrand.net wrote:
On Tue, Jul 13, 2021 at 10:23 AM Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Jul 13, 2021 at 04:06:13PM +0100, Matthew Auld wrote:
On Tue, 13 Jul 2021 at 15:44, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Jul 12, 2021 at 06:12:34PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
Until we support p2p dma or as a complement to that, migrate data to system memory at dma-buf attach time if possible.
v2:
- Rebase on dynamic exporter. Update the igt_dmabuf_import_same_driver selftest to migrate if we are LMEM capable.
v3:
- Migrate also in the pin() callback.
v4:
- Migrate in attach
v5: (jason)
- Lock around the migration
Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Reported-by: kernel test robot lkp@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 25 ++++++++++++++++++- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 4 ++- 2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 9a655f69a0671..3163f00554476 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -170,8 +170,31 @@ static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
struct i915_gem_ww_ctx ww;
int err;
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
if (!i915_gem_object_can_migrate(obj, INTEL_REGION_SMEM)) {
err = -EOPNOTSUPP;
continue;
}
err = i915_gem_object_migrate(obj, &ww, INTEL_REGION_SMEM);
if (err)
continue;
return i915_gem_object_pin_pages_unlocked(obj);
err = i915_gem_object_wait_migration(obj, 0);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
}
return err;
}
static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf, diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index 3dc0f8b3cdab0..4f7e77b1c0152 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -106,7 +106,9 @@ static int igt_dmabuf_import_same_driver(void *arg) int err;
force_different_devices = true;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
obj = i915_gem_object_create_lmem(i915, PAGE_SIZE, 0);
I'm wondering (and couldn't answer) whether this creates an lmem+smem buffer, since if we create an lmem-only buffer then the migration above should fail.
It's lmem-only, but it's also a kernel internal object, so the migration path will still happily migrate it if asked. On the other hand if it's a userspace object then we always have to respect the placements.
I think for now the only usecase for that is in the selftests.
Yeah I've read the kerneldoc, it's all nicely documented but feels a bit dangerous. What I proposed on irc:
- i915_gem_object_migrate does the placement check, i.e. as strict as can_migrate.
- A new __i915_gem_object_migrate is for selftest that do special stuff.
I just sent out a patch which does this except we don't actually need the __ version because there are no self-tests that want to do a dangerous migrate. We could add such a helper later if we needed.
- In the import selftest we check that lmem-only fails (because we can't pin it into smem) for a non-dynamic importer, but lmem+smem works and gets migrated.
I think we maybe want multiple things here? The test we have right now is useful because, by creating an internal LMEM buffer we ensure that the migration actually happens. If we create LMEM+SMEM, then it's possible it'll start off in SMEM and the migration would be a no-op. Not sure how likely that is in reality in a self-test environment, though.
lmem+smem is supposed to allocate in lmem first (I guess we could verify this by peeking behind the curtain), so it should migrate. -Daniel
--Jason
- Once we have dynamic dma-buf for p2p pci, then we'll have another selftest which checks that things work for lmem only if and only if the importer is dynamic and has set the allow_p2p flag.
We could also add the can_migrate check everywhere (including dma_buf->attach), but that feels like the less save api. -Daniel
Which I'm also not sure we have a testcase for that testcase either ...
I tried to read some code here, but got a bit lost. Ideas? -Daniel
if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); if (IS_ERR(obj)) goto out_ret;
-- 2.31.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
On Mon, Jul 12, 2021 at 06:12:33PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
If our exported dma-bufs are imported by another instance of our driver, that instance will typically have the imported dma-bufs locked during dma_buf_map_attachment(). But the exporter also locks the same reservation object in the map_dma_buf() callback, which leads to recursive locking.
So taking the lock inside _pin_pages_unlocked() is incorrect.
Additionally, the current pinning code path is contrary to the defined way that pinning should occur.
Remove the explicit pin/unpin from the map/umap functions and move them to the attach/detach allowing correct locking to occur, and to match the static dma-buf drm_prime pattern.
Add a live selftest to exercise both dynamic and non-dynamic exports.
v2:
- Extend the selftest with a fake dynamic importer.
- Provide real pin and unpin callbacks to not abuse the interface.
v3: (ruhl)
- Remove the dynamic export support and move the pinning into the attach/detach path.
v4: (ruhl)
- Put pages does not need to assert on the dma-resv
v5: (jason)
- Lock around dma_buf_unmap_attachment() when emulating a dynamic importer in the subtests.
- Use pin_pages_unlocked
Reported-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 43 +++++-- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 118 +++++++++++++++++- 2 files changed, 147 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 616c3a2f1baf0..9a655f69a0671 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -12,6 +12,8 @@ #include "i915_gem_object.h" #include "i915_scatterlist.h"
+I915_SELFTEST_DECLARE(static bool force_different_devices;)
static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf) { return to_intel_bo(buf->priv); @@ -25,15 +27,11 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme struct scatterlist *src, *dst; int ret, i;
- ret = i915_gem_object_pin_pages_unlocked(obj);
- if (ret)
goto err;
- /* Copy sg so that we make an independent mapping */ st = kmalloc(sizeof(struct sg_table), GFP_KERNEL); if (st == NULL) { ret = -ENOMEM;
goto err_unpin_pages;
goto err;
}
ret = sg_alloc_table(st, obj->mm.pages->nents, GFP_KERNEL);
@@ -58,8 +56,6 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme sg_free_table(st); err_free: kfree(st); -err_unpin_pages:
- i915_gem_object_unpin_pages(obj);
err: return ERR_PTR(ret); } @@ -68,13 +64,9 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) {
- struct drm_i915_gem_object *obj = dma_buf_to_obj(attachment->dmabuf);
- dma_unmap_sgtable(attachment->dev, sg, dir, DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sg); kfree(sg);
- i915_gem_object_unpin_pages(obj);
}
static int i915_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct dma_buf_map *map) @@ -168,7 +160,31 @@ static int i915_gem_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direct return err; }
+/**
- i915_gem_dmabuf_attach - Do any extra attach work necessary
- @dmabuf: imported dma-buf
- @attach: new attach to do work on
- */
+static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
+{
- struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
- return i915_gem_object_pin_pages_unlocked(obj);
+}
+static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
+{
- struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
- i915_gem_object_unpin_pages(obj);
+}
static const struct dma_buf_ops i915_dmabuf_ops = {
- .attach = i915_gem_dmabuf_attach,
- .detach = i915_gem_dmabuf_detach, .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release,
@@ -204,6 +220,8 @@ static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) struct sg_table *pages; unsigned int sg_page_sizes;
- assert_object_held(obj);
- pages = dma_buf_map_attachment(obj->base.import_attach, DMA_BIDIRECTIONAL); if (IS_ERR(pages))
@@ -241,7 +259,8 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (dma_buf->ops == &i915_dmabuf_ops) { obj = dma_buf_to_obj(dma_buf); /* is it from our device? */
if (obj->base.dev == dev) {
if (obj->base.dev == dev &&
!I915_SELFTEST_ONLY(force_different_devices)) { /* * Importing dmabuf exported from out own gem increases * refcount on gem itself instead of f_count of dmabuf.
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index dd74bc09ec88d..3dc0f8b3cdab0 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -35,7 +35,7 @@ static int igt_dmabuf_export(void *arg) static int igt_dmabuf_import_self(void *arg) { struct drm_i915_private *i915 = arg;
- struct drm_i915_gem_object *obj;
- struct drm_i915_gem_object *obj, *import_obj; struct drm_gem_object *import; struct dma_buf *dmabuf; int err;
@@ -65,14 +65,127 @@ static int igt_dmabuf_import_self(void *arg) err = -EINVAL; goto out_import; }
import_obj = to_intel_bo(import);
i915_gem_object_lock(import_obj, NULL);
err = ____i915_gem_object_get_pages(import_obj);
i915_gem_object_unlock(import_obj);
if (err) {
pr_err("Same object dma-buf get_pages failed!\n");
goto out_import;
}
err = 0;
out_import:
- i915_gem_object_put(to_intel_bo(import));
- i915_gem_object_put(import_obj);
+out_dmabuf:
- dma_buf_put(dmabuf);
+out:
- i915_gem_object_put(obj);
- return err;
+}
+static void igt_dmabuf_move_notify(struct dma_buf_attachment *attach) +{
- GEM_WARN_ON(1);
+}
+static const struct dma_buf_attach_ops igt_dmabuf_attach_ops = {
- .move_notify = igt_dmabuf_move_notify,
+};
+static int igt_dmabuf_import_same_driver(void *arg) +{
- struct drm_i915_private *i915 = arg;
- struct drm_i915_gem_object *obj, *import_obj;
- struct drm_gem_object *import;
- struct dma_buf *dmabuf;
- struct dma_buf_attachment *import_attach;
- struct sg_table *st;
- long timeout;
- int err;
- force_different_devices = true;
- obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
- if (IS_ERR(obj))
goto out_ret;
- dmabuf = i915_gem_prime_export(&obj->base, 0);
- if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%d\n",
(int)PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto out;
- }
- import = i915_gem_prime_import(&i915->drm, dmabuf);
- if (IS_ERR(import)) {
pr_err("i915_gem_prime_import failed with err=%d\n",
(int)PTR_ERR(import));
err = PTR_ERR(import);
goto out_dmabuf;
- }
- if (import == &obj->base) {
pr_err("i915_gem_prime_import reused gem object!\n");
err = -EINVAL;
goto out_import;
- }
- import_obj = to_intel_bo(import);
- i915_gem_object_lock(import_obj, NULL);
- err = ____i915_gem_object_get_pages(import_obj);
- if (err) {
pr_err("Different objects dma-buf get_pages failed!\n");
i915_gem_object_unlock(import_obj);
goto out_import;
- }
- /*
* If the exported object is not in system memory, something
* weird is going on. TODO: When p2p is supported, this is no
* longer considered weird.
*/
- if (obj->mm.region != i915->mm.regions[INTEL_REGION_SMEM]) {
pr_err("Exported dma-buf is not in system memory\n");
err = -EINVAL;
- }
- i915_gem_object_unlock(import_obj);
- /* Now try a fake dynamic importer */
- import_attach = dma_buf_dynamic_attach(dmabuf, obj->base.dev->dev,
Since we don't do a fake dynamic importer please use the non-dynamic version here. I think just needs you to delete the attach_ops.
&igt_dmabuf_attach_ops,
NULL);
- if (IS_ERR(import_attach))
goto out_import;
- dma_resv_lock(dmabuf->resv, NULL);
Also non-dynamic doesn't need dma_resv_lock here (dma-buf.c does that for you if needed).
- st = dma_buf_map_attachment(import_attach, DMA_BIDIRECTIONAL);
- dma_resv_unlock(dmabuf->resv);
- if (IS_ERR(st))
goto out_detach;
- timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ);
And also not this one here.
With that:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
- if (!timeout) {
pr_err("dmabuf wait for exclusive fence timed out.\n");
timeout = -ETIME;
- }
- err = timeout > 0 ? 0 : timeout;
- dma_resv_lock(dmabuf->resv, NULL);
- dma_buf_unmap_attachment(import_attach, st, DMA_BIDIRECTIONAL);
- dma_resv_unlock(dmabuf->resv);
+out_detach:
- dma_buf_detach(dmabuf, import_attach);
+out_import:
- i915_gem_object_put(import_obj);
out_dmabuf: dma_buf_put(dmabuf); out: i915_gem_object_put(obj); +out_ret:
- force_different_devices = false; return err;
}
@@ -286,6 +399,7 @@ int i915_gem_dmabuf_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(igt_dmabuf_export),
SUBTEST(igt_dmabuf_import_same_driver),
};
return i915_subtests(tests, i915);
-- 2.31.1
On Tue, Jul 13, 2021 at 9:40 AM Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Jul 12, 2021 at 06:12:33PM -0500, Jason Ekstrand wrote:
From: Thomas Hellström thomas.hellstrom@linux.intel.com
If our exported dma-bufs are imported by another instance of our driver, that instance will typically have the imported dma-bufs locked during dma_buf_map_attachment(). But the exporter also locks the same reservation object in the map_dma_buf() callback, which leads to recursive locking.
So taking the lock inside _pin_pages_unlocked() is incorrect.
Additionally, the current pinning code path is contrary to the defined way that pinning should occur.
Remove the explicit pin/unpin from the map/umap functions and move them to the attach/detach allowing correct locking to occur, and to match the static dma-buf drm_prime pattern.
Add a live selftest to exercise both dynamic and non-dynamic exports.
v2:
- Extend the selftest with a fake dynamic importer.
- Provide real pin and unpin callbacks to not abuse the interface.
v3: (ruhl)
- Remove the dynamic export support and move the pinning into the attach/detach path.
v4: (ruhl)
- Put pages does not need to assert on the dma-resv
v5: (jason)
- Lock around dma_buf_unmap_attachment() when emulating a dynamic importer in the subtests.
- Use pin_pages_unlocked
Reported-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Thomas Hellström thomas.hellstrom@linux.intel.com Signed-off-by: Michael J. Ruhl michael.j.ruhl@intel.com Signed-off-by: Jason Ekstrand jason@jlekstrand.net Reviewed-by: Jason Ekstrand jason@jlekstrand.net
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 43 +++++-- .../drm/i915/gem/selftests/i915_gem_dmabuf.c | 118 +++++++++++++++++- 2 files changed, 147 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 616c3a2f1baf0..9a655f69a0671 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -12,6 +12,8 @@ #include "i915_gem_object.h" #include "i915_scatterlist.h"
+I915_SELFTEST_DECLARE(static bool force_different_devices;)
static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf) { return to_intel_bo(buf->priv); @@ -25,15 +27,11 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme struct scatterlist *src, *dst; int ret, i;
ret = i915_gem_object_pin_pages_unlocked(obj);
if (ret)
goto err;
/* Copy sg so that we make an independent mapping */ st = kmalloc(sizeof(struct sg_table), GFP_KERNEL); if (st == NULL) { ret = -ENOMEM;
goto err_unpin_pages;
goto err; } ret = sg_alloc_table(st, obj->mm.pages->nents, GFP_KERNEL);
@@ -58,8 +56,6 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme sg_free_table(st); err_free: kfree(st); -err_unpin_pages:
i915_gem_object_unpin_pages(obj);
err: return ERR_PTR(ret); } @@ -68,13 +64,9 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) {
struct drm_i915_gem_object *obj = dma_buf_to_obj(attachment->dmabuf);
dma_unmap_sgtable(attachment->dev, sg, dir, DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sg); kfree(sg);
i915_gem_object_unpin_pages(obj);
}
static int i915_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct dma_buf_map *map) @@ -168,7 +160,31 @@ static int i915_gem_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direct return err; }
+/**
- i915_gem_dmabuf_attach - Do any extra attach work necessary
- @dmabuf: imported dma-buf
- @attach: new attach to do work on
- */
+static int i915_gem_dmabuf_attach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
+{
struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
return i915_gem_object_pin_pages_unlocked(obj);
+}
+static void i915_gem_dmabuf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
+{
struct drm_i915_gem_object *obj = dma_buf_to_obj(dmabuf);
i915_gem_object_unpin_pages(obj);
+}
static const struct dma_buf_ops i915_dmabuf_ops = {
.attach = i915_gem_dmabuf_attach,
.detach = i915_gem_dmabuf_detach, .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release,
@@ -204,6 +220,8 @@ static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) struct sg_table *pages; unsigned int sg_page_sizes;
assert_object_held(obj);
pages = dma_buf_map_attachment(obj->base.import_attach, DMA_BIDIRECTIONAL); if (IS_ERR(pages))
@@ -241,7 +259,8 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (dma_buf->ops == &i915_dmabuf_ops) { obj = dma_buf_to_obj(dma_buf); /* is it from our device? */
if (obj->base.dev == dev) {
if (obj->base.dev == dev &&
!I915_SELFTEST_ONLY(force_different_devices)) { /* * Importing dmabuf exported from out own gem increases * refcount on gem itself instead of f_count of dmabuf.
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c index dd74bc09ec88d..3dc0f8b3cdab0 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c @@ -35,7 +35,7 @@ static int igt_dmabuf_export(void *arg) static int igt_dmabuf_import_self(void *arg) { struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct drm_i915_gem_object *obj, *import_obj; struct drm_gem_object *import; struct dma_buf *dmabuf; int err;
@@ -65,14 +65,127 @@ static int igt_dmabuf_import_self(void *arg) err = -EINVAL; goto out_import; }
import_obj = to_intel_bo(import);
i915_gem_object_lock(import_obj, NULL);
err = ____i915_gem_object_get_pages(import_obj);
i915_gem_object_unlock(import_obj);
if (err) {
pr_err("Same object dma-buf get_pages failed!\n");
goto out_import;
} err = 0;
out_import:
i915_gem_object_put(to_intel_bo(import));
i915_gem_object_put(import_obj);
+out_dmabuf:
dma_buf_put(dmabuf);
+out:
i915_gem_object_put(obj);
return err;
+}
+static void igt_dmabuf_move_notify(struct dma_buf_attachment *attach) +{
GEM_WARN_ON(1);
+}
+static const struct dma_buf_attach_ops igt_dmabuf_attach_ops = {
.move_notify = igt_dmabuf_move_notify,
+};
+static int igt_dmabuf_import_same_driver(void *arg) +{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj, *import_obj;
struct drm_gem_object *import;
struct dma_buf *dmabuf;
struct dma_buf_attachment *import_attach;
struct sg_table *st;
long timeout;
int err;
force_different_devices = true;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj))
goto out_ret;
dmabuf = i915_gem_prime_export(&obj->base, 0);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%d\n",
(int)PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto out;
}
import = i915_gem_prime_import(&i915->drm, dmabuf);
if (IS_ERR(import)) {
pr_err("i915_gem_prime_import failed with err=%d\n",
(int)PTR_ERR(import));
err = PTR_ERR(import);
goto out_dmabuf;
}
if (import == &obj->base) {
pr_err("i915_gem_prime_import reused gem object!\n");
err = -EINVAL;
goto out_import;
}
import_obj = to_intel_bo(import);
i915_gem_object_lock(import_obj, NULL);
err = ____i915_gem_object_get_pages(import_obj);
if (err) {
pr_err("Different objects dma-buf get_pages failed!\n");
i915_gem_object_unlock(import_obj);
goto out_import;
}
/*
* If the exported object is not in system memory, something
* weird is going on. TODO: When p2p is supported, this is no
* longer considered weird.
*/
if (obj->mm.region != i915->mm.regions[INTEL_REGION_SMEM]) {
pr_err("Exported dma-buf is not in system memory\n");
err = -EINVAL;
}
i915_gem_object_unlock(import_obj);
/* Now try a fake dynamic importer */
import_attach = dma_buf_dynamic_attach(dmabuf, obj->base.dev->dev,
Since we don't do a fake dynamic importer please use the non-dynamic version here. I think just needs you to delete the attach_ops.
Isn't the whole point of these tests to test the dynamic import paths?
&igt_dmabuf_attach_ops,
NULL);
if (IS_ERR(import_attach))
goto out_import;
dma_resv_lock(dmabuf->resv, NULL);
Also non-dynamic doesn't need dma_resv_lock here (dma-buf.c does that for you if needed).
st = dma_buf_map_attachment(import_attach, DMA_BIDIRECTIONAL);
dma_resv_unlock(dmabuf->resv);
if (IS_ERR(st))
goto out_detach;
timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ);
And also not this one here.
With that:
Reviewed-by: Daniel Vetter daniel.vetter@ffwll.ch
if (!timeout) {
pr_err("dmabuf wait for exclusive fence timed out.\n");
timeout = -ETIME;
}
err = timeout > 0 ? 0 : timeout;
dma_resv_lock(dmabuf->resv, NULL);
dma_buf_unmap_attachment(import_attach, st, DMA_BIDIRECTIONAL);
dma_resv_unlock(dmabuf->resv);
+out_detach:
dma_buf_detach(dmabuf, import_attach);
+out_import:
i915_gem_object_put(import_obj);
out_dmabuf: dma_buf_put(dmabuf); out: i915_gem_object_put(obj); +out_ret:
force_different_devices = false; return err;
}
@@ -286,6 +399,7 @@ int i915_gem_dmabuf_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(igt_dmabuf_export),
SUBTEST(igt_dmabuf_import_same_driver), }; return i915_subtests(tests, i915);
-- 2.31.1
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
dri-devel@lists.freedesktop.org