Re: [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work

Matthew Brost
2026-04-01T22:11:44Z

On Wed, Mar 18, 2026 at 01:14:49PM +0530, Himal Prasad Ghimiray wrote:
> Implement worker function to dequeue and process access counter
> notifications and migrate bo based vma to vram and rebind it.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/xe_access_counter.c | 139 ++++++++++++++++++++++++-
> 1 file changed, 138 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> index a2ce9dc45d05..9eb9917d8da7 100644
> --- a/drivers/gpu/drm/xe/xe_access_counter.c
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -11,7 +11,10 @@
> #include "xe_access_counter.h"
> #include "xe_access_counter_types.h"
> #include "xe_device.h"
> +#include "xe_gt_printk.h"
> +#include "xe_hw_engine.h"
> #include "xe_usm_queue.h"
> +#include "xe_vm.h"
>
> /**
> * DOC: Xe access counters
> @@ -33,9 +36,143 @@ static int xe_access_counter_entry_size(void)
> return roundup_pow_of_two(sizeof(struct xe_access_counter));
> }
>
> +static int xe_access_counter_sub_granularity_in_byte(int val)
> +{
> + return xe_access_counter_granularity_in_byte(val) / 32;
> +}
> +
> +static struct xe_vma *xe_access_counter_get_vma(struct xe_vm *vm,
> + struct xe_access_counter *ac)
> +{
> + u64 page_va;
> +
> + if (ac->consumer.granularity != XE_ACCESS_COUNTER_GRANULARITY_128K) {
> + page_va = ac->consumer.va_range_base;
> + } else {
> + page_va = ac->consumer.va_range_base +
> + (ffs(ac->consumer.sub_granularity) - 1) *
> + xe_access_counter_sub_granularity_in_byte(ac->consumer.granularity);
> + }
> +
> + return xe_vm_find_overlapping_vma(vm, page_va, SZ_4K);
> +}
> +
> +static void xe_access_counter_print(struct xe_access_counter *ac)
> +{
> + xe_gt_dbg(ac->gt, "\n\tASID: %d\n"
> + "\tVA Range Base: 0x%08x%08x\n"
> + "\tCounter Type: %d\n"
> + "\tGranularity: %d\n"
> + "\tSub-Granularity: 0x%08x\n"
> + "\tEngineClass: %d %s\n"
> + "\tEngineInstance: %d\n",
> + ac->consumer.xe3.asid,
> + upper_32_bits(ac->consumer.va_range_base),
> + lower_32_bits(ac->consumer.va_range_base),
> + ac->consumer.counter_type,
> + ac->consumer.granularity,
> + ac->consumer.sub_granularity,
> + ac->consumer.xe3.engine_class,
> + xe_hw_engine_class_to_str(ac->consumer.xe3.engine_class),
> + ac->consumer.xe3.engine_instance);
> +}
> +
> +static int xe_access_counter_service(struct xe_access_counter *ac)
> +{
> + struct xe_gt *gt = ac->gt;
> + struct xe_device *xe = gt_to_xe(gt);
> + struct xe_tile *tile = gt_to_tile(gt);
> + struct xe_validation_ctx ctx;
> + struct drm_exec exec;
> + struct dma_fence *fence;
> + struct xe_vm *vm;
> + struct xe_vma *vma;
> + int err = 0;
> +
> + if (ac->consumer.counter_type > XE_ACCESS_COUNTER_TYPE_NOTIFY)
> + return -EINVAL;
> +
> + vm = xe_device_asid_to_fault_vm(xe, ac->consumer.xe3.asid);
> + if (IS_ERR(vm))
> + return PTR_ERR(vm);
> +
> + down_write(&vm->lock);
> +
> + if (xe_vm_is_closed(vm)) {
> + err = -ENOENT;
> + goto unlock_vm;
> + }
> + /* Lookup VMA */
> + vma = xe_access_counter_get_vma(vm, ac);
> + if (!vma) {
> + err = -EINVAL;
> + goto unlock_vm;
> + }
> +
> + /* TODO: Handle svm vma's */

One last thought — this might be the point where it actually makes sense
to support userptrs that migrate to VRAM. We basically have everything
in place to do this. It may be out of scope for this series, but it
seems like a good time to implement it here and perhaps update the
prefetch uAPI to move userptrs to VRAM as well.

SVM less concerned here as those should basically always be in VRAM
unless an app is racing CPU / GPU access.

Matt

> + if (xe_vma_has_no_bo(vma))
> + goto unlock_vm;
> +
> + /* Lock VM and BOs dma-resv */
> + xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
> + drm_exec_until_all_locked(&exec) {
> + err = xe_vma_lock_and_validate(&exec, vma, tile->mem.vram, true);
> + drm_exec_retry_on_contention(&exec);
> + xe_validation_retry_on_oom(&ctx, &err);
> + if (err)
> + break;
> +
> + xe_vm_set_validation_exec(vm, &exec);
> + fence = xe_vma_rebind(vm, vma, BIT(tile->id));
> + xe_vm_set_validation_exec(vm, NULL);
> + if (IS_ERR(fence))
> + err = PTR_ERR(fence);
> + }
> +
> + if (!err && !IS_ERR(fence)) {
> + dma_fence_wait(fence, false);
> + dma_fence_put(fence);
> + }
> +
> + xe_validation_ctx_fini(&ctx);
> +
> +unlock_vm:
> + up_write(&vm->lock);
> + xe_vm_put(vm);
> +
> + return err;
> +}
> +
> static void xe_access_counter_queue_work_func(struct work_struct *w)
> {
> - /* TODO: Implement */
> + struct xe_usm_queue *ac_queue =
> + container_of(w, typeof(*ac_queue), worker);
> + struct xe_access_counter ac = {};
> + unsigned long threshold;
> +
> +#define USM_QUEUE_MAX_RUNTIME_MS 20
> + threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
> +
> + while (xe_usm_queue_pop(ac_queue, &ac, xe_access_counter_entry_size())) {
> + int err;
> +
> + if (!ac.gt) /* Access counter squashed during reset */
> + continue;
> +
> + err = xe_access_counter_service(&ac);
> + if (err) {
> + xe_access_counter_print(&ac);
> + xe_gt_dbg(ac.gt, "Access counter handling: Unsuccessful %pe\n",
> + ERR_PTR(err));
> + }
> +
> + if (time_after(jiffies, threshold) &&
> + ac_queue->tail != ac_queue->head) {
> + queue_work(gt_to_xe(ac.gt)->usm.pf_wq, w);
> + break;
> + }
> + }
> +#undef USM_QUEUE_MAX_RUNTIME_MS
> }
>
> static int xe_access_counter_queue_init(struct xe_device *xe,
> --
> 2.34.1
>