On 6/12/26 11:09, Huang Rui wrote:
> From: Honglei Huang <[email protected]>
>
> Complete attribute subsystem with validation, tree modification, and
> public API:
> - Attribute validation: per-type checks for preferred_loc, prefetch_loc,
> access, flags, granularity
> - Flag application: attr_apply_flag, amdgpu_svm_attr_apply
> - VMA range validation: amdgpu_svm_attr_validate_range_vma
> - VM BO overlap detection: amdgpu_svm_attr_check_vm_bo
> - Tree modification with gap/overlap handling:
> amdgpu_svm_attr_set_hole (new range in gap),
> amdgpu_svm_attr_set_existing (split/update existing range)
> - amdgpu_svm_attr_set_range: cursor based iteration with per segment
> - Lifecycle: amdgpu_svm_attr_tree_create/destroy
> - Public API: amdgpu_svm_attr_set (validate, set_range with retry),
> amdgpu_svm_attr_get (aggregate attrs across interval),
> amdgpu_svm_attr_clear (interval removal with split),
> amdgpu_svm_attr_reset
>
> Signed-off-by: Honglei Huang <[email protected]>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c | 738 +++++++++++++++++++
> 1 file changed, 738 insertions(+)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
> index ba09aa3c3fe4d..26e33defb8b9d 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
> @@ -232,3 +232,741 @@ static void attr_remove_range_locked(struct
> amdgpu_svm_attr_tree *attr_tree,
> if (free_range)
> kmem_cache_free(amdgpu_svm_attr_range_cache, range);
> }
> +
> +static void attr_change_ctx_set(
> + struct attr_set_ctx *change,
> + const struct amdgpu_svm_attrs *old_attrs,
> + const struct amdgpu_svm_attrs *new_attrs,
> + unsigned long start_page,
> + unsigned long last_page)
> +{
> + change->old_attrs = *old_attrs;
> + change->new_attrs = *new_attrs;
> + change->start_page = start_page;
> + change->last_page = last_page;
> +}
> +
> +static inline int attr_check_preferred_loc(uint32_t value)
> +{
> + /* cause one svm one gpu so value > 0 then means preferred loc is this
> GPU */
> + if (value == AMDGPU_SVM_LOCATION_SYSMEM || value ==
> AMDGPU_SVM_LOCATION_UNDEFINED)
> + return 0;
> +
> + return 0;
> +}
> +
> +static inline int attr_check_prefetch_loc(uint32_t value)
> +{
> + /* cause one svm one gpu so value > 0 then means prefetch loc is this
> GPU
> + * keep prefetch loc to adapt to KFD API
> + */
> + if (value == AMDGPU_SVM_LOCATION_SYSMEM)
> + return 0;
> +
> + if (value == AMDGPU_SVM_LOCATION_UNDEFINED)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static inline int attr_check_access(uint32_t value)
> +{
> + switch (value) {
> + case AMDGPU_SVM_ACCESS_INACCESSIBLE:
> + case AMDGPU_SVM_ACCESS_IN_PLACE:
> + case AMDGPU_SVM_ACCESS_ALLOW_MIGRATE:
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static bool attr_flag_type_to_bit(uint32_t type, uint32_t *flag_bit)
> +{
> + if (type < AMDGPU_SVM_ATTR_HOST_ACCESS ||
> + type > AMDGPU_SVM_ATTR_GPU_READ_MOSTLY)
> + return false;
> +
> + *flag_bit = 1u << (type - AMDGPU_SVM_ATTR_HOST_ACCESS);
> + return true;
> +}
> +
> +static inline int attr_check_flag_value(uint32_t value)
> +{
> + if (value > 1)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static inline int attr_check_flag_attr(uint32_t type, uint32_t value)
> +{
> + uint32_t flag_bit;
> + int ret;
> +
> + if (!attr_flag_type_to_bit(type, &flag_bit))
> + return -EINVAL;
> +
> + ret = attr_check_flag_value(value);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static inline int attr_check_granularity(uint32_t value)
> +{
> + return 0;
> +}
> +
> +int
> +amdgpu_svm_attr_check_vm_bo(struct amdgpu_svm_attr_tree *attr_tree,
> + unsigned long start_page,
> + unsigned long last_page,
> + unsigned long *bo_start, unsigned long *bo_last)
> +{
> + struct amdgpu_svm *svm = attr_tree->svm;
> + struct amdgpu_vm *vm = svm->vm;
> + struct interval_tree_node *node;
> + int r;
> +
> + r = amdgpu_bo_reserve(vm->root.bo, false);
> + if (r)
> + return r;
> +
> + node = interval_tree_iter_first(&vm->va, start_page, last_page);
> + if (node) {
> + AMDGPU_SVM_ERR("SVM range [0x%lx 0x%lx] overlaps with BO
> mapping [0x%lx 0x%lx]\n",
> + start_page, last_page, node->start, node->last);
Yeah that approach is a clear NAK.
We must enforce that based on the range I think.
> + if (bo_start)
> + *bo_start = node->start;
> + if (bo_last)
> + *bo_last = node->last;
> + amdgpu_bo_unreserve(vm->root.bo);
> + return -EADDRINUSE;
> + }
> +
> + amdgpu_bo_unreserve(vm->root.bo);
> + return 0;
> +}
> +
> +static int
> +amdgpu_svm_attr_validate_range_vma(struct amdgpu_svm_attr_tree *attr_tree,
> + unsigned long start_page,
> + unsigned long last_page)
> +{
> + struct vm_area_struct *vma;
> + struct mm_struct *mm;
> + unsigned long start, end;
> + int ret = 0;
> +
> + if (start_page > last_page)
> + return -EINVAL;
> +
> + if (last_page == ULONG_MAX)
> + return -EINVAL;
> +
> + start = start_page << PAGE_SHIFT;
> + end = (last_page + 1) << PAGE_SHIFT;
> + mm = attr_tree->svm->gpusvm.mm;
> + if (!mm)
> + return -EFAULT;
> +
> + mmap_read_lock(mm);
> + while (start < end) {
> + vma = amdgpu_svm_check_vma(mm, start);
> + if (IS_ERR(vma)) {
> + ret = PTR_ERR(vma);
> + break;
> + }
> +
> + start = min(end, vma->vm_end);
> + }
That's complete nonsense as well.
The result becomes invalid as soon as you drop the lock again, so just
completely drop the check.
hmm_range fault will tell us if the fault was succesful or not.
Regards,
Christian.
> + mmap_read_unlock(mm);
> +
> + return ret;
> +}
> +
> +static int attr_set_validate(const struct drm_amdgpu_svm_attribute *attr)
> +{
> + switch (attr->type) {
> + case AMDGPU_SVM_ATTR_PREFERRED_LOC:
> + return attr_check_preferred_loc(attr->value);
> + case AMDGPU_SVM_ATTR_PREFETCH_LOC:
> + return attr_check_prefetch_loc(attr->value);
> + case AMDGPU_SVM_ATTR_ACCESS:
> + return attr_check_access(attr->value);
> + case AMDGPU_SVM_ATTR_GRANULARITY:
> + return attr_check_granularity(attr->value);
> + case AMDGPU_SVM_ATTR_HOST_ACCESS:
> + case AMDGPU_SVM_ATTR_COHERENT:
> + case AMDGPU_SVM_ATTR_HIVE_LOCAL:
> + case AMDGPU_SVM_ATTR_GPU_RO:
> + case AMDGPU_SVM_ATTR_GPU_EXEC:
> + case AMDGPU_SVM_ATTR_GPU_READ_MOSTLY:
> + case AMDGPU_SVM_ATTR_EXT_COHERENT:
> + return attr_check_flag_attr(attr->type, attr->value);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static void attr_apply_flag(struct amdgpu_svm_attrs *attrs,
> + uint32_t type, uint32_t value)
> +{
> + uint32_t flag_bit;
> +
> + if (!attr_flag_type_to_bit(type, &flag_bit))
> + return;
> +
> + if (value)
> + attrs->flags |= flag_bit;
> + else
> + attrs->flags &= ~flag_bit;
> +}
> +
> +static void attr_apply(struct amdgpu_svm_attrs *attrs,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute
> *pattrs)
> +{
> + const struct drm_amdgpu_svm_attribute *attr;
> +
> + for (attr = pattrs; nattr--; attr++) {
> + switch (attr->type) {
> + case AMDGPU_SVM_ATTR_PREFERRED_LOC:
> + attrs->preferred_loc = (int32_t)attr->value;
> + break;
> + case AMDGPU_SVM_ATTR_PREFETCH_LOC:
> + attrs->prefetch_loc = (int32_t)attr->value;
> + break;
> + case AMDGPU_SVM_ATTR_ACCESS:
> + attrs->access = (enum
> amdgpu_ioctl_svm_access)attr->value;
> + break;
> + case AMDGPU_SVM_ATTR_HOST_ACCESS:
> + case AMDGPU_SVM_ATTR_COHERENT:
> + case AMDGPU_SVM_ATTR_HIVE_LOCAL:
> + case AMDGPU_SVM_ATTR_GPU_RO:
> + case AMDGPU_SVM_ATTR_GPU_EXEC:
> + case AMDGPU_SVM_ATTR_GPU_READ_MOSTLY:
> + case AMDGPU_SVM_ATTR_EXT_COHERENT:
> + attr_apply_flag(attrs, attr->type, attr->value);
> + break;
> + case AMDGPU_SVM_ATTR_GRANULARITY:
> + attrs->granularity = min_t(uint32_t, attr->value, 0x3f);
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +static bool attr_same_attrs(const struct amdgpu_svm_attr_range *range,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute *attrs)
> +{
> + struct amdgpu_svm_attrs target;
> +
> + target = range->attrs;
> + attr_apply(&target, nattr, attrs);
> + return attr_equal(&range->attrs, &target);
> +}
> +
> +static int
> +amdgpu_svm_attr_set_hole(struct amdgpu_svm_attr_tree *attr_tree,
> + const struct amdgpu_svm_attrs *default_attrs,
> + unsigned long start_page, unsigned long last_page,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute *attrs,
> + struct attr_set_ctx *change)
> +{
> + struct amdgpu_svm_attrs new_attrs;
> + struct amdgpu_svm_attr_range *range;
> +
> + lockdep_assert_held(&attr_tree->lock);
> +
> + if (start_page > last_page)
> + return 0;
> +
> + new_attrs = *default_attrs;
> + attr_apply(&new_attrs, nattr, attrs);
> +
> + /* Always create a range entry even when attrs equal defaults */
> + range = amdgpu_svm_attr_range_alloc(start_page, last_page, &new_attrs);
> + if (!range)
> + return -ENOMEM;
> +
> + amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> +
> + attr_change_ctx_set(change, default_attrs,
> + &new_attrs, start_page, last_page);
> + return 0;
> +}
> +
> +static int
> +amdgpu_svm_attr_set_existing(struct amdgpu_svm_attr_tree *attr_tree,
> + struct amdgpu_svm_attr_range *range,
> + unsigned long start_page, unsigned long last_page,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute *attrs,
> + struct attr_set_ctx *change)
> +{
> + unsigned long range_start = amdgpu_svm_attr_start_page(range);
> + unsigned long range_last = amdgpu_svm_attr_last_page(range);
> + struct amdgpu_svm_attr_range *left = NULL;
> + struct amdgpu_svm_attr_range *right = NULL;
> + struct amdgpu_svm_attrs old_attrs;
> + struct amdgpu_svm_attrs new_attrs;
> +
> + lockdep_assert_held(&attr_tree->lock);
> +
> + old_attrs = range->attrs;
> +
> + if (attr_same_attrs(range, nattr, attrs)) {
> + /* Report old==new so apply_attr_change can decide */
> + attr_change_ctx_set(change, &old_attrs,
> + &old_attrs,
> + start_page, last_page);
> + return 0;
> + }
> +
> + new_attrs = old_attrs;
> + attr_apply(&new_attrs, nattr, attrs);
> +
> + /* only need to update attr */
> + if (start_page == range_start && last_page == range_last) {
> + range->attrs = new_attrs;
> + attr_change_ctx_set(change, &old_attrs,
> + &new_attrs, start_page,
> last_page);
> + return 0;
> + }
> +
> + /* split head */
> + if (start_page > range_start) {
> + left = amdgpu_svm_attr_range_alloc(range_start, start_page - 1,
> &old_attrs);
> + if (!left)
> + return -ENOMEM;
> + }
> +
> + /* split tail */
> + if (last_page < range_last) {
> + right = amdgpu_svm_attr_range_alloc(last_page + 1, range_last,
> &old_attrs);
> + if (!right) {
> + if (left)
> + kmem_cache_free(amdgpu_svm_attr_range_cache,
> left);
> + return -ENOMEM;
> + }
> + }
> +
> + attr_remove_range_locked(attr_tree, range, false);
> + if (left)
> + amdgpu_svm_attr_range_insert_locked(attr_tree, left);
> + attr_set_interval(range, start_page, last_page);
> + range->attrs = new_attrs;
> + amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> + if (right)
> + amdgpu_svm_attr_range_insert_locked(attr_tree, right);
> +
> + attr_change_ctx_set(change, &old_attrs,
> + &new_attrs, start_page, last_page);
> + return 0;
> +}
> +
> +static int
> +amdgpu_svm_attr_set_range(struct amdgpu_svm_attr_tree *attr_tree,
> + const struct amdgpu_svm_attrs *default_attrs,
> + unsigned long start_page, unsigned long last_page,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute *attrs)
> +{
> + struct amdgpu_svm *svm = attr_tree->svm;
> + unsigned long cursor = start_page;
> + bool need_retry = false;
> +
> + while (cursor <= last_page) {
> + struct interval_tree_node *node;
> + unsigned long seg_last;
> + struct attr_set_ctx change = { 0 };
> + int ret;
> +
> + amdgpu_svm_lock(svm);
> + mutex_lock(&attr_tree->lock);
> + node = interval_tree_iter_first(&attr_tree->tree, cursor,
> cursor);
> + if (node) {
> + struct amdgpu_svm_attr_range *range;
> +
> + range = container_of(node, struct
> amdgpu_svm_attr_range, it_node);
> + seg_last = min(last_page,
> amdgpu_svm_attr_last_page(range));
> + ret = amdgpu_svm_attr_set_existing(attr_tree, range,
> + cursor,
> seg_last,
> + nattr,
> attrs, &change);
> + } else {
> + struct interval_tree_node *next;
> +
> + seg_last = last_page;
> + if (cursor != ULONG_MAX) {
> + next =
> interval_tree_iter_first(&attr_tree->tree,
> + cursor + 1,
> + ULONG_MAX);
> + if (next) {
> + struct amdgpu_svm_attr_range
> *next_range;
> +
> + next_range = container_of(next,
> + struct amdgpu_svm_attr_range,
> + it_node);
> + seg_last = min(last_page,
> +
> amdgpu_svm_attr_start_page(next_range) - 1);
> + }
> + }
> + ret = amdgpu_svm_attr_set_hole(attr_tree,
> + default_attrs,
> + cursor, seg_last,
> + nattr, attrs,
> + &change);
> + }
> + mutex_unlock(&attr_tree->lock);
> +
> + if (ret) {
> + amdgpu_svm_unlock(svm);
> + return ret;
> + }
> +
> + ret = amdgpu_svm_apply_attr_change(svm,
> + &change.old_attrs,
> + &change.new_attrs,
> + change.start_page,
> + change.last_page);
> + amdgpu_svm_unlock(svm);
> +
> + if (ret == -EAGAIN) {
> + need_retry = true;
> + ret = 0;
> + }
> +
> + if (ret)
> + return ret;
> +
> + if (seg_last == ULONG_MAX || seg_last == last_page)
> + break;
> +
> + cursor = seg_last + 1;
> + }
> +
> + return need_retry ? -EAGAIN : 0;
> +}
> +
> +struct amdgpu_svm_attr_tree *
> +amdgpu_svm_attr_tree_create(struct amdgpu_svm *svm)
> +{
> + struct amdgpu_svm_attr_tree *attr_tree;
> +
> + attr_tree = kzalloc(sizeof(*attr_tree), GFP_KERNEL);
> + if (!attr_tree)
> + return NULL;
> +
> + mutex_init(&attr_tree->lock);
> + attr_tree->tree = RB_ROOT_CACHED;
> + INIT_LIST_HEAD(&attr_tree->range_list);
> + attr_tree->svm = svm;
> + return attr_tree;
> +}
> +
> +void amdgpu_svm_attr_tree_destroy(struct amdgpu_svm_attr_tree *attr_tree)
> +{
> + struct amdgpu_svm_attr_range *range, *tmp;
> +
> + if (!attr_tree)
> + return;
> +
> + mutex_lock(&attr_tree->lock);
> + list_for_each_entry_safe(range, tmp, &attr_tree->range_list, list) {
> + interval_tree_remove(&range->it_node, &attr_tree->tree);
> + list_del_init(&range->list);
> + kmem_cache_free(amdgpu_svm_attr_range_cache, range);
> + }
> + mutex_unlock(&attr_tree->lock);
> +
> + mutex_destroy(&attr_tree->lock);
> + kfree(attr_tree);
> +}
> +
> +int amdgpu_svm_attr_set(struct amdgpu_svm_attr_tree *attr_tree,
> + uint64_t start,
> + uint64_t size,
> + uint32_t nattr,
> + const struct drm_amdgpu_svm_attribute *attrs)
> +{
> + struct amdgpu_svm *svm = attr_tree->svm;
> + struct amdgpu_svm_attrs default_attrs;
> + unsigned long start_page, last_page;
> + uint32_t i;
> + int r;
> +
> + start_page = start >> PAGE_SHIFT;
> + last_page = (start + size - 1) >> PAGE_SHIFT;
> +
> + for (i = 0; i < nattr; i++) {
> + AMDGPU_SVM_TRACE("set attr type %u value 0x%08x range
> [0x%lx-0x%lx] xnack:%d",
> + attrs[i].type, attrs[i].value, start_page, last_page,
> + svm->xnack_enabled ? 1 : 0);
> + r = attr_set_validate(&attrs[i]);
> + if (r) {
> + AMDGPU_SVM_TRACE("invalid attribute %u value 0x%08x",
> + attrs[i].type, attrs[i].value);
> + return r;
> + }
> + }
> +
> + r = amdgpu_svm_attr_validate_range_vma(attr_tree, start_page,
> last_page);
> + if (r)
> + return r;
> +
> + r = amdgpu_svm_attr_check_vm_bo(attr_tree, start_page, last_page,
> + NULL, NULL);
> + if (r)
> + return r;
> +
> + amdgpu_svm_attr_set_default(attr_tree->svm, &default_attrs);
> +
> +retry:
> + r = amdgpu_svm_attr_set_range(attr_tree, &default_attrs,
> + start_page, last_page,
> + nattr, attrs);
> + if (r == -EAGAIN) {
> + AMDGPU_SVM_TRACE("attr_set retry [0x%lx-0x%lx]\n",
> + start_page, last_page);
> + amdgpu_svm_sync_work(svm);
> + cond_resched();
> + goto retry;
> + }
> +
> + return r;
> +}
> +
> +int amdgpu_svm_attr_clear(struct amdgpu_svm_attr_tree *attr_tree,
> + unsigned long start_page,
> + unsigned long last_page)
> +{
> + struct interval_tree_node *node;
> + int r = 0;
> +
> + if (start_page > last_page)
> + return -EINVAL;
> +
> + mutex_lock(&attr_tree->lock);
> +
> + node = interval_tree_iter_first(&attr_tree->tree, start_page,
> last_page);
> + while (node) {
> + struct interval_tree_node *next;
> + struct amdgpu_svm_attr_range *range;
> + unsigned long range_start;
> + unsigned long range_last;
> +
> + range = container_of(node, struct amdgpu_svm_attr_range,
> it_node);
> + next = interval_tree_iter_next(node, start_page, last_page);
> + range_start = amdgpu_svm_attr_start_page(range);
> + range_last = amdgpu_svm_attr_last_page(range);
> +
> + if (range_start < start_page && range_last > last_page) {
> + struct amdgpu_svm_attr_range *tail;
> +
> + tail = amdgpu_svm_attr_range_alloc(last_page + 1,
> range_last,
> + &range->attrs);
> + if (!tail) {
> + r = -ENOMEM;
> + break;
> + }
> +
> + attr_remove_range_locked(attr_tree, range, false);
> + attr_set_interval(range, range_start, start_page - 1);
> + amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> + amdgpu_svm_attr_range_insert_locked(attr_tree, tail);
> + } else if (range_start < start_page) {
> + attr_remove_range_locked(attr_tree, range, false);
> + attr_set_interval(range, range_start, start_page - 1);
> + amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> + } else if (range_last > last_page) {
> + attr_remove_range_locked(attr_tree, range, false);
> + attr_set_interval(range, last_page + 1, range_last);
> + amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> + } else {
> + attr_remove_range_locked(attr_tree, range, true);
> + }
> +
> + node = next;
> + }
> +
> + mutex_unlock(&attr_tree->lock);
> + return r;
> +}
> +
> +int amdgpu_svm_attr_reset(struct amdgpu_svm_attr_tree *attr_tree,
> + unsigned long start_page,
> + unsigned long last_page)
> +{
> + /*
> + * Range with no attr node in this implementation is treated the
> + * same as one that explicitly stores default attrs. So clear
> + * the attribute ranges when user wants to reset the attrs.
> + *
> + * - GET_ATTR: return default_attrs to userspace when there is no attr
> + * range.
> + * - SET_ATTR: holes are treated as having default_attrs when
> + * computing the change trigger.
> + * - Fault: attr lookup falls back to defaults when no attr range
> exists,
> + * so migration and PTE flag decisions are unchanged.
> + *
> + * This approach simplifies the implementation and avoids redundant
> + * attribute maintenance. This ioctl operation is for attribute only,
> + * so do not invalidate the GPU mapping here.
> + */
> + return amdgpu_svm_attr_clear(attr_tree, start_page, last_page);
> +}
> +
> +static void attr_get_ctx_add(struct attr_get_ctx *ctx,
> + const struct amdgpu_svm_attrs *attrs)
> +{
> + if (!ctx->has_range) {
> + ctx->preferred_loc = attrs->preferred_loc;
> + ctx->prefetch_loc = attrs->prefetch_loc;
> + ctx->granularity = attrs->granularity;
> + ctx->access = attrs->access;
> + ctx->flags_and = attrs->flags;
> + ctx->has_range = true;
> + return;
> + }
> +
> + if (ctx->preferred_loc != attrs->preferred_loc)
> + ctx->preferred_loc = AMDGPU_SVM_LOCATION_UNDEFINED;
> + if (ctx->prefetch_loc != attrs->prefetch_loc)
> + ctx->prefetch_loc = AMDGPU_SVM_LOCATION_UNDEFINED;
> + if (attrs->granularity < ctx->granularity)
> + ctx->granularity = attrs->granularity;
> + if (ctx->access != attrs->access)
> + ctx->access = AMDGPU_SVM_ACCESS_INACCESSIBLE;
> + ctx->flags_and &= attrs->flags;
> +}
> +
> +static int attr_get_ctx_to_result(const struct attr_get_ctx *ctx,
> + uint32_t nattr,
> + struct drm_amdgpu_svm_attribute *attrs)
> +{
> + uint32_t i;
> +
> + for (i = 0; i < nattr; i++) {
> + switch (attrs[i].type) {
> + case AMDGPU_SVM_ATTR_PREFERRED_LOC:
> + attrs[i].value = ctx->preferred_loc;
> + break;
> + case AMDGPU_SVM_ATTR_PREFETCH_LOC:
> + attrs[i].value = ctx->prefetch_loc;
> + break;
> + case AMDGPU_SVM_ATTR_ACCESS:
> + attrs[i].value = (uint32_t)ctx->access;
> + break;
> + case AMDGPU_SVM_ATTR_HOST_ACCESS:
> + case AMDGPU_SVM_ATTR_COHERENT:
> + case AMDGPU_SVM_ATTR_HIVE_LOCAL:
> + case AMDGPU_SVM_ATTR_GPU_RO:
> + case AMDGPU_SVM_ATTR_GPU_EXEC:
> + case AMDGPU_SVM_ATTR_GPU_READ_MOSTLY:
> + case AMDGPU_SVM_ATTR_EXT_COHERENT: {
> + uint32_t flag_bit;
> +
> + if (!attr_flag_type_to_bit(attrs[i].type, &flag_bit))
> + return -EINVAL;
> +
> + attrs[i].value = (ctx->flags_and & flag_bit) ? 1 : 0;
> + break;
> + }
> + case AMDGPU_SVM_ATTR_GRANULARITY:
> + attrs[i].value = ctx->granularity;
> + break;
> + default:
> + return -EINVAL;
> + }
> + }
> +
> + return 0;
> +}
> +
> +int amdgpu_svm_attr_get(struct amdgpu_svm_attr_tree *attr_tree,
> + uint64_t start, uint64_t size,
> + uint32_t nattr,
> + struct drm_amdgpu_svm_attribute *attrs)
> +{
> + struct amdgpu_svm_attrs default_attrs;
> + struct attr_get_ctx ctx = { 0 };
> + struct interval_tree_node *node;
> + unsigned long start_page, last_page, cursor;
> + int r;
> +
> + start_page = start >> PAGE_SHIFT;
> + last_page = (start + size - 1) >> PAGE_SHIFT;
> +
> + r = amdgpu_svm_attr_validate_range_vma(attr_tree, start_page,
> last_page);
> + if (r)
> + return r;
> +
> + r = amdgpu_svm_attr_check_vm_bo(attr_tree, start_page, last_page,
> + NULL, NULL);
> + if (r)
> + return r;
> +
> + mutex_lock(&attr_tree->lock);
> + amdgpu_svm_attr_set_default(attr_tree->svm, &default_attrs);
> + node = interval_tree_iter_first(&attr_tree->tree, start_page,
> last_page);
> +
> + if (!node) {
> + attr_get_ctx_add(&ctx, &default_attrs);
> + r = attr_get_ctx_to_result(&ctx, nattr, attrs);
> + mutex_unlock(&attr_tree->lock);
> + return r;
> + }
> +
> + cursor = start_page;
> + while (cursor <= last_page) {
> + const struct amdgpu_svm_attrs *range_attrs;
> + unsigned long range_last = last_page;
> + struct amdgpu_svm_attr_range *range = NULL;
> + unsigned long next;
> +
> + if (node) {
> + range = container_of(node, struct amdgpu_svm_attr_range,
> + it_node);
> +
> + if (amdgpu_svm_attr_last_page(range) < cursor) {
> + node = interval_tree_iter_next(node, start_page,
> + last_page);
> + continue;
> + }
> +
> + if (amdgpu_svm_attr_start_page(range) <= cursor) {
> + range_last = min(last_page,
> amdgpu_svm_attr_last_page(range));
> + node = interval_tree_iter_next(node, start_page,
> + last_page);
> + } else {
> + range_last = min(last_page,
> +
> amdgpu_svm_attr_start_page(range) - 1);
> + range = NULL;
> + }
> + }
> +
> + range_attrs = range ? &range->attrs : &default_attrs;
> + attr_get_ctx_add(&ctx, range_attrs);
> +
> + if (range_last == ULONG_MAX)
> + break;
> +
> + next = range_last + 1;
> + if (next <= cursor)
> + break;
> + cursor = next;
> + }
> +
> + if (!ctx.has_range)
> + attr_get_ctx_add(&ctx, &default_attrs);
> +
> + r = attr_get_ctx_to_result(&ctx, nattr, attrs);
> + mutex_unlock(&attr_tree->lock);
> + return r;
> +}