[Intel-gfx] [PATCH v3 1/5] drm/i915/gem: Implement object migration
Ruhl, Michael J
michael.j.ruhl at intel.com
Mon Jun 28 20:13:42 UTC 2021
>-----Original Message-----
>From: Thomas Hellström <thomas.hellstrom at linux.intel.com>
>Sent: Monday, June 28, 2021 3:54 PM
>To: Ruhl, Michael J <michael.j.ruhl at intel.com>; intel-
>gfx at lists.freedesktop.org; dri-devel at lists.freedesktop.org
>Cc: Auld, Matthew <matthew.auld at intel.com>; lkp <lkp at intel.com>
>Subject: Re: [PATCH v3 1/5] drm/i915/gem: Implement object migration
>
>
>On 6/28/21 9:50 PM, Ruhl, Michael J wrote:
>>> -----Original Message-----
>>> From: Thomas Hellström <thomas.hellstrom at linux.intel.com>
>>> Sent: Monday, June 28, 2021 3:03 PM
>>> To: Ruhl, Michael J <michael.j.ruhl at intel.com>; intel-
>>> gfx at lists.freedesktop.org; dri-devel at lists.freedesktop.org
>>> Cc: Auld, Matthew <matthew.auld at intel.com>; lkp <lkp at intel.com>
>>> Subject: Re: [PATCH v3 1/5] drm/i915/gem: Implement object migration
>>>
>>>
>>> On 6/28/21 8:11 PM, Ruhl, Michael J wrote:
>>>>> -----Original Message-----
>>>>> From: dri-devel <dri-devel-bounces at lists.freedesktop.org> On Behalf
>Of
>>>>> Thomas Hellström
>>>>> Sent: Monday, June 28, 2021 10:46 AM
>>>>> To: intel-gfx at lists.freedesktop.org; dri-devel at lists.freedesktop.org
>>>>> Cc: Thomas Hellström <thomas.hellstrom at linux.intel.com>; Auld,
>>> Matthew
>>>>> <matthew.auld at intel.com>; lkp <lkp at intel.com>
>>>>> Subject: [PATCH v3 1/5] drm/i915/gem: Implement object migration
>>>>>
>>>>> Introduce an interface to migrate objects between regions.
>>>>> This is primarily intended to migrate objects to LMEM for display and
>>>>> to SYSTEM for dma-buf, but might be reused in one form or another for
>>>>> performance-based migration.
>>>>>
>>>>> v2:
>>>>> - Verify that the memory region given as an id really exists.
>>>>> (Reported by Matthew Auld)
>>>>> - Call i915_gem_object_{init,release}_memory_region() when switching
>>>>> region
>>>>> to handle also switching region lists. (Reported by Matthew Auld)
>>>>> v3:
>>>>> - Fix i915_gem_object_can_migrate() to return true if object is already
>in
>>>>> the correct region, even if the object ops doesn't have a migrate()
>>>>> callback.
>>>>> - Update typo in commit message.
>>>>> - Fix kerneldoc of i915_gem_object_wait_migration().
>>>>>
>>>>> Reported-by: kernel test robot <lkp at intel.com>
>>>>> Signed-off-by: Thomas Hellström <thomas.hellstrom at linux.intel.com>
>>>>> ---
>>>>> drivers/gpu/drm/i915/gem/i915_gem_object.c | 96
>>>>> +++++++++++++++++++
>>>>> drivers/gpu/drm/i915/gem/i915_gem_object.h | 12 +++
>>>>> .../gpu/drm/i915/gem/i915_gem_object_types.h | 9 ++
>>>>> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 69 +++++++++----
>>>>> drivers/gpu/drm/i915/gem/i915_gem_wait.c | 19 ++++
>>>>> 5 files changed, 188 insertions(+), 17 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>>>> b/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>>>> index 07e8ff9a8aae..1c18be067b58 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>>>> @@ -513,6 +513,102 @@ bool i915_gem_object_has_iomem(const
>struct
>>>>> drm_i915_gem_object *obj)
>>>>> return obj->mem_flags & I915_BO_FLAG_IOMEM;
>>>>> }
>>>>>
>>>>> +/**
>>>>> + * i915_gem_object_can_migrate - Whether an object likely can be
>>> migrated
>>>>> + *
>>>>> + * @obj: The object to migrate
>>>>> + * @id: The region intended to migrate to
>>>>> + *
>>>>> + * Check whether the object backend supports migration to the
>>>>> + * given region. Note that pinning may affect the ability to migrate.
>>>>> + *
>>>>> + * Return: true if migration is possible, false otherwise.
>>>>> + */
>>>>> +bool i915_gem_object_can_migrate(struct drm_i915_gem_object
>*obj,
>>>>> + enum intel_region_id id)
>>>>> +{
>>>>> + struct drm_i915_private *i915 = to_i915(obj->base.dev);
>>>>> + unsigned int num_allowed = obj->mm.n_placements;
>>>>> + struct intel_memory_region *mr;
>>>>> + unsigned int i;
>>>>> +
>>>>> + GEM_BUG_ON(id >= INTEL_REGION_UNKNOWN);
>>>>> + GEM_BUG_ON(obj->mm.madv != I915_MADV_WILLNEED);
>>>>> +
>>>>> + mr = i915->mm.regions[id];
>>>>> + if (!mr)
>>>>> + return false;
>>>>> +
>>>>> + if (obj->mm.region == mr)
>>>>> + return true;
>>>>> +
>>>>> + if (!i915_gem_object_evictable(obj))
>>>>> + return false;
>>>>> +
>>>>> + if (!obj->ops->migrate)
>>>>> + return false;
>>>>> +
>>>>> + if (!(obj->flags & I915_BO_ALLOC_USER))
>>>>> + return true;
>>>>> +
>>>>> + if (num_allowed == 0)
>>>>> + return false;
>>>>> +
>>>>> + for (i = 0; i < num_allowed; ++i) {
>>>>> + if (mr == obj->mm.placements[i])
>>>>> + return true;
>>>>> + }
>>>> Hi Thomas,
>>>>
>>>> I am a little confused over the difference in checks between this function
>>>> and i915_gem_object_migrate().
>>>>
>>>> Why is the lack of an mr a BUG_ON in _object_migrate(), but here it just
>>>> false?
>>>>
>>>> So that means that under certain circumstances, you could not have a mr?
>>>>
>>>> If that is the case, when?
>>>>
>>>> Would that be when the I915_BO_ALLOC_USER is set?
>>>>
>>>> If so, should there be a check for "non" user vs user?
>>>>
>>>> Or maybe document this function pointing out why there are differences
>>>> and why?
>>> Yes, I'll give it some more documentation. The basic idea is that the
>>> above function also could be
>>> used to validate user supplied data, whereas there might be cases where
>>> we want to use the gem_object_migrate() function and override the
>above.
>>>
>>>
>>>>> + return false;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * i915_gem_object_migrate - Migrate an object to the desired region
>id
>>>>> + * @obj: The object to migrate.
>>>>> + * @ww: An optional struct i915_gem_ww_ctx. If NULL, the backend
>may
>>>>> + * not be successful in evicting other objects to make room for this
>object.
>>>> Is the ww for future consideration? (I don't see any use of it in the
>patch).
>>> Yes, but it will remain optional.
>>>
>>>
>>>>> + * @id: The region id to migrate to.
>>>>> + *
>>>>> + * Attempt to migrate the object to the desired memory region. The
>>>>> + * object backend must support migration and the object may not be
>>>>> + * pinned, (explicitly pinned pages or pinned vmas). The object must
>>>>> + * be locked.
>>>>> + * On successful completion, the object will have pages pointing to
>>>>> + * memory in the new region, but an async migration task may not
>have
>>>>> + * completed yet, and to accomplish that,
>>>>> i915_gem_object_wait_migration()
>>>>> + * must be called.
>>>>> + *
>>>>> + * Return: 0 on success. Negative error code on failure. In particular
>may
>>>>> + * return -ENXIO on lack of region space, -EDEADLK for deadlock
>>> avoidance
>>>>> + * if @ww is set, -EINTR or -ERESTARTSYS if signal pending, and
>>>>> + * -EBUSY if the object is pinned.
>>>>> + */
>>>>> +int i915_gem_object_migrate(struct drm_i915_gem_object *obj,
>>>>> + struct i915_gem_ww_ctx *ww,
>>>>> + enum intel_region_id id)
>>>>> +{
>>>>> + struct drm_i915_private *i915 = to_i915(obj->base.dev);
>>>>> + struct intel_memory_region *mr;
>>>>> +
>>>>> + GEM_BUG_ON(id >= INTEL_REGION_UNKNOWN);
>>>>> + GEM_BUG_ON(obj->mm.madv != I915_MADV_WILLNEED);
>>>>> + assert_object_held(obj);
>>>>> +
>>>>> + mr = i915->mm.regions[id];
>>>>> + GEM_BUG_ON(!mr);
>>>>> +
>>>>> + if (obj->mm.region == mr)
>>>>> + return 0;
>>>>> +
>>>>> + if (!i915_gem_object_evictable(obj))
>>>>> + return -EBUSY;
>>>>> +
>>>>> + if (!obj->ops->migrate)
>>>>> + return -EOPNOTSUPP;
>>>> Why aren't you using _can_migrate here?
>>> It's just in case we want to override. I'll add some more comments about
>>> this.
>>>
>>>>> + return obj->ops->migrate(obj, mr);
>>>>> +}
>>>>> +
>>>>> void i915_gem_init__objects(struct drm_i915_private *i915)
>>>>> {
>>>>> INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h
>>>>> b/drivers/gpu/drm/i915/gem/i915_gem_object.h
>>>>> index ea3224a480c4..8cbd7a5334e2 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
>>>>> @@ -17,6 +17,8 @@
>>>>> #include "i915_gem_ww.h"
>>>>> #include "i915_vma_types.h"
>>>>>
>>>>> +enum intel_region_id;
>>>>> +
>>>>> /*
>>>>> * XXX: There is a prevalence of the assumption that we fit the
>>>>> * object's page count inside a 32bit _signed_ variable. Let's document
>>>>> @@ -597,6 +599,16 @@ bool i915_gem_object_migratable(struct
>>>>> drm_i915_gem_object *obj);
>>>>>
>>>>> bool i915_gem_object_validates_to_lmem(struct
>drm_i915_gem_object
>>>>> *obj);
>>>>>
>>>>> +int i915_gem_object_migrate(struct drm_i915_gem_object *obj,
>>>>> + struct i915_gem_ww_ctx *ww,
>>>>> + enum intel_region_id id);
>>>>> +
>>>>> +bool i915_gem_object_can_migrate(struct drm_i915_gem_object
>*obj,
>>>>> + enum intel_region_id id);
>>>>> +
>>>>> +int i915_gem_object_wait_migration(struct drm_i915_gem_object
>*obj,
>>>>> + unsigned int flags);
>>>>> +
>>>>> #ifdef CONFIG_MMU_NOTIFIER
>>>>> static inline bool
>>>>> i915_gem_object_is_userptr(struct drm_i915_gem_object *obj)
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
>>>>> b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
>>>>> index 441f913c87e6..ef3de2ae9723 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
>>>>> @@ -18,6 +18,7 @@
>>>>>
>>>>> struct drm_i915_gem_object;
>>>>> struct intel_fronbuffer;
>>>>> +struct intel_memory_region;
>>>>>
>>>>> /*
>>>>> * struct i915_lut_handle tracks the fast lookups from handle to vma
>used
>>>>> @@ -77,6 +78,14 @@ struct drm_i915_gem_object_ops {
>>>>> * delayed_free - Override the default delayed free implementation
>>>>> */
>>>>> void (*delayed_free)(struct drm_i915_gem_object *obj);
>>>>> +
>>>>> + /**
>>>>> + * migrate - Migrate object to a different region either for
>>>>> + * pinning or for as long as the object lock is held.
>>>>> + */
>>>>> + int (*migrate)(struct drm_i915_gem_object *obj,
>>>>> + struct intel_memory_region *mr);
>>>>> +
>>>>> void (*release)(struct drm_i915_gem_object *obj);
>>>>>
>>>>> const struct vm_operations_struct *mmap_ops;
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>>> b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>>> index c39d982c4fa6..8f89185b6507 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>>>>> @@ -617,7 +617,8 @@ struct ttm_device_funcs *i915_ttm_driver(void)
>>>>> return &i915_ttm_bo_driver;
>>>>> }
>>>>>
>>>>> -static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
>>>>> +static int __i915_ttm_get_pages(struct drm_i915_gem_object *obj,
>>>>> + struct ttm_placement *placement)
>>>>> {
>>>>> struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
>>>>> struct ttm_operation_ctx ctx = {
>>>>> @@ -625,19 +626,12 @@ static int i915_ttm_get_pages(struct
>>>>> drm_i915_gem_object *obj)
>>>>> .no_wait_gpu = false,
>>>>> };
>>>>> struct sg_table *st;
>>>>> - struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
>>>>> - struct ttm_placement placement;
>>>>> int real_num_busy;
>>>>> int ret;
>>>>>
>>>>> - GEM_BUG_ON(obj->mm.n_placements >
>>>>> I915_TTM_MAX_PLACEMENTS);
>>>>> -
>>>>> - /* Move to the requested placement. */
>>>>> - i915_ttm_placement_from_obj(obj, &requested, busy, &placement);
>>>>> -
>>>>> /* First try only the requested placement. No eviction. */
>>>>> - real_num_busy =
>>>>> fetch_and_zero(&placement.num_busy_placement);
>>>>> - ret = ttm_bo_validate(bo, &placement, &ctx);
>>>>> + real_num_busy = fetch_and_zero(&placement-
>>>>>> num_busy_placement);
>>>>> + ret = ttm_bo_validate(bo, placement, &ctx);
>>>>> if (ret) {
>>>>> ret = i915_ttm_err_to_gem(ret);
>>>>> /*
>>>>> @@ -652,8 +646,8 @@ static int i915_ttm_get_pages(struct
>>>>> drm_i915_gem_object *obj)
>>>>> * If the initial attempt fails, allow all accepted placements,
>>>>> * evicting if necessary.
>>>>> */
>>>>> - placement.num_busy_placement = real_num_busy;
>>>>> - ret = ttm_bo_validate(bo, &placement, &ctx);
>>>>> + placement->num_busy_placement = real_num_busy;
>>>>> + ret = ttm_bo_validate(bo, placement, &ctx);
>>>>> if (ret)
>>>>> return i915_ttm_err_to_gem(ret);
>>>>> }
>>>>> @@ -668,16 +662,56 @@ static int i915_ttm_get_pages(struct
>>>>> drm_i915_gem_object *obj)
>>>>> i915_ttm_adjust_gem_after_move(obj);
>>>>> }
>>>>>
>>>>> - /* Object either has a page vector or is an iomem object */
>>>>> - st = bo->ttm ? i915_ttm_tt_get_st(bo->ttm) : obj->ttm.cached_io_st;
>>>>> - if (IS_ERR(st))
>>>>> - return PTR_ERR(st);
>>>>> + if (!obj->mm.pages) {
>>>>> + /* Object either has a page vector or is an iomem object */
>>>>> + st = bo->ttm ? i915_ttm_tt_get_st(bo->ttm) : obj-
>>>>>> ttm.cached_io_st;
>>>>> + if (IS_ERR(st))
>>>>> + return PTR_ERR(st);
>>>>>
>>>>> - __i915_gem_object_set_pages(obj, st, i915_sg_dma_sizes(st->sgl));
>>>>> + __i915_gem_object_set_pages(obj, st,
>>>>> i915_sg_dma_sizes(st->sgl));
>>>>> + }
>>>>>
>>>>> return ret;
>>>>> }
>>>>>
>>>>> +static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
>>>>> +{
>>>>> + struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
>>>>> + struct ttm_placement placement;
>>>>> +
>>>>> + GEM_BUG_ON(obj->mm.n_placements >
>>>>> I915_TTM_MAX_PLACEMENTS);
>>>>> +
>>>>> + /* Move to the requested placement. */
>>>>> + i915_ttm_placement_from_obj(obj, &requested, busy, &placement);
>>>>> +
>>>>> + return __i915_ttm_get_pages(obj, &placement);
>>>>> +}
>>>>> +
>>>>> +static int i915_ttm_migrate(struct drm_i915_gem_object *obj,
>>>>> + struct intel_memory_region *mr)
>>>>> +{
>>>>> + struct ttm_place requested;
>>>>> + struct ttm_placement placement;
>>>>> + int ret;
>>>>> +
>>>>> + i915_ttm_place_from_region(mr, &requested, obj->flags);
>>>>> + placement.num_placement = 1;
>>>>> + placement.num_busy_placement = 1;
>>>>> + placement.placement = &requested;
>>>>> + placement.busy_placement = &requested;
>>>>> +
>>>>> + ret = __i915_ttm_get_pages(obj, &placement);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + if (obj->mm.region != mr) {
>>>>> + i915_gem_object_release_memory_region(obj);
>>>>> + i915_gem_object_init_memory_region(obj, mr);
>>>>> + }
>>>> Perhaps a minor nit:
>>>>
>>>> Doing this after we have done the _get_pages() just doesn't seem right.
>>>>
>>>> I.e. we do work on the object, and then we init some portion of it.
>>>>
>>>> Do we need to do this incase the migration/placement fails? If so,
>>>> maybe a comment to that effect?
>>> This is simply switching memory region under the lock, and to also move
>>> to another memory region list. Is it the naming _release_ and _init_
>>> that is confusing?
>> Hmm, re-reading my question, I am wondering if I was unclear.
>>
>> My "real" question was, can the release/init occur before the _get_pages()?
>>
>> But looking at this some more, I can see answer is probably no.
>>
>> I was going to suggest calling _init_ _set_, but when I looked at it, it was
>> doing init things as well as setting things
>>
>> Maybe just a comment like:
>>
>> /* Complete the migration by updating the memory region info. */
>> if (object->mm...)
>
>Sure, I'll add that. Note that another thread would have to take the
>object lock anyway to inspect both the region and the pages, so it
>shouldn't be confused by the order here.
Understood.
I think I was just disoriented by the pattern. :)
With the added documentation, I think this looks good.
Reviewed-by: Michael J. Ruhl <michael.j.ruhl at intel.com>
Mike
>/Thomas
>
More information about the Intel-gfx
mailing list