[Intel-gfx] [PATCH v7 4/6] drm/i915: Use vma resources for async unbinding
Thomas Hellström
thomas.hellstrom at linux.intel.com
Tue Jan 18 09:06:26 UTC 2022
Hi,
On Mon, 2022-01-17 at 21:07 +0000, Robert Beckett wrote:
>
>
> On 10/01/2022 17:22, Thomas Hellström wrote:
> > Implement async (non-blocking) unbinding by not syncing the vma
> > before
> > calling unbind on the vma_resource.
> > Add the resulting unbind fence to the object's dma_resv from where
> > it is
> > picked up by the ttm migration code.
> > Ideally these unbind fences should be coalesced with the migration
> > blit
> > fence to avoid stalling the migration blit waiting for unbind, as
> > they
> > can certainly go on in parallel, but since we don't yet have a
> > reasonable data structure to use to coalesce fences and attach the
> > resulting fence to a timeline, we defer that for now.
> >
> > Note that with async unbinding, even while the unbind waits for the
> > preceding bind to complete before unbinding, the vma itself might
> > have been
> > destroyed in the process, clearing the vma pages. Therefore we can
> > only allow async unbinding if we have a refcounted sg-list and keep
> > a
> > refcount on that for the vma resource pages to stay intact until
> > binding occurs. If this condition is not met, a request for an
> > async
> > unbind is diverted to a sync unbind.
> >
> > v2:
> > - Use a separate kmem_cache for vma resources for now to isolate
> > their
> > memory allocation and aid debugging.
> > - Move the check for vm closed to the actual unbinding thread.
> > Regardless
> > of whether the vm is closed, we need the unbind fence to
> > properly wait
> > for capture.
> > - Clear vma_res::vm on unbind and update its documentation.
> > v4:
> > - Take cache coloring into account when searching for vma resources
> > pending unbind. (Matthew Auld)
> > v5:
> > - Fix timeout and error check in
> > i915_vma_resource_bind_dep_await().
> > - Avoid taking a reference on the object for async binding if
> > async unbind capable.
> > - Fix braces around a single-line if statement.
> > v6:
> > - Fix up the cache coloring adjustment. (Kernel test robot
> > <lkp at intel.com>)
> > - Don't allow async unbinding if the vma_res pages are not the same
> > as
> > the object pages. (Matthew Auld)
> > v7:
> > - s/unsigned long/u64/ in a number of places (Matthew Auld)
> >
> > Signed-off-by: Thomas Hellström <thomas.hellstrom at linux.intel.com>
> > Reviewed-by: Matthew Auld <matthew.auld at intel.com>
> > ---
> > drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c | 11 +-
> > drivers/gpu/drm/i915/gt/intel_ggtt.c | 2 +-
> > drivers/gpu/drm/i915/gt/intel_gtt.c | 4 +
> > drivers/gpu/drm/i915/gt/intel_gtt.h | 3 +
> > drivers/gpu/drm/i915/i915_drv.h | 1 +
> > drivers/gpu/drm/i915/i915_gem.c | 12 +-
> > drivers/gpu/drm/i915/i915_module.c | 3 +
> > drivers/gpu/drm/i915/i915_vma.c | 205 +++++++++--
> > drivers/gpu/drm/i915/i915_vma.h | 3 +-
> > drivers/gpu/drm/i915/i915_vma_resource.c | 354
> > +++++++++++++++++--
> > drivers/gpu/drm/i915/i915_vma_resource.h | 48 +++
> > 11 files changed, 579 insertions(+), 67 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
> > b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
> > index 8653855d808b..1de306c03aaf 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
> > @@ -142,7 +142,16 @@ int i915_ttm_move_notify(struct
> > ttm_buffer_object *bo)
> > struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
> > int ret;
> >
> > - ret = i915_gem_object_unbind(obj,
> > I915_GEM_OBJECT_UNBIND_ACTIVE);
> > + /*
> > + * Note: The async unbinding here will actually transform
> > the
> > + * blocking wait for unbind into a wait before finally
> > submitting
> > + * evict / migration blit and thus stall the migration
> > timeline
> > + * which may not be good for overall throughput. We should
> > make
> > + * sure we await the unbind fences *after* the migration
> > blit
> > + * instead of *before* as we currently do.
> > + */
> > + ret = i915_gem_object_unbind(obj,
> > I915_GEM_OBJECT_UNBIND_ACTIVE |
> > + I915_GEM_OBJECT_UNBIND_ASYNC);
> > if (ret)
> > return ret;
> >
> > diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c
> > b/drivers/gpu/drm/i915/gt/intel_ggtt.c
> > index e49b6250c4b7..a1b2761bc16e 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
> > +++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
> > @@ -142,7 +142,7 @@ void i915_ggtt_suspend_vm(struct
> > i915_address_space *vm)
> > continue;
> >
> > if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
> > {
> > - __i915_vma_evict(vma);
> > + __i915_vma_evict(vma, false);
> > drm_mm_remove_node(&vma->node);
> > }
> > }
> > diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.c
> > b/drivers/gpu/drm/i915/gt/intel_gtt.c
> > index a94be0306464..46be4197b93f 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_gtt.c
> > +++ b/drivers/gpu/drm/i915/gt/intel_gtt.c
> > @@ -161,6 +161,9 @@ static void __i915_vm_release(struct
> > work_struct *work)
> > struct i915_address_space *vm =
> > container_of(work, struct i915_address_space,
> > release_work);
> >
> > + /* Synchronize async unbinds. */
> > + i915_vma_resource_bind_dep_sync_all(vm);
> > +
> > vm->cleanup(vm);
> > i915_address_space_fini(vm);
> >
> > @@ -189,6 +192,7 @@ void i915_address_space_init(struct
> > i915_address_space *vm, int subclass)
> > if (!kref_read(&vm->resv_ref))
> > kref_init(&vm->resv_ref);
> >
> > + vm->pending_unbind = RB_ROOT_CACHED;
> > INIT_WORK(&vm->release_work, __i915_vm_release);
> > atomic_set(&vm->open, 1);
> >
> > diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h
> > b/drivers/gpu/drm/i915/gt/intel_gtt.h
> > index 676b839d1a34..8073438b67c8 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_gtt.h
> > +++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
> > @@ -265,6 +265,9 @@ struct i915_address_space {
> > /* Flags used when creating page-table objects for this vm
> > */
> > unsigned long lmem_pt_obj_flags;
> >
> > + /* Interval tree for pending unbind vma resources */
> > + struct rb_root_cached pending_unbind;
> > +
> > struct drm_i915_gem_object *
> > (*alloc_pt_dma)(struct i915_address_space *vm, int
> > sz);
> > struct drm_i915_gem_object *
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h
> > b/drivers/gpu/drm/i915/i915_drv.h
> > index a44e0c3298fc..6caec2eca8cd 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -1661,6 +1661,7 @@ int i915_gem_object_unbind(struct
> > drm_i915_gem_object *obj,
> > #define I915_GEM_OBJECT_UNBIND_BARRIER BIT(1)
> > #define I915_GEM_OBJECT_UNBIND_TEST BIT(2)
> > #define I915_GEM_OBJECT_UNBIND_VM_TRYLOCK BIT(3)
> > +#define I915_GEM_OBJECT_UNBIND_ASYNC BIT(4)
> >
> > void i915_gem_runtime_suspend(struct drm_i915_private *dev_priv);
> >
> > diff --git a/drivers/gpu/drm/i915/i915_gem.c
> > b/drivers/gpu/drm/i915/i915_gem.c
> > index 3729ec01b5bc..7188db42c85b 100644
> > --- a/drivers/gpu/drm/i915/i915_gem.c
> > +++ b/drivers/gpu/drm/i915/i915_gem.c
> > @@ -157,10 +157,16 @@ int i915_gem_object_unbind(struct
> > drm_i915_gem_object *obj,
> > spin_unlock(&obj->vma.lock);
> >
> > if (vma) {
> > + bool vm_trylock = !!(flags &
> > I915_GEM_OBJECT_UNBIND_VM_TRYLOCK);
> > ret = -EBUSY;
> > - if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE
> > ||
> > - !i915_vma_is_active(vma)) {
> > - if (flags &
> > I915_GEM_OBJECT_UNBIND_VM_TRYLOCK) {
> > + if (flags & I915_GEM_OBJECT_UNBIND_ASYNC) {
> > + assert_object_held(vma->obj);
> > + ret = i915_vma_unbind_async(vma,
> > vm_trylock);
> > + }
> > +
> > + if (ret == -EBUSY && (flags &
> > I915_GEM_OBJECT_UNBIND_ACTIVE ||
> > +
> > !i915_vma_is_active(vma))) {
> > + if (vm_trylock) {
> > if (mutex_trylock(&vma->vm-
> > >mutex)) {
> > ret =
> > __i915_vma_unbind(vma);
> > mutex_unlock(&vma-
> > >vm->mutex);
> > diff --git a/drivers/gpu/drm/i915/i915_module.c
> > b/drivers/gpu/drm/i915/i915_module.c
> > index f6bcd2f89257..a8f175960b34 100644
> > --- a/drivers/gpu/drm/i915/i915_module.c
> > +++ b/drivers/gpu/drm/i915/i915_module.c
> > @@ -17,6 +17,7 @@
> > #include "i915_scheduler.h"
> > #include "i915_selftest.h"
> > #include "i915_vma.h"
> > +#include "i915_vma_resource.h"
> >
> > static int i915_check_nomodeset(void)
> > {
> > @@ -64,6 +65,8 @@ static const struct {
> > .exit = i915_scheduler_module_exit },
> > { .init = i915_vma_module_init,
> > .exit = i915_vma_module_exit },
> > + { .init = i915_vma_resource_module_init,
> > + .exit = i915_vma_resource_module_exit },
> > { .init = i915_mock_selftests },
> > { .init = i915_pmu_init,
> > .exit = i915_pmu_exit },
> > diff --git a/drivers/gpu/drm/i915/i915_vma.c
> > b/drivers/gpu/drm/i915/i915_vma.c
> > index 29c770a764aa..5881b0713b1a 100644
> > --- a/drivers/gpu/drm/i915/i915_vma.c
> > +++ b/drivers/gpu/drm/i915/i915_vma.c
> > @@ -286,9 +286,10 @@ struct i915_vma_work {
> > struct dma_fence_work base;
> > struct i915_address_space *vm;
> > struct i915_vm_pt_stash stash;
> > - struct i915_vma *vma;
> > + struct i915_vma_resource *vma_res;
> > struct drm_i915_gem_object *pinned;
> > struct i915_sw_dma_fence_cb cb;
> > + struct i915_refct_sgt *rsgt;
> > enum i915_cache_level cache_level;
> > unsigned int flags;
> > };
> > @@ -296,10 +297,11 @@ struct i915_vma_work {
> > static void __vma_bind(struct dma_fence_work *work)
> > {
> > struct i915_vma_work *vw = container_of(work, typeof(*vw),
> > base);
> > - struct i915_vma *vma = vw->vma;
> > + struct i915_vma_resource *vma_res = vw->vma_res;
> > +
> > + vma_res->ops->bind_vma(vma_res->vm, &vw->stash,
> > + vma_res, vw->cache_level, vw-
> > >flags);
> >
> > - vma->ops->bind_vma(vw->vm, &vw->stash,
> > - vma->resource, vw->cache_level, vw-
> > >flags);
> > }
> >
> > static void __vma_release(struct dma_fence_work *work)
> > @@ -311,6 +313,10 @@ static void __vma_release(struct
> > dma_fence_work *work)
> >
> > i915_vm_free_pt_stash(vw->vm, &vw->stash);
> > i915_vm_put(vw->vm);
> > + if (vw->vma_res)
> > + i915_vma_resource_put(vw->vma_res);
> > + if (vw->rsgt)
> > + i915_refct_sgt_put(vw->rsgt);
> > }
> >
> > static const struct dma_fence_work_ops bind_ops = {
> > @@ -380,13 +386,11 @@ i915_vma_resource_init_from_vma(struct
> > i915_vma_resource *vma_res,
> > {
> > struct drm_i915_gem_object *obj = vma->obj;
> >
> > - i915_vma_resource_init(vma_res, vma->pages, &vma-
> > >page_sizes,
> > + i915_vma_resource_init(vma_res, vma->vm, vma->pages, &vma-
> > >page_sizes,
> > i915_gem_object_is_readonly(obj),
> > i915_gem_object_is_lmem(obj),
> > - vma->private,
> > - vma->node.start,
> > - vma->node.size,
> > - vma->size);
> > + vma->ops, vma->private, vma-
> > >node.start,
> > + vma->node.size, vma->size);
> > }
> >
> > /**
> > @@ -410,6 +414,7 @@ int i915_vma_bind(struct i915_vma *vma,
> > {
> > u32 bind_flags;
> > u32 vma_flags;
> > + int ret;
> >
> > lockdep_assert_held(&vma->vm->mutex);
> > GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> > @@ -418,12 +423,12 @@ int i915_vma_bind(struct i915_vma *vma,
> > if (GEM_DEBUG_WARN_ON(range_overflows(vma->node.start,
> > vma->node.size,
> > vma->vm->total))) {
> > - kfree(vma_res);
> > + i915_vma_resource_free(vma_res);
> > return -ENODEV;
> > }
> >
> > if (GEM_DEBUG_WARN_ON(!flags)) {
> > - kfree(vma_res);
> > + i915_vma_resource_free(vma_res);
> > return -EINVAL;
> > }
> >
> > @@ -435,12 +440,30 @@ int i915_vma_bind(struct i915_vma *vma,
> >
> > bind_flags &= ~vma_flags;
> > if (bind_flags == 0) {
> > - kfree(vma_res);
> > + i915_vma_resource_free(vma_res);
> > return 0;
> > }
> >
> > GEM_BUG_ON(!atomic_read(&vma->pages_count));
> >
> > + /* Wait for or await async unbinds touching our range */
> > + if (work && bind_flags & vma->vm->bind_async_flags)
> > + ret = i915_vma_resource_bind_dep_await(vma->vm,
> > + &work-
> > >base.chain,
> > + vma-
> > >node.start,
> > + vma-
> > >node.size,
> > + true,
> > + GFP_NOWAIT |
> > +
> > __GFP_RETRY_MAYFAIL |
> > +
> > __GFP_NOWARN);
> > + else
> > + ret = i915_vma_resource_bind_dep_sync(vma->vm, vma-
> > >node.start,
> > + vma-
> > >node.size, true);
> > + if (ret) {
> > + i915_vma_resource_free(vma_res);
> > + return ret;
> > + }
> > +
> > if (vma->resource || !vma_res) {
> > /* Rebinding with an additional I915_VMA_*_BIND */
> > GEM_WARN_ON(!vma_flags);
>
> You missed an extra `kfree(vma_res)` on the next line from here (not
> in
> patch context), which would presumably need to be
> `i915_vma_resource_free(vma_res)`
It should, thanks. I see I've missed that in an error path as well, as
you mention below.
>
> Further to that, you are freeing it where it can already be known to
> be
> null, which, while relying on known zero side effect of double free
> will
> work, looks very unintuitive.
This stems from NULL checking before kfree() is typically not done.
IIRC even checkpatch.pl warns if doing that, but with transition to
kmem_cache_free() that doesn't hold anymore so I need to fix this up.
>
> Firther to that, once this series is applied, `i915_vma_bind` only
> appears to be called from `eb_relocate_entry()` with `vma_res` always
> null, and from `i915_vma_pin_ww()` where it allocates `vma_res`.
>
> It would be much cleaner to just allocate the `vma_res` in
> `i915_vma_bind` if required. That way it will avoid the null
> asignment
> on return to `i915_vma_pin_ww` with the equally unintuitive
> `kfree(vma_res)` during the error fallthrough path at the end of
> `i915_vma_pin_ww` on a knowingly null pointer (also that would need
> to
> be a `i915_vma_resource_free` if you keep it the way it is).
We can't allocate (GFP_KERNEL) under the vm mutex, hence need to pass
it as an argument.
Thanks,
Thomas
More information about the Intel-gfx
mailing list