* ✗ CI.checkpatch: warning for drm/xe: Access counter consumer layer
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
@ 2026-03-18 7:34 ` Patchwork
2026-03-18 7:35 ` ✗ CI.KUnit: failure " Patchwork
` (15 subsequent siblings)
16 siblings, 0 replies; 37+ messages in thread
From: Patchwork @ 2026-03-18 7:34 UTC (permalink / raw)
To: Himal Prasad Ghimiray; +Cc: intel-xe
== Series Details ==
Series: drm/xe: Access counter consumer layer
URL : https://patchwork.freedesktop.org/series/163429/
State : warning
== Summary ==
+ KERNEL=/kernel
+ git clone https://gitlab.freedesktop.org/drm/maintainer-tools mt
Cloning into 'mt'...
warning: redirecting to https://gitlab.freedesktop.org/drm/maintainer-tools.git/
+ git -C mt rev-list -n1 origin/master
1f57ba1afceae32108bd24770069f764d940a0e4
+ cd /kernel
+ git config --global --add safe.directory /kernel
+ git log -n1
commit 722116be97eb29b05dfc5d6e8a116200e6e6afda
Author: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
Date: Wed Mar 18 13:14:56 2026 +0530
drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting
Set XE_PPGTT_PTE_NC on PTEs for VMAs that can't benefit from access
counter migration hints, avoiding wasteful slot allocation.
Bspec: 67095
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
+ /mt/dim checkpatch 146a21986f74225d0343edeb925095825fa5474f drm-intel
a34ae79aabf3 drm/xe: Add xe_usm_queue generic USM circular buffer
-:19: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#19:
new file mode 100644
-:77: CHECK:LINE_SPACING: Please use a blank line after function/struct/union/enum declarations
#77: FILE: drivers/gpu/drm/xe/xe_usm_queue.h:54:
+}
+/**
total: 0 errors, 1 warnings, 1 checks, 100 lines checked
ef7ba0ced1e6 drm/xe/pagefault: Use xe_usm_queue helpers
96db1d53a369 drm/xe: Stub out new access_counter layer
-:27: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#27:
new file mode 100644
total: 0 errors, 1 warnings, 0 checks, 204 lines checked
67c41fd9b2e5 drm/xe: Implement xe_access_counter_init
b4fd5859c2d2 drm/xe: Implement xe_access_counter_handler
54b53d5dd1dd drm/xe: Extract xe_vma_lock_and_validate helper
ab7b7bdecbe8 drm/xe: Move ASID to FAULT VM lookup to xe_device
6ca35a793038 drm/xe: Implement xe_access_counter_queue_work
24fc8fc6bec7 drm/xe/trace: Add xe_vma_acc trace event for access counter notifications
-:74: CHECK:OPEN_ENDED_LINE: Lines should not end with a '('
#74: FILE: drivers/gpu/drm/xe/xe_trace_bo.h:133:
+ TP_STRUCT__entry(
-:84: CHECK:OPEN_ENDED_LINE: Lines should not end with a '('
#84: FILE: drivers/gpu/drm/xe/xe_trace_bo.h:143:
+ TP_fast_assign(
total: 0 errors, 0 warnings, 2 checks, 75 lines checked
2b1673bca06e drm/xe/svm: Handle svm ranges on access ctr trigger
869d75abbabb drm/xe: Add xe_guc_access_counter layer
-:25: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#25:
new file mode 100644
total: 0 errors, 1 warnings, 0 checks, 100 lines checked
dd0bdd77cda5 drm/xe/uapi: Add access counter parameter extension for exec queue
56d33bf0de01 drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params
14844dea0366 drm/xe/vm: Add xe_vma_supports_access_ctr() helper
722116be97eb drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting
^ permalink raw reply [flat|nested] 37+ messages in thread
* ✗ CI.KUnit: failure for drm/xe: Access counter consumer layer
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
2026-03-18 7:34 ` ✗ CI.checkpatch: warning for " Patchwork
@ 2026-03-18 7:35 ` Patchwork
2026-03-18 7:44 ` [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer Himal Prasad Ghimiray
` (14 subsequent siblings)
16 siblings, 0 replies; 37+ messages in thread
From: Patchwork @ 2026-03-18 7:35 UTC (permalink / raw)
To: Himal Prasad Ghimiray; +Cc: intel-xe
== Series Details ==
Series: drm/xe: Access counter consumer layer
URL : https://patchwork.freedesktop.org/series/163429/
State : failure
== Summary ==
+ trap cleanup EXIT
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig
ERROR:root:../drivers/gpu/drm/xe/xe_access_counter.c: In function ‘xe_access_counter_service’:
../drivers/gpu/drm/xe/xe_access_counter.c:114:31: error: implicit declaration of function ‘xe_svm_range_setup’; did you mean ‘xe_svm_range_debug’? [-Werror=implicit-function-declaration]
114 | err = xe_svm_range_setup(vm, vma, gt, page_va,
| ^~~~~~~~~~~~~~~~~~
| xe_svm_range_debug
cc1: some warnings being treated as errors
make[7]: *** [../scripts/Makefile.build:289: drivers/gpu/drm/xe/xe_access_counter.o] Error 1
make[7]: *** Waiting for unfinished jobs....
make[6]: *** [../scripts/Makefile.build:549: drivers/gpu/drm/xe] Error 2
make[6]: *** Waiting for unfinished jobs....
make[5]: *** [../scripts/Makefile.build:549: drivers/gpu/drm] Error 2
make[4]: *** [../scripts/Makefile.build:549: drivers/gpu] Error 2
make[3]: *** [../scripts/Makefile.build:549: drivers] Error 2
make[3]: *** Waiting for unfinished jobs....
make[2]: *** [/kernel/Makefile:2105: .] Error 2
make[1]: *** [/kernel/Makefile:248: __sub-make] Error 2
make: *** [Makefile:248: __sub-make] Error 2
[07:34:37] Configuring KUnit Kernel ...
Generating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[07:34:41] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel
^ permalink raw reply [flat|nested] 37+ messages in thread
* [RFC 00/15] drm/xe: Access counter consumer layer
@ 2026-03-18 7:44 Himal Prasad Ghimiray
2026-03-18 7:34 ` ✗ CI.checkpatch: warning for " Patchwork
` (16 more replies)
0 siblings, 17 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
This series implements the Xe access counter consumer layer for discrete
Intel GPUs. Access counters track GPU memory access patterns and emit
notifications when a threshold is exceeded; the KMD uses these as hints
to migrate BOs and SVM ranges to tile-local VRAM.
The series starts by factoring the identical circular buffer code shared
between page faults and access counters into a generic struct
xe_usm_queue, then wires both consumers to it. The access counter consumer reuses
pf_wq rather than allocating a new workqueue.
Two helpers are extracted from the page fault path for reuse:
xe_vma_lock_and_validate() and xe_device_asid_to_fault_vm(). The worker
handles both BO-backed VMAs (migrate + rebind) and SVM VMAs (via
xe_svm_range_setup()).
A new exec queue extension (DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM)
lets userspace configure per-queue trigger/notify thresholds and
granularity, which are programmed into CTX_ACC_CTR_THOLD and CTX_ASID
during LRC init (Bspec: 59264, 59265).
Finally, xe_vma_supports_access_ctr() identifies VMAs ineligible for
access counting, and xe_pt_stage_bind() sets the NC PTE bit on them to
avoid wasting finite hardware counter slots (Bspec: 67095).
Suggested-by: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
Himal Prasad Ghimiray (15):
drm/xe: Add xe_usm_queue generic USM circular buffer
drm/xe/pagefault: Use xe_usm_queue helpers
drm/xe: Stub out new access_counter layer
drm/xe: Implement xe_access_counter_init
drm/xe: Implement xe_access_counter_handler
drm/xe: Extract xe_vma_lock_and_validate helper
drm/xe: Move ASID to FAULT VM lookup to xe_device
drm/xe: Implement xe_access_counter_queue_work
drm/xe/trace: Add xe_vma_acc trace event for access counter
notifications
drm/xe/svm: Handle svm ranges on access ctr trigger
drm/xe: Add xe_guc_access_counter layer
drm/xe/uapi: Add access counter parameter extension for exec queue
drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params
drm/xe/vm: Add xe_vma_supports_access_ctr() helper
drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting
drivers/gpu/drm/xe/Makefile | 4 +-
drivers/gpu/drm/xe/regs/xe_gtt_defs.h | 2 +-
drivers/gpu/drm/xe/regs/xe_lrc_layout.h | 10 +
drivers/gpu/drm/xe/xe_access_counter.c | 255 +++++++++++++++++++
drivers/gpu/drm/xe/xe_access_counter.h | 17 ++
drivers/gpu/drm/xe/xe_access_counter_types.h | 123 +++++++++
drivers/gpu/drm/xe/xe_device.c | 30 +++
drivers/gpu/drm/xe/xe_device.h | 1 +
drivers/gpu/drm/xe/xe_device_types.h | 6 +-
drivers/gpu/drm/xe/xe_exec_queue.c | 29 ++-
drivers/gpu/drm/xe/xe_exec_queue_types.h | 9 +
drivers/gpu/drm/xe/xe_execlist.c | 2 +-
drivers/gpu/drm/xe/xe_guc_access_counter.c | 62 +++++
drivers/gpu/drm/xe/xe_guc_access_counter.h | 15 ++
drivers/gpu/drm/xe/xe_guc_ct.c | 4 +
drivers/gpu/drm/xe/xe_lrc.c | 35 ++-
drivers/gpu/drm/xe/xe_lrc.h | 5 +-
drivers/gpu/drm/xe/xe_pagefault.c | 79 +-----
drivers/gpu/drm/xe/xe_pagefault_types.h | 27 +-
drivers/gpu/drm/xe/xe_pt.c | 11 +
drivers/gpu/drm/xe/xe_svm.c | 59 +++--
drivers/gpu/drm/xe/xe_svm.h | 4 +
drivers/gpu/drm/xe/xe_trace_bo.h | 32 ++-
drivers/gpu/drm/xe/xe_usm_queue.h | 100 ++++++++
drivers/gpu/drm/xe/xe_vm.c | 72 ++++++
drivers/gpu/drm/xe/xe_vm.h | 6 +
include/uapi/drm/xe_drm.h | 39 +++
27 files changed, 910 insertions(+), 128 deletions(-)
create mode 100644 drivers/gpu/drm/xe/xe_access_counter.c
create mode 100644 drivers/gpu/drm/xe/xe_access_counter.h
create mode 100644 drivers/gpu/drm/xe/xe_access_counter_types.h
create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.c
create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.h
create mode 100644 drivers/gpu/drm/xe/xe_usm_queue.h
--
2.34.1
^ permalink raw reply [flat|nested] 37+ messages in thread
* [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
2026-03-18 7:34 ` ✗ CI.checkpatch: warning for " Patchwork
2026-03-18 7:35 ` ✗ CI.KUnit: failure " Patchwork
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-01 21:28 ` Matthew Brost
2026-03-18 7:44 ` [RFC 02/15] drm/xe/pagefault: Use xe_usm_queue helpers Himal Prasad Ghimiray
` (13 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Introduce struct xe_usm_queue, a generic lock-protected circular FIFO
used by USM consumers (page fault, access counter) to receive fixed-size
entries from IRQ context and process them via a work_struct.
Provide three inline helpers in xe_usm_queue.h:
xe_usm_queue_full() - CIRC_SPACE check (caller holds lock)
xe_usm_queue_pop() - dequeue one entry (acquires lock internally)
xe_usm_queue_push() - enqueue one entry (caller holds lock)
Suggested-by: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_usm_queue.h | 100 ++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 drivers/gpu/drm/xe/xe_usm_queue.h
diff --git a/drivers/gpu/drm/xe/xe_usm_queue.h b/drivers/gpu/drm/xe/xe_usm_queue.h
new file mode 100644
index 000000000000..5dc2d692d8bb
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_usm_queue.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_USM_QUEUE_H_
+#define _XE_USM_QUEUE_H_
+
+#include <linux/circ_buf.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+/**
+ * struct xe_usm_queue - Generic USM circular FIFO queue
+ *
+ * A lock-protected circular buffer used by both the page fault consumer and
+ * the access counter consumer. Producers push fixed-size entries from IRQ
+ * context; a work_struct processes them asynchronously.
+ */
+struct xe_usm_queue {
+ /**
+ * @data: Raw byte buffer backing the queue, protected by @lock
+ */
+ void *data;
+ /** @size: Total size of @data in bytes */
+ u32 size;
+ /** @head: Write cursor in bytes, moved by producer, protected by @lock */
+ u32 head;
+ /** @tail: Read cursor in bytes, moved by consumer, protected by @lock */
+ u32 tail;
+ /** @lock: Protects the queue head/tail */
+ spinlock_t lock;
+ /** @worker: Work item scheduled to drain the queue */
+ struct work_struct worker;
+};
+
+/**
+ * xe_usm_queue_full - Check whether the queue has no room for one more entry
+ * @q: queue
+ * @size: exact size of one entry in bytes (sizeof the entry struct)
+ *
+ * Must be called with @q->lock held.
+ *
+ * Return: true if the queue is full
+ */
+static inline bool xe_usm_queue_full(struct xe_usm_queue *q, u32 size)
+{
+ u32 entry_size = roundup_pow_of_two(size);
+
+ lockdep_assert_held(&q->lock);
+
+ return CIRC_SPACE(q->head, q->tail, q->size) <= entry_size;
+}
+/**
+ * xe_usm_queue_pop - Pop one entry from the queue into @out
+ * @q: queue
+ * @out: destination buffer, must be at least @size bytes
+ * @size: exact size of one entry in bytes (sizeof the entry struct)
+ *
+ * Acquires @q->lock internally with spin_lock_irq().
+ *
+ * Return: true if an entry was dequeued, false if the queue was empty
+ */
+static inline bool xe_usm_queue_pop(struct xe_usm_queue *q, void *out,
+ u32 size)
+{
+ u32 entry_size = roundup_pow_of_two(size);
+ bool found = false;
+
+ spin_lock_irq(&q->lock);
+ if (q->tail != q->head) {
+ memcpy(out, q->data + q->tail, size);
+ q->tail = (q->tail + entry_size) % q->size;
+ found = true;
+ }
+ spin_unlock_irq(&q->lock);
+
+ return found;
+}
+
+/**
+ * xe_usm_queue_push - Push one entry into the queue
+ * @q: queue
+ * @in: source buffer, must be at least @size bytes
+ * @size: size of one entry of in bytes
+ *
+ * Must be called with @q->lock held.
+ * Caller must check xe_usm_queue_full() before calling.
+ */
+static inline void xe_usm_queue_push(struct xe_usm_queue *q, const void *in,
+ u32 size)
+{
+ u32 entry_size = roundup_pow_of_two(size);
+
+ lockdep_assert_held(&q->lock);
+
+ memcpy(q->data + q->head, in, size);
+ q->head = (q->head + entry_size) % q->size;
+}
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 02/15] drm/xe/pagefault: Use xe_usm_queue helpers
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (2 preceding siblings ...)
2026-03-18 7:44 ` [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 03/15] drm/xe: Stub out new access_counter layer Himal Prasad Ghimiray
` (12 subsequent siblings)
16 siblings, 0 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Wire struct xe_pagefault into the generic xe_usm_queue infrastructure
introduced in the previous commit.
- Use struct xe_usm_queue directly in all pagefault function signatures
instead of keeping a xe_pagefault_queue alias; xe_device_types.h
already declares pf_queue[] as struct xe_usm_queue.
- Replace xe_pagefault_entry_size() function with XE_PF_ENTRY_SIZE macro
so the value is available at compile time.
- Remove xe_pagefault_queue_pop() and xe_pagefault_queue_full() wrappers;
call xe_usm_queue_pop(), xe_usm_queue_full(), and xe_usm_queue_push()
directly at every call site.
No behaviour change.
Suggested-by: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_device_types.h | 2 +-
drivers/gpu/drm/xe/xe_pagefault.c | 41 +++++--------------------
drivers/gpu/drm/xe/xe_pagefault_types.h | 27 ++--------------
3 files changed, 10 insertions(+), 60 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 615218d775b1..b9231544e500 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -310,7 +310,7 @@ struct xe_device {
*/
#define XE_PAGEFAULT_QUEUE_COUNT 4
/** @usm.pf_queue: Page fault queues */
- struct xe_pagefault_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
+ struct xe_usm_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/** @usm.pagemap_shrinker: Shrinker for unused pagemaps */
struct drm_pagemap_shrinker *dpagemap_shrinker;
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index ea4857acf28d..67dd07927684 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -204,23 +204,6 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
return err;
}
-static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue,
- struct xe_pagefault *pf)
-{
- bool found_fault = false;
-
- spin_lock_irq(&pf_queue->lock);
- if (pf_queue->tail != pf_queue->head) {
- memcpy(pf, pf_queue->data + pf_queue->tail, sizeof(*pf));
- pf_queue->tail = (pf_queue->tail + xe_pagefault_entry_size()) %
- pf_queue->size;
- found_fault = true;
- }
- spin_unlock_irq(&pf_queue->lock);
-
- return found_fault;
-}
-
static void xe_pagefault_print(struct xe_pagefault *pf)
{
xe_gt_info(pf->gt, "\n\tASID: %d\n"
@@ -246,7 +229,7 @@ static void xe_pagefault_print(struct xe_pagefault *pf)
static void xe_pagefault_queue_work(struct work_struct *w)
{
- struct xe_pagefault_queue *pf_queue =
+ struct xe_usm_queue *pf_queue =
container_of(w, typeof(*pf_queue), worker);
struct xe_pagefault pf;
unsigned long threshold;
@@ -254,7 +237,7 @@ static void xe_pagefault_queue_work(struct work_struct *w)
#define USM_QUEUE_MAX_RUNTIME_MS 20
threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
- while (xe_pagefault_queue_pop(pf_queue, &pf)) {
+ while (xe_usm_queue_pop(pf_queue, &pf, sizeof(struct xe_pagefault))) {
int err;
if (!pf.gt) /* Fault squashed during reset */
@@ -284,7 +267,7 @@ static void xe_pagefault_queue_work(struct work_struct *w)
}
static int xe_pagefault_queue_init(struct xe_device *xe,
- struct xe_pagefault_queue *pf_queue)
+ struct xe_usm_queue *pf_queue)
{
struct xe_gt *gt;
int total_num_eus = 0;
@@ -373,7 +356,7 @@ int xe_pagefault_init(struct xe_device *xe)
}
static void xe_pagefault_queue_reset(struct xe_device *xe, struct xe_gt *gt,
- struct xe_pagefault_queue *pf_queue)
+ struct xe_usm_queue *pf_queue)
{
u32 i;
@@ -410,14 +393,6 @@ void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt)
xe_pagefault_queue_reset(xe, gt, xe->usm.pf_queue + i);
}
-static bool xe_pagefault_queue_full(struct xe_pagefault_queue *pf_queue)
-{
- lockdep_assert_held(&pf_queue->lock);
-
- return CIRC_SPACE(pf_queue->head, pf_queue->tail, pf_queue->size) <=
- xe_pagefault_entry_size();
-}
-
/**
* xe_pagefault_handler() - Page fault handler
* @xe: xe device instance
@@ -430,17 +405,15 @@ static bool xe_pagefault_queue_full(struct xe_pagefault_queue *pf_queue)
*/
int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf)
{
- struct xe_pagefault_queue *pf_queue = xe->usm.pf_queue +
+ struct xe_usm_queue *pf_queue = xe->usm.pf_queue +
(pf->consumer.asid % XE_PAGEFAULT_QUEUE_COUNT);
unsigned long flags;
bool full;
spin_lock_irqsave(&pf_queue->lock, flags);
- full = xe_pagefault_queue_full(pf_queue);
+ full = xe_usm_queue_full(pf_queue, sizeof(struct xe_pagefault));
if (!full) {
- memcpy(pf_queue->data + pf_queue->head, pf, sizeof(*pf));
- pf_queue->head = (pf_queue->head + xe_pagefault_entry_size()) %
- pf_queue->size;
+ xe_usm_queue_push(pf_queue, pf, sizeof(struct xe_pagefault));
queue_work(xe->usm.pf_wq, &pf_queue->worker);
} else {
drm_warn(&xe->drm,
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index b3289219b1be..49359f643358 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -8,6 +8,8 @@
#include <linux/workqueue.h>
+#include "xe_usm_queue.h"
+
struct xe_gt;
struct xe_pagefault;
@@ -110,29 +112,4 @@ struct xe_pagefault {
u32 msg[XE_PAGEFAULT_PRODUCER_MSG_LEN_DW];
} producer;
};
-
-/**
- * struct xe_pagefault_queue: Xe pagefault queue (consumer)
- *
- * Used to capture all device page faults for deferred processing. Size this
- * queue to absorb the device’s worst-case number of outstanding faults.
- */
-struct xe_pagefault_queue {
- /**
- * @data: Data in queue containing struct xe_pagefault, protected by
- * @lock
- */
- void *data;
- /** @size: Size of queue in bytes */
- u32 size;
- /** @head: Head pointer in bytes, moved by producer, protected by @lock */
- u32 head;
- /** @tail: Tail pointer in bytes, moved by consumer, protected by @lock */
- u32 tail;
- /** @lock: protects page fault queue */
- spinlock_t lock;
- /** @worker: to process page faults */
- struct work_struct worker;
-};
-
#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 03/15] drm/xe: Stub out new access_counter layer
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (3 preceding siblings ...)
2026-03-18 7:44 ` [RFC 02/15] drm/xe/pagefault: Use xe_usm_queue helpers Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-02 21:46 ` Matthew Brost
2026-03-18 7:44 ` [RFC 04/15] drm/xe: Implement xe_access_counter_init Himal Prasad Ghimiray
` (11 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Add access counter infrastructure with type definitions, header files,
and stub implementation. This follows a two-layer producer-consumer
architecture similar to the pagefault layer.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/Makefile | 3 +-
drivers/gpu/drm/xe/xe_access_counter.c | 55 +++++++++
drivers/gpu/drm/xe/xe_access_counter.h | 17 +++
drivers/gpu/drm/xe/xe_access_counter_types.h | 123 +++++++++++++++++++
4 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/xe/xe_access_counter.c
create mode 100644 drivers/gpu/drm/xe/xe_access_counter.h
create mode 100644 drivers/gpu/drm/xe/xe_access_counter_types.h
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 3a3f9f22d42a..92d8d6e4a447 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -32,7 +32,8 @@ $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe
# core driver code
-xe-y += xe_bb.o \
+xe-y += xe_access_counter.o \
+ xe_bb.o \
xe_bo.o \
xe_bo_evict.o \
xe_dep_scheduler.o \
diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
new file mode 100644
index 000000000000..f3a8a93b5135
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_access_counter.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <linux/circ_buf.h>
+
+#include <drm/drm_exec.h>
+#include <drm/drm_managed.h>
+
+#include "xe_access_counter.h"
+#include "xe_access_counter_types.h"
+#include "xe_device.h"
+
+/**
+ * DOC: Xe access counters
+ *
+ * Xe access counters are handled in two layers with one-way communication.
+ * The producer layer interacts with hardware or firmware to receive and parse
+ * access counter notifications into struct xe_access_counter, then forwards them
+ * to the consumer. The consumer layer services the notifications (e.g., memory
+ * migration hints, binding decisions). No acknowledgment is sent back to the
+ * producer. The consumer uses an access counter queue sized to absorb all potential
+ * notifications and a multi-threaded worker to process them. Multiple producers
+ * are supported, with a single shared consumer.
+ *
+ * xe_access_counter.c implements the consumer layer.
+ */
+
+/**
+ * xe_access_counter_init - Initialize access counter consumer layer
+ * @xe: xe device
+ *
+ * Return: 0 on success, negative error code on error
+ */
+int xe_access_counter_init(struct xe_device *xe)
+{
+ /* Stub implementation - to be filled in */
+ return 0;
+}
+
+/**
+ * xe_access_counter_handler - Handle an access counter notification
+ * @xe: xe device
+ * @ac: access counter notification
+ *
+ * Process an access counter notification from the producer layer.
+ *
+ * Return: 0 on success, negative error code on error
+ */
+int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
+{
+ /* Stub implementation - to be filled in */
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_access_counter.h b/drivers/gpu/drm/xe/xe_access_counter.h
new file mode 100644
index 000000000000..b3a331687f13
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_access_counter.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_ACCESS_COUNTER_H_
+#define _XE_ACCESS_COUNTER_H_
+
+struct xe_device;
+struct xe_gt;
+struct xe_access_counter;
+
+int xe_access_counter_init(struct xe_device *xe);
+
+int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_access_counter_types.h b/drivers/gpu/drm/xe/xe_access_counter_types.h
new file mode 100644
index 000000000000..74b903b9461b
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_access_counter_types.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_ACCESS_COUNTER_TYPES_H_
+#define _XE_ACCESS_COUNTER_TYPES_H_
+
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+struct xe_gt;
+struct xe_access_counter;
+
+/** enum xe_access_counter_type - Xe access counter type */
+enum xe_access_counter_type {
+ /** @XE_ACCESS_COUNTER_TYPE_TRIGGER */
+ XE_ACCESS_COUNTER_TYPE_TRIGGER = 0,
+ /** @XE_ACCESS_COUNTER_TYPE_NOTIFY*/
+ XE_ACCESS_COUNTER_TYPE_NOTIFY = 1,
+};
+
+/** enum xe_access_counter_granularity - Xe access counter granularity */
+enum xe_access_counter_granularity {
+ /** @XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
+ XE_ACCESS_COUNTER_GRANULARITY_128K = 0,
+ /** @XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
+ XE_ACCESS_COUNTER_GRANULARITY_2M = 1,
+ /** @XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
+ XE_ACCESS_COUNTER_GRANULARITY_16M = 2,
+ /** @XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
+ XE_ACCESS_COUNTER_GRANULARITY_64M = 3,
+};
+
+static inline int xe_access_counter_granularity_in_byte(int val)
+{
+ switch (val) {
+ case XE_ACCESS_COUNTER_GRANULARITY_128K:
+ return SZ_128K;
+ case XE_ACCESS_COUNTER_GRANULARITY_2M:
+ return SZ_2M;
+ case XE_ACCESS_COUNTER_GRANULARITY_16M:
+ return SZ_16M;
+ case XE_ACCESS_COUNTER_GRANULARITY_64M:
+ return SZ_64M;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * struct xe_access_counter - Xe access counter
+ *
+ * Generic access counter structure for communication between producer and consumer.
+ * Carefully sized to be 64 bytes. Upon a device access counter notification, the
+ * producer populates this structure, and the consumer copies it into the access
+ * counter queue for deferred handling.
+ */
+struct xe_access_counter {
+ /**
+ * @gt: GT of access counter
+ */
+ struct xe_gt *gt;
+ /**
+ * @consumer: State for the software handling the access counter.
+ * Populated by the producer and may be modified by the consumer to
+ * communicate information back to the producer upon acknowledgment.
+ */
+ struct {
+ /** @consumer.va_range_base: base virtual address of range */
+ u64 va_range_base;
+ /** @consumer.sub_granularity: sub-granularity */
+ u32 sub_granularity;
+ /**
+ * @consumer.counter_type: counter type, u8 rather than enum to
+ * keep size compact
+ */
+ u8 counter_type;
+ /**
+ * @consumer.granularity: access granularity, u8 rather than enum
+ * to keep size compact
+ */
+ u8 granularity;
+ /** @consumer.reserved: reserved bits for alignment */
+ u8 reserved[2];
+ /** @consumer: Platform-specific fields */
+ union {
+ /** @consumer.xe3: Xe3-specific fields */
+ struct {
+ /** @consumer.xe3.asid: address space ID */
+ u32 asid;
+ /** @consumer.xe3.engine_class: engine class */
+ u8 engine_class;
+ /** @consumer.xe3.engine_instance: engine instance */
+ u8 engine_instance;
+ /** @consumer.xe3.vfid: VFID */
+ u8 vfid;
+ /** @consumer.xe3.reserved: reserved bits */
+ u8 reserved;
+ } xe3;
+ };
+ } consumer;
+ /**
+ * @producer: State for the producer (i.e., HW/FW interface). Populated
+ * by the producer and should not be modified—or even inspected—by the
+ * consumer, except for calling operations.
+ */
+ struct {
+ /** @producer.private: private pointer */
+ void *private;
+#define XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW 4
+ /**
+ * @producer.msg: access counter message, used by producer in
+ * acknowledgment to formulate response to HW/FW interface.
+ * Included in the access counter message because the producer
+ * typically receives the notification in a context where memory
+ * cannot be allocated (e.g., atomic context or the reclaim path).
+ */
+ u32 msg[XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW];
+ } producer;
+};
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 04/15] drm/xe: Implement xe_access_counter_init
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (4 preceding siblings ...)
2026-03-18 7:44 ` [RFC 03/15] drm/xe: Stub out new access_counter layer Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 05/15] drm/xe: Implement xe_access_counter_handler Himal Prasad Ghimiray
` (10 subsequent siblings)
16 siblings, 0 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Implement initialization for access counter queues and per-queue
workers. Reuse the existing page fault workqueue (pf_wq) as both
subsystems are part of the same USM domain.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_access_counter.c | 45 +++++++++++++++++++++++++-
drivers/gpu/drm/xe/xe_device.c | 5 +++
drivers/gpu/drm/xe/xe_device_types.h | 4 +++
3 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
index f3a8a93b5135..cafcc42897f8 100644
--- a/drivers/gpu/drm/xe/xe_access_counter.c
+++ b/drivers/gpu/drm/xe/xe_access_counter.c
@@ -11,6 +11,7 @@
#include "xe_access_counter.h"
#include "xe_access_counter_types.h"
#include "xe_device.h"
+#include "xe_usm_queue.h"
/**
* DOC: Xe access counters
@@ -27,6 +28,38 @@
* xe_access_counter.c implements the consumer layer.
*/
+static int xe_access_counter_entry_size(void)
+{
+ return roundup_pow_of_two(sizeof(struct xe_access_counter));
+}
+
+static void xe_access_counter_queue_work_func(struct work_struct *w)
+{
+ /* TODO: Implement */
+}
+
+static int xe_access_counter_queue_init(struct xe_device *xe,
+ struct xe_usm_queue *ac_queue)
+{
+ /*
+ * Allocate a fixed-size queue for access counter notifications.
+ * Size is determined based on hardware capabilities.
+ */
+#define XE_ACCESS_COUNTER_QUEUE_NUM_ENTRIES 128
+ ac_queue->size = XE_ACCESS_COUNTER_QUEUE_NUM_ENTRIES *
+ xe_access_counter_entry_size();
+#undef XE_ACCESS_COUNTER_QUEUE_NUM_ENTRIES
+
+ ac_queue->data = devm_kzalloc(xe->drm.dev, ac_queue->size, GFP_KERNEL);
+ if (!ac_queue->data)
+ return -ENOMEM;
+
+ spin_lock_init(&ac_queue->lock);
+ INIT_WORK(&ac_queue->worker, xe_access_counter_queue_work_func);
+
+ return 0;
+}
+
/**
* xe_access_counter_init - Initialize access counter consumer layer
* @xe: xe device
@@ -35,7 +68,17 @@
*/
int xe_access_counter_init(struct xe_device *xe)
{
- /* Stub implementation - to be filled in */
+ int err, i;
+
+ if (!xe->info.has_usm)
+ return 0;
+
+ for (i = 0; i < XE_ACCESS_COUNTER_QUEUE_COUNT; ++i) {
+ err = xe_access_counter_queue_init(xe, xe->usm.ac_queue + i);
+ if (err)
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index e77a3a3db73d..8773c2d2f69a 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -23,6 +23,7 @@
#include "instructions/xe_gpu_commands.h"
#include "regs/xe_gt_regs.h"
#include "regs/xe_regs.h"
+#include "xe_access_counter.h"
#include "xe_bo.h"
#include "xe_bo_evict.h"
#include "xe_debugfs.h"
@@ -933,6 +934,10 @@ int xe_device_probe(struct xe_device *xe)
if (err)
return err;
+ err = xe_access_counter_init(xe);
+ if (err)
+ return err;
+
if (xe->tiles->media_gt &&
XE_GT_WA(xe->tiles->media_gt, 15015404425_disable))
XE_DEVICE_WA_DISABLE(xe, 15015404425);
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index b9231544e500..526cfd09d0b9 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -12,6 +12,7 @@
#include <drm/drm_file.h>
#include <drm/ttm/ttm_device.h>
+#include "xe_access_counter_types.h"
#include "xe_devcoredump_types.h"
#include "xe_drm_ras_types.h"
#include "xe_heci_gsc.h"
@@ -311,6 +312,9 @@ struct xe_device {
#define XE_PAGEFAULT_QUEUE_COUNT 4
/** @usm.pf_queue: Page fault queues */
struct xe_usm_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
+#define XE_ACCESS_COUNTER_QUEUE_COUNT 4
+ /** @usm.ac_queue: Access counter queues */
+ struct xe_usm_queue ac_queue[XE_ACCESS_COUNTER_QUEUE_COUNT];
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/** @usm.pagemap_shrinker: Shrinker for unused pagemaps */
struct drm_pagemap_shrinker *dpagemap_shrinker;
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 05/15] drm/xe: Implement xe_access_counter_handler
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (5 preceding siblings ...)
2026-03-18 7:44 ` [RFC 04/15] drm/xe: Implement xe_access_counter_init Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-03 2:06 ` Matthew Brost
2026-03-18 7:44 ` [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper Himal Prasad Ghimiray
` (9 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Enqueue access counter notifications to the appropriate queue based on
ASID and schedule worker to process them.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_access_counter.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
index cafcc42897f8..a2ce9dc45d05 100644
--- a/drivers/gpu/drm/xe/xe_access_counter.c
+++ b/drivers/gpu/drm/xe/xe_access_counter.c
@@ -87,12 +87,29 @@ int xe_access_counter_init(struct xe_device *xe)
* @xe: xe device
* @ac: access counter notification
*
- * Process an access counter notification from the producer layer.
+ * Sink the access counter notification to a queue (i.e., a memory buffer) and
+ * queue a worker to service it.
*
* Return: 0 on success, negative error code on error
*/
int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
{
- /* Stub implementation - to be filled in */
- return 0;
+ struct xe_usm_queue *ac_queue = xe->usm.ac_queue +
+ (ac->consumer.xe3.asid % XE_ACCESS_COUNTER_QUEUE_COUNT);
+ unsigned long flags;
+ bool full;
+
+ spin_lock_irqsave(&ac_queue->lock, flags);
+ full = xe_usm_queue_full(ac_queue, sizeof(struct xe_access_counter));
+ if (!full) {
+ xe_usm_queue_push(ac_queue, ac, xe_access_counter_entry_size());
+ queue_work(xe->usm.pf_wq, &ac_queue->worker);
+ } else {
+ drm_warn(&xe->drm,
+ "AccessCounter Queue (%d) full, shouldn't be possible\n",
+ ac->consumer.xe3.asid % XE_ACCESS_COUNTER_QUEUE_COUNT);
+ }
+ spin_unlock_irqrestore(&ac_queue->lock, flags);
+
+ return full ? -ENOSPC : 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (6 preceding siblings ...)
2026-03-18 7:44 ` [RFC 05/15] drm/xe: Implement xe_access_counter_handler Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-01 22:03 ` Matthew Brost
2026-03-18 7:44 ` [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device Himal Prasad Ghimiray
` (8 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Move xe_pagefault_begin to xe_vm.c as xe_vma_lock_and_validate for reuse
in access counter processing.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_pagefault.c | 22 ++--------------------
drivers/gpu/drm/xe/xe_vm.c | 31 +++++++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_vm.h | 3 +++
3 files changed, 36 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index 67dd07927684..8c159e3512ee 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -45,24 +45,6 @@ static int xe_pagefault_entry_size(void)
return roundup_pow_of_two(sizeof(struct xe_pagefault));
}
-static int xe_pagefault_begin(struct drm_exec *exec, struct xe_vma *vma,
- struct xe_vram_region *vram, bool need_vram_move)
-{
- struct xe_bo *bo = xe_vma_bo(vma);
- struct xe_vm *vm = xe_vma_vm(vma);
- int err;
-
- err = xe_vm_lock_vma(exec, vma);
- if (err)
- return err;
-
- if (!bo)
- return 0;
-
- return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
- xe_bo_validate(bo, vm, true, exec);
-}
-
static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma,
bool atomic)
{
@@ -103,8 +85,8 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma,
/* 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_pagefault_begin(&exec, vma, tile->mem.vram,
- needs_vram == 1);
+ err = xe_vma_lock_and_validate(&exec, vma, tile->mem.vram,
+ needs_vram == 1);
drm_exec_retry_on_contention(&exec);
xe_validation_retry_on_oom(&ctx, &err);
if (err)
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 5572e12c2a7e..659504ec5a13 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1170,6 +1170,37 @@ int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma)
return err;
}
+/**
+ * xe_vma_lock_and_validate - Lock Vma and Validate bo location
+ * @exec: drm execution context
+ * @vma: VMA to prepare
+ * @vram: target VRAM region
+ * @need_vram_move: true if BO must be moved to VRAM
+ *
+ * Locks the VMA and its associated BO, then ensures the BO is in the correct
+ * memory location for GPU access. If need_vram_move is true, migrates the BO
+ * to VRAM; otherwise validates it in its current location.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int xe_vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
+ struct xe_vram_region *vram, bool need_vram_move)
+{
+ struct xe_bo *bo = xe_vma_bo(vma);
+ struct xe_vm *vm = xe_vma_vm(vma);
+ int err;
+
+ err = xe_vm_lock_vma(exec, vma);
+ if (err)
+ return err;
+
+ if (!bo)
+ return 0;
+
+ return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
+ xe_bo_validate(bo, vm, true, exec);
+}
+
static void xe_vma_destroy_unlocked(struct xe_vma *vma)
{
struct xe_device *xe = xe_vma_vm(vma)->xe;
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index 0bc7ed23eeae..127cecfcb80b 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -271,6 +271,9 @@ static inline void xe_vm_reactivate_rebind(struct xe_vm *vm)
int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma);
+int xe_vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
+ struct xe_vram_region *vram, bool need_vram_move);
+
int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
unsigned int num_fences);
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (7 preceding siblings ...)
2026-03-18 7:44 ` [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-02 21:50 ` Matthew Brost
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
` (7 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Move xe_pagefault_asid_to_vm() to xe_device.c as
xe_device_asid_to_fault_vm() for reuse in access counter handling.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_device.c | 25 +++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_device.h | 1 +
drivers/gpu/drm/xe/xe_pagefault.c | 16 +---------------
3 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 8773c2d2f69a..ba2f0e345257 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -1399,3 +1399,28 @@ struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid)
return vm;
}
+
+/**
+ * xe_device_asid_to_fault_vm() - Find FAULT VM from ASID
+ * @xe: the &xe_device
+ * @asid: Address space ID
+ *
+ * Find a FAULTING VM from ASID and take a reference to VM which
+ * caller must drop. Reclaim safe.
+ *
+ * Return: VM on success, ERR_PTR on failure
+ */
+struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid)
+{
+ struct xe_vm *vm;
+
+ down_read(&xe->usm.lock);
+ vm = xa_load(&xe->usm.asid_to_vm, asid);
+ if (vm && xe_vm_in_fault_mode(vm))
+ xe_vm_get(vm);
+ else
+ vm = ERR_PTR(-EINVAL);
+ up_read(&xe->usm.lock);
+
+ return vm;
+}
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index c4d267002661..e746f5c7cce0 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -209,6 +209,7 @@ int xe_is_injection_active(void);
bool xe_is_xe_file(const struct file *file);
struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid);
+struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid);
/*
* Occasionally it is seen that the G2H worker starts running after a delay of more than
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index 8c159e3512ee..a4ee9393e4af 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -121,20 +121,6 @@ xe_pagefault_access_is_atomic(enum xe_pagefault_access_type access_type)
return (access_type & XE_PAGEFAULT_ACCESS_TYPE_MASK) == XE_PAGEFAULT_ACCESS_TYPE_ATOMIC;
}
-static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid)
-{
- struct xe_vm *vm;
-
- down_read(&xe->usm.lock);
- vm = xa_load(&xe->usm.asid_to_vm, asid);
- if (vm && xe_vm_in_fault_mode(vm))
- xe_vm_get(vm);
- else
- vm = ERR_PTR(-EINVAL);
- up_read(&xe->usm.lock);
-
- return vm;
-}
static int xe_pagefault_service(struct xe_pagefault *pf)
{
@@ -149,7 +135,7 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
if (pf->consumer.fault_type_level == XE_PAGEFAULT_TYPE_LEVEL_NACK)
return -EFAULT;
- vm = xe_pagefault_asid_to_vm(xe, pf->consumer.asid);
+ vm = xe_device_asid_to_fault_vm(xe, pf->consumer.asid);
if (IS_ERR(vm))
return PTR_ERR(vm);
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (8 preceding siblings ...)
2026-03-18 7:44 ` [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-01 21:10 ` Matthew Brost
` (2 more replies)
2026-03-18 7:44 ` [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications Himal Prasad Ghimiray
` (6 subsequent siblings)
16 siblings, 3 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
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 */
+ 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
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (9 preceding siblings ...)
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-03 1:01 ` Matthew Brost
2026-03-18 7:44 ` [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger Himal Prasad Ghimiray
` (5 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Trace VMA access counter notifications with asid, address range, and
counter type (TRIGGER or NOTIFY) to aid debugging of migration hints.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_access_counter.c | 7 ++++--
drivers/gpu/drm/xe/xe_trace_bo.h | 32 +++++++++++++++++++++++---
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
index 9eb9917d8da7..f93618faab02 100644
--- a/drivers/gpu/drm/xe/xe_access_counter.c
+++ b/drivers/gpu/drm/xe/xe_access_counter.c
@@ -13,6 +13,7 @@
#include "xe_device.h"
#include "xe_gt_printk.h"
#include "xe_hw_engine.h"
+#include "xe_trace_bo.h"
#include "xe_usm_queue.h"
#include "xe_vm.h"
@@ -109,6 +110,8 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
goto unlock_vm;
}
+ trace_xe_vma_acc(vma, ac->consumer.counter_type);
+
/* TODO: Handle svm vma's */
if (xe_vma_has_no_bo(vma))
goto unlock_vm;
@@ -153,7 +156,7 @@ static void xe_access_counter_queue_work_func(struct work_struct *w)
#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())) {
+ while (xe_usm_queue_pop(ac_queue, &ac, sizeof(struct xe_access_counter))) {
int err;
if (!ac.gt) /* Access counter squashed during reset */
@@ -239,7 +242,7 @@ int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac
spin_lock_irqsave(&ac_queue->lock, flags);
full = xe_usm_queue_full(ac_queue, sizeof(struct xe_access_counter));
if (!full) {
- xe_usm_queue_push(ac_queue, ac, xe_access_counter_entry_size());
+ xe_usm_queue_push(ac_queue, ac, sizeof(struct xe_access_counter));
queue_work(xe->usm.pf_wq, &ac_queue->worker);
} else {
drm_warn(&xe->drm,
diff --git a/drivers/gpu/drm/xe/xe_trace_bo.h b/drivers/gpu/drm/xe/xe_trace_bo.h
index 86323cf3be2c..eae5a5d0dbdc 100644
--- a/drivers/gpu/drm/xe/xe_trace_bo.h
+++ b/drivers/gpu/drm/xe/xe_trace_bo.h
@@ -12,6 +12,7 @@
#include <linux/tracepoint.h>
#include <linux/types.h>
+#include "xe_access_counter_types.h"
#include "xe_bo.h"
#include "xe_bo_types.h"
#include "xe_vm.h"
@@ -125,9 +126,34 @@ DEFINE_EVENT(xe_vma, xe_vma_pagefault,
TP_ARGS(vma)
);
-DEFINE_EVENT(xe_vma, xe_vma_acc,
- TP_PROTO(struct xe_vma *vma),
- TP_ARGS(vma)
+TRACE_EVENT(xe_vma_acc,
+ TP_PROTO(struct xe_vma *vma, u8 counter_type),
+ TP_ARGS(vma, counter_type),
+
+ TP_STRUCT__entry(
+ __string(dev, __dev_name_vma(vma))
+ __field(struct xe_vma *, vma)
+ __field(struct xe_vm *, vm)
+ __field(u32, asid)
+ __field(u64, start)
+ __field(u64, end)
+ __field(u8, counter_type)
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->vma = vma;
+ __entry->vm = xe_vma_vm(vma);
+ __entry->asid = xe_vma_vm(vma)->usm.asid;
+ __entry->start = xe_vma_start(vma);
+ __entry->end = xe_vma_end(vma) - 1;
+ __entry->counter_type = counter_type;
+ ),
+
+ TP_printk("dev=%s, vma=%p, vm=%p, asid=0x%05x, start=0x%012llx, end=0x%012llx, type=%s",
+ __get_str(dev), __entry->vma, __entry->vm,
+ __entry->asid, __entry->start, __entry->end,
+ __entry->counter_type == XE_ACCESS_COUNTER_TYPE_NOTIFY ? "NOTIFY" : "TRIGGER")
);
DEFINE_EVENT(xe_vma, xe_vma_bind,
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (10 preceding siblings ...)
2026-03-18 7:44 ` [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-03 0:25 ` Matthew Brost
2026-03-18 7:44 ` [RFC 11/15] drm/xe: Add xe_guc_access_counter layer Himal Prasad Ghimiray
` (4 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Migrate ranges to local vram and setup pte for gpu access.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_access_counter.c | 30 ++++++-------
drivers/gpu/drm/xe/xe_svm.c | 59 +++++++++++++++++++-------
drivers/gpu/drm/xe/xe_svm.h | 4 ++
3 files changed, 63 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
index f93618faab02..ba15f21c6803 100644
--- a/drivers/gpu/drm/xe/xe_access_counter.c
+++ b/drivers/gpu/drm/xe/xe_access_counter.c
@@ -13,6 +13,7 @@
#include "xe_device.h"
#include "xe_gt_printk.h"
#include "xe_hw_engine.h"
+#include "xe_svm.h"
#include "xe_trace_bo.h"
#include "xe_usm_queue.h"
#include "xe_vm.h"
@@ -42,20 +43,14 @@ 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)
+static u64 xe_access_counter_get_va(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);
- }
+ if (ac->consumer.granularity != XE_ACCESS_COUNTER_GRANULARITY_128K)
+ return ac->consumer.va_range_base;
- return xe_vm_find_overlapping_vma(vm, page_va, SZ_4K);
+ return ac->consumer.va_range_base +
+ (ffs(ac->consumer.sub_granularity) - 1) *
+ xe_access_counter_sub_granularity_in_byte(ac->consumer.granularity);
}
static void xe_access_counter_print(struct xe_access_counter *ac)
@@ -88,6 +83,7 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
struct dma_fence *fence;
struct xe_vm *vm;
struct xe_vma *vma;
+ u64 page_va;
int err = 0;
if (ac->consumer.counter_type > XE_ACCESS_COUNTER_TYPE_NOTIFY)
@@ -104,7 +100,8 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
goto unlock_vm;
}
/* Lookup VMA */
- vma = xe_access_counter_get_vma(vm, ac);
+ page_va = xe_access_counter_get_va(ac);
+ vma = xe_vm_find_vma_by_addr(vm, page_va);
if (!vma) {
err = -EINVAL;
goto unlock_vm;
@@ -112,9 +109,12 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
trace_xe_vma_acc(vma, ac->consumer.counter_type);
- /* TODO: Handle svm vma's */
- if (xe_vma_has_no_bo(vma))
+ if (xe_vma_has_no_bo(vma)) {
+ if (xe_vma_is_cpu_addr_mirror(vma))
+ err = xe_svm_range_setup(vm, vma, gt, page_va,
+ false, true);
goto unlock_vm;
+ }
/* Lock VM and BOs dma-resv */
xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index a91c84487a67..fe0f86a2d0bf 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -1186,9 +1186,9 @@ DECL_SVM_RANGE_US_STATS(get_pages, GET_PAGES)
DECL_SVM_RANGE_US_STATS(bind, BIND)
DECL_SVM_RANGE_US_STATS(fault, PAGEFAULT)
-static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_gt *gt, u64 fault_addr,
- bool need_vram)
+static int __xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_gt *gt, u64 fault_addr,
+ bool need_vram, bool acc_ctr_trigger)
{
int devmem_possible = IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP);
@@ -1196,7 +1196,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
.read_only = xe_vma_read_only(vma),
.devmem_possible = devmem_possible,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
- .devmem_only = need_vram && devmem_possible,
+ .devmem_only = (need_vram || acc_ctr_trigger) && devmem_possible,
.timeslice_ms = need_vram && devmem_possible ?
vm->xe->atomic_svm_timeslice_ms : 0,
};
@@ -1213,7 +1213,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
lockdep_assert_held_write(&vm->lock);
xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
- xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_PAGEFAULT_COUNT, 1);
+ if (!acc_ctr_trigger)
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_PAGEFAULT_COUNT, 1);
retry:
/* Always process UNMAPs first so view SVM ranges is current */
@@ -1229,7 +1230,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
if (IS_ERR(range))
return PTR_ERR(range);
- xe_svm_range_fault_count_stats_incr(gt, range);
+ if (!acc_ctr_trigger)
+ xe_svm_range_fault_count_stats_incr(gt, range);
if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) {
err = -EACCES;
@@ -1244,6 +1246,10 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
range_debug(range, "PAGE FAULT");
+ if (acc_ctr_trigger && !range->base.pages.flags.migrate_devmem) {
+ goto out;
+ }
+
if (--migrate_try_count >= 0 &&
xe_svm_range_needs_migrate_to_vram(range, vma, dpagemap)) {
ktime_t migrate_start = xe_gt_stats_ktime_get();
@@ -1307,6 +1313,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
}
xe_svm_range_get_pages_us_stats_incr(gt, range, get_pages_start);
+
range_debug(range, "PAGE FAULT - BIND");
bind_start = xe_gt_stats_ktime_get();
@@ -1347,21 +1354,22 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
}
/**
- * xe_svm_handle_pagefault() - SVM handle page fault
+ * xe_svm_range_setup - Setup range for GPU access
* @vm: The VM.
* @vma: The CPU address mirror VMA.
- * @gt: The gt upon the fault occurred.
- * @fault_addr: The GPU fault address.
+ * @gt: The gt for which binding.
+ * @addr: Addr for which need to bind svm range.
* @atomic: The fault atomic access bit.
+ * @acc_ctr_trigger: If true, always migrate to local device memory.
*
* Create GPU bindings for a SVM page fault. Optionally migrate to device
* memory.
*
* Return: 0 on success, negative error code on error.
*/
-int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
- struct xe_gt *gt, u64 fault_addr,
- bool atomic)
+int xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_gt *gt, u64 addr,
+ bool atomic, bool acc_ctr_trigger)
{
int need_vram, ret;
retry:
@@ -1369,14 +1377,15 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
if (need_vram < 0)
return need_vram;
- ret = __xe_svm_handle_pagefault(vm, vma, gt, fault_addr,
- need_vram ? true : false);
+ ret = __xe_svm_range_setup(vm, vma, gt, addr,
+ need_vram ? true : false,
+ acc_ctr_trigger);
if (ret == -EAGAIN) {
/*
* Retry once on -EAGAIN to re-lookup the VMA, as the original VMA
* may have been split by xe_svm_range_set_default_attr.
*/
- vma = xe_vm_find_vma_by_addr(vm, fault_addr);
+ vma = xe_vm_find_vma_by_addr(vm, addr);
if (!vma)
return -EINVAL;
@@ -1385,6 +1394,26 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
return ret;
}
+/**
+ * xe_svm_handle_pagefault() - SVM handle page fault
+ * @vm: The VM.
+ * @vma: The CPU address mirror VMA.
+ * @gt: The gt upon the fault occurred.
+ * @fault_addr: The GPU fault address.
+ * @atomic: The fault atomic access bit.
+ *
+ * Create GPU bindings for a SVM page fault. Optionally migrate to device
+ * memory.
+ *
+ * Return: 0 on success, negative error code on error.
+ */
+int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_gt *gt, u64 fault_addr,
+ bool atomic)
+{
+ return xe_svm_range_setup(vm, vma, gt, fault_addr, atomic, false);
+}
+
/**
* xe_svm_has_mapping() - SVM has mappings
* @vm: The VM.
diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
index b7b8eeacf196..50861d93b12f 100644
--- a/drivers/gpu/drm/xe/xe_svm.h
+++ b/drivers/gpu/drm/xe/xe_svm.h
@@ -85,6 +85,10 @@ void xe_svm_fini(struct xe_vm *vm);
void xe_svm_close(struct xe_vm *vm);
+int xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
+ struct xe_gt *gt, u64 addr,
+ bool atomic, bool acc_ctr_trigger);
+
int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
struct xe_gt *gt, u64 fault_addr,
bool atomic);
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 11/15] drm/xe: Add xe_guc_access_counter layer
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (11 preceding siblings ...)
2026-03-18 7:44 ` [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-02 21:27 ` Matthew Brost
2026-03-18 7:44 ` [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue Himal Prasad Ghimiray
` (3 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Add GuC to host (G2H) access counter notification handler to parse
GuC firmware messages into struct xe_access_counter and forward to
xe_access_counter_handler for processing.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/Makefile | 1 +
drivers/gpu/drm/xe/xe_guc_access_counter.c | 62 ++++++++++++++++++++++
drivers/gpu/drm/xe/xe_guc_access_counter.h | 15 ++++++
drivers/gpu/drm/xe/xe_guc_ct.c | 4 ++
4 files changed, 82 insertions(+)
create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.c
create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.h
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 92d8d6e4a447..296b3cba0b89 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -74,6 +74,7 @@ xe-y += xe_access_counter.o \
xe_guc_id_mgr.o \
xe_guc_klv_helpers.o \
xe_guc_log.o \
+ xe_guc_access_counter.o \
xe_guc_pagefault.o \
xe_guc_pc.o \
xe_guc_rc.o \
diff --git a/drivers/gpu/drm/xe/xe_guc_access_counter.c b/drivers/gpu/drm/xe/xe_guc_access_counter.c
new file mode 100644
index 000000000000..2158400bc50a
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_access_counter.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include "xe_guc_access_counter.h"
+
+#include "xe_access_counter.h"
+#include "xe_device.h"
+#include "xe_gt.h"
+#include "xe_guc.h"
+#include "xe_guc_fwif.h"
+
+/**
+ * xe_guc_access_counter_handler() - G2H access counter handler
+ * @guc: GuC object
+ * @msg: G2H message
+ * @len: Length of G2H message
+ *
+ * Parse GuC to host (G2H) message into a struct xe_access_counter and forward
+ * onto the Xe access counter layer.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int xe_guc_access_counter_handler(struct xe_guc *guc, u32 *msg, u32 len)
+{
+ struct xe_access_counter ac;
+ struct xe_device *xe = guc_to_xe(guc);
+ int i;
+
+#define GUC_ACC_MSG_LEN_DW \
+ (sizeof(struct xe_guc_acc_desc) / sizeof(u32))
+
+ BUILD_BUG_ON(GUC_ACC_MSG_LEN_DW > XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW);
+
+ if (len != GUC_ACC_MSG_LEN_DW)
+ return -EPROTO;
+
+ ac.gt = guc_to_gt(guc);
+
+ /* Parse access counter descriptor */
+ ac.consumer.granularity = FIELD_GET(ACC_GRANULARITY, msg[2]);
+ ac.consumer.sub_granularity = FIELD_GET(ACC_SUBG_HI, msg[1]) << 31 |
+ FIELD_GET(ACC_SUBG_LO, msg[0]);
+ ac.consumer.counter_type = FIELD_GET(ACC_TYPE, msg[0]);
+ ac.consumer.va_range_base = ((u64)(msg[3] & ACC_VIRTUAL_ADDR_RANGE_HI) << 32) |
+ (msg[2] & ACC_VIRTUAL_ADDR_RANGE_LO);
+ /* xe3: Use ASID and engine info */
+ ac.consumer.xe3.asid = FIELD_GET(ACC_ASID, msg[1]);
+ ac.consumer.xe3.engine_class = FIELD_GET(ACC_ENG_CLASS, msg[1]);
+ ac.consumer.xe3.engine_instance = FIELD_GET(ACC_ENG_INSTANCE, msg[1]);
+ ac.consumer.xe3.vfid = FIELD_GET(ACC_VFID, msg[2]);
+
+ /* Store producer message for potential acknowledgment */
+ ac.producer.private = guc;
+ for (i = 0; i < GUC_ACC_MSG_LEN_DW; ++i)
+ ac.producer.msg[i] = msg[i];
+
+#undef GUC_ACC_MSG_LEN_DW
+
+ return xe_access_counter_handler(xe, &ac);
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_access_counter.h b/drivers/gpu/drm/xe/xe_guc_access_counter.h
new file mode 100644
index 000000000000..1ac8e76398d2
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_access_counter.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_GUC_ACCESS_COUNTER_H_
+#define _XE_GUC_ACCESS_COUNTER_H_
+
+#include <linux/types.h>
+
+struct xe_guc;
+
+int xe_guc_access_counter_handler(struct xe_guc *guc, u32 *msg, u32 len);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index a11cff7a20be..8ac0938f7a28 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -26,6 +26,7 @@
#include "xe_gt_sriov_pf_monitor.h"
#include "xe_guc.h"
#include "xe_guc_log.h"
+#include "xe_guc_access_counter.h"
#include "xe_guc_pagefault.h"
#include "xe_guc_relay.h"
#include "xe_guc_submit.h"
@@ -1630,6 +1631,9 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC:
ret = xe_guc_pagefault_handler(guc, payload, adj_len);
break;
+ case XE_GUC_ACTION_ACCESS_COUNTER_NOTIFY:
+ ret = xe_guc_access_counter_handler(guc, payload, adj_len);
+ break;
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
break;
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (12 preceding siblings ...)
2026-03-18 7:44 ` [RFC 11/15] drm/xe: Add xe_guc_access_counter layer Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-03-24 14:25 ` Francois Dugast
2026-03-18 7:44 ` [RFC 13/15] drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params Himal Prasad Ghimiray
` (2 subsequent siblings)
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Introduce DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension to allow
userspace to configure access counter notifications per exec queue.
The extension provides:
- trigger: Access counter trigger threshold
- notify: Access counter notify threshold
- granularity: Access counter granularity level
These parameters control hardware access counter behavior for memory
access pattern tracking and optimization hints. Userspace can configure
different thresholds per exec queue based on workload characteristics.
UAPI changes:
- Add drm_xe_exec_queue_set_acc_param structure
- Add DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension ID
KMD changes:
- Add exec_queue_user_ext_set_acc_param() handler
- Store parameters in xe_exec_queue.acc structure
- Register handler in extension function table
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_exec_queue.c | 27 ++++++++++++++++
drivers/gpu/drm/xe/xe_exec_queue_types.h | 9 ++++++
include/uapi/drm/xe_drm.h | 39 ++++++++++++++++++++++++
3 files changed, 75 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index b287d0e0e60a..815e82011c6d 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -1080,6 +1080,32 @@ static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properti
return 0;
}
+static int exec_queue_user_ext_set_acc_param(struct xe_device *xe,
+ struct xe_exec_queue *q,
+ u64 extension, u64 *properties)
+{
+ u64 __user *address = u64_to_user_ptr(extension);
+ struct drm_xe_exec_queue_set_acc_param ext;
+ int err;
+
+ if (!xe->info.has_access_counter)
+ return -EINVAL;
+
+ err = copy_from_user(&ext, address, sizeof(ext));
+ if (XE_IOCTL_DBG(xe, err))
+ return -EFAULT;
+
+ if (XE_IOCTL_DBG(xe, ext.pad1 || ext.pad2))
+ return -EINVAL;
+
+ /* Store access counter parameters in exec queue */
+ q->acc.trigger = ext.trigger;
+ q->acc.notify = ext.notify;
+ q->acc.granularity = ext.granularity;
+
+ return 0;
+}
+
static int exec_queue_user_ext_set_property(struct xe_device *xe,
struct xe_exec_queue *q,
u64 extension, u64 *properties)
@@ -1123,6 +1149,7 @@ typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
[DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
+ [DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM] = exec_queue_user_ext_set_acc_param,
};
#define MAX_USER_EXTENSIONS 16
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 8ce78e0b1d50..3f10e3371e5a 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -239,6 +239,15 @@ struct xe_exec_queue {
/** @replay_state: GPU hang replay state */
void *replay_state;
+ /** @acc: Access counter parameters */
+ struct {
+ /** @acc.trigger: Access counter trigger threshold */
+ u16 trigger;
+ /** @acc.notify: Access counter notify threshold */
+ u16 notify;
+ /** @acc.granularity: Access counter granularity */
+ u8 granularity;
+ } acc;
/** @ops: submission backend exec queue operations */
const struct xe_exec_queue_ops *ops;
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index f8b2afb20540..19447ba10b1d 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -1337,6 +1337,7 @@ struct drm_xe_vm_bind {
*/
struct drm_xe_exec_queue_create {
#define DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY 0
+#define DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM 1
#define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY 0
#define DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE 1
#define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE 2
@@ -1377,6 +1378,44 @@ struct drm_xe_exec_queue_create {
__u64 reserved[2];
};
+/** enum drm_xe_access_counter_granularity - Xe access counter granularity */
+enum drm_xe_access_counter_granularity {
+ /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
+ DRM_XE_ACCESS_COUNTER_GRANULARITY_128K,
+ /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
+ DRM_XE_ACCESS_COUNTER_GRANULARITY_2M,
+ /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
+ DRM_XE_ACCESS_COUNTER_GRANULARITY_16M,
+ /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
+ DRM_XE_ACCESS_COUNTER_GRANULARITY_64M,
+};
+
+/**
+ * struct drm_xe_exec_queue_set_acc_param - Access counter parameters extension
+ *
+ * Extension to configure access counter notifications for an exec queue.
+ * Used with DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM.
+ */
+struct drm_xe_exec_queue_set_acc_param {
+ /** @base: base user extension */
+ struct drm_xe_user_extension base;
+
+ /** @trigger: Access counter trigger threshold */
+ __u16 trigger;
+
+ /** @notify: Access counter notify threshold */
+ __u16 notify;
+
+ /** @granularity: granularity, enum @drm_xe_access_counter_granularity */
+ __u8 granularity;
+
+ /** @pad1: MBZ */
+ __u8 pad1;
+
+ /** @pad2: MBZ */
+ __u16 pad2;
+};
+
/**
* struct drm_xe_exec_queue_destroy - Input of &DRM_IOCTL_XE_EXEC_QUEUE_DESTROY
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 13/15] drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (13 preceding siblings ...)
2026-03-18 7:44 ` [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 14/15] drm/xe/vm: Add xe_vma_supports_access_ctr() helper Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting Himal Prasad Ghimiray
16 siblings, 0 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Update xe_lrc_create() and xe_lrc_init() to receive exec_queue pointer,
enabling access to q->acc.{trigger, notify, granularity} during LRC
initialization. Program CTX_ACC_CTR_THOLD and CTX_ASID registers with
access counter values.
Bspec: 59264, 59265
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/regs/xe_lrc_layout.h | 10 +++++++
drivers/gpu/drm/xe/xe_exec_queue.c | 2 +-
drivers/gpu/drm/xe/xe_execlist.c | 2 +-
drivers/gpu/drm/xe/xe_lrc.c | 35 ++++++++++++++++++-------
drivers/gpu/drm/xe/xe_lrc.h | 5 ++--
5 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
index b5eff383902c..35ee070059dd 100644
--- a/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
+++ b/drivers/gpu/drm/xe/regs/xe_lrc_layout.h
@@ -40,4 +40,14 @@
#define INDIRECT_CTX_RING_START_UDW (0x08 + 1)
#define INDIRECT_CTX_RING_CTL (0x0a + 1)
+/* Fields for CTX_ACC_CTR_THOLD */
+#define ACC_TRIGGER_MASK REG_GENMASK(15, 0)
+#define ACC_TRIGGER_VAL(x) REG_FIELD_PREP(ACC_TRIGGER_MASK, x)
+#define ACC_NOTIFY_MASK REG_GENMASK(31, 16)
+#define ACC_NOTIFY_VAL(x) REG_FIELD_PREP(ACC_NOTIFY_MASK, x)
+
+/* Fields for CTX_ASID */
+#define ACC_GRANULARITY_MASK REG_GENMASK(22, 20)
+#define ACC_GRANULARITY_VAL(x) REG_FIELD_PREP(ACC_GRANULARITY_MASK, x)
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 815e82011c6d..05c808b330b4 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -379,7 +379,7 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
marker = xe_gt_sriov_vf_wait_valid_ggtt(q->gt);
- lrc = xe_lrc_create(q->hwe, q->vm, q->replay_state,
+ lrc = xe_lrc_create(q, q->hwe, q->vm, q->replay_state,
xe_lrc_ring_size(), q->msix_vec, flags);
if (IS_ERR(lrc)) {
err = PTR_ERR(lrc);
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index 7e8a3a7db741..05de991ae90a 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -268,7 +268,7 @@ struct xe_execlist_port *xe_execlist_port_create(struct xe_device *xe,
port->hwe = hwe;
- port->lrc = xe_lrc_create(hwe, NULL, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
+ port->lrc = xe_lrc_create(NULL, hwe, NULL, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
if (IS_ERR(port->lrc)) {
err = PTR_ERR(port->lrc);
goto err;
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 35b365ac55e5..9cb91ce63d59 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1439,8 +1439,10 @@ void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_pri
lrc->desc |= FIELD_PREP(LRC_PRIORITY, xe_multi_queue_prio_to_lrc(lrc, priority));
}
-static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct xe_vm *vm,
- void *replay_state, u16 msix_vec, u32 init_flags)
+static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_exec_queue *q,
+ struct xe_hw_engine *hwe, struct xe_vm *vm,
+ void *replay_state,
+ u16 msix_vec, u32 init_flags)
{
struct xe_gt *gt = hwe->gt;
struct xe_tile *tile = gt_to_tile(gt);
@@ -1527,8 +1529,20 @@ static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct
if (lrc_to_xe(lrc)->info.has_64bit_timestamp)
xe_lrc_write_ctx_reg(lrc, CTX_TIMESTAMP_UDW, 0);
- if (xe->info.has_asid && vm)
- xe_lrc_write_ctx_reg(lrc, CTX_ASID, vm->usm.asid);
+ if (xe->info.has_asid && vm) {
+ u32 asid;
+
+ if (q)
+ asid = vm->usm.asid | ACC_GRANULARITY_VAL(q->acc.granularity);
+ else
+ asid = vm->usm.asid;
+ xe_lrc_write_ctx_reg(lrc, CTX_ASID, asid);
+ }
+
+ if (q && xe->info.has_access_counter && vm)
+ xe_lrc_write_ctx_reg(lrc, CTX_ACC_CTR_THOLD,
+ ACC_NOTIFY_VAL(q->acc.notify) |
+ ACC_TRIGGER_VAL(q->acc.trigger));
lrc->desc = LRC_VALID;
lrc->desc |= FIELD_PREP(LRC_ADDRESSING_MODE, LRC_LEGACY_64B_CONTEXT);
@@ -1570,7 +1584,8 @@ static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct
return err;
}
-static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct xe_vm *vm,
+static int xe_lrc_init(struct xe_lrc *lrc, struct xe_exec_queue *q,
+ struct xe_hw_engine *hwe, struct xe_vm *vm,
void *replay_state, u32 ring_size, u16 msix_vec, u32 init_flags)
{
struct xe_gt *gt = hwe->gt;
@@ -1626,7 +1641,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct xe_v
xe_hw_fence_ctx_init(&lrc->fence_ctx, hwe->gt,
hwe->fence_irq, hwe->name);
- err = xe_lrc_ctx_init(lrc, hwe, vm, replay_state, msix_vec, init_flags);
+ err = xe_lrc_ctx_init(lrc, q, hwe, vm, replay_state, msix_vec, init_flags);
if (err)
goto err_lrc_finish;
@@ -1642,6 +1657,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct xe_v
/**
* xe_lrc_create - Create a LRC
+ * @q: Exec queue (can be NULL for kernel queues)
* @hwe: Hardware Engine
* @vm: The VM (address space)
* @replay_state: GPU hang replay state
@@ -1654,8 +1670,9 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct xe_v
* Return pointer to created LRC upon success and an error pointer
* upon failure.
*/
-struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
- void *replay_state, u32 ring_size, u16 msix_vec, u32 flags)
+struct xe_lrc *xe_lrc_create(struct xe_exec_queue *q, struct xe_hw_engine *hwe,
+ struct xe_vm *vm, void *replay_state, u32 ring_size,
+ u16 msix_vec, u32 flags)
{
struct xe_lrc *lrc;
int err;
@@ -1664,7 +1681,7 @@ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
if (!lrc)
return ERR_PTR(-ENOMEM);
- err = xe_lrc_init(lrc, hwe, vm, replay_state, ring_size, msix_vec, flags);
+ err = xe_lrc_init(lrc, q, hwe, vm, replay_state, ring_size, msix_vec, flags);
if (err) {
kfree(lrc);
return ERR_PTR(err);
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index e7c975f9e2d9..b101aabe3d0d 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -51,8 +51,9 @@ struct xe_lrc_snapshot {
#define XE_LRC_CREATE_USER_CTX BIT(2)
#define XE_LRC_DISABLE_STATE_CACHE_PERF_FIX BIT(3)
-struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
- void *replay_state, u32 ring_size, u16 msix_vec, u32 flags);
+struct xe_lrc *xe_lrc_create(struct xe_exec_queue *q, struct xe_hw_engine *hwe,
+ struct xe_vm *vm, void *replay_state,
+ u32 ring_size, u16 msix_vec, u32 flags);
void xe_lrc_destroy(struct kref *ref);
/**
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 14/15] drm/xe/vm: Add xe_vma_supports_access_ctr() helper
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (14 preceding siblings ...)
2026-03-18 7:44 ` [RFC 13/15] drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting Himal Prasad Ghimiray
16 siblings, 0 replies; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Hardware access counter slots are finite resources. Add a helper
to determine whether a VMA is eligible for access counting, so
callers can avoid wasting slots on BOs that cannot benefit from
migration hints.
A VMA is eligible if:
- The device is a discrete GPU (access counters unused on iGPU)
- It is a CPU address mirror (SVM) VMA, which is always migratable
- The backing BO has more than one placement region
- The BO is not already resident in VRAM local to the triggering tile
Userptr and null VMAs are excluded as they have no associated BO.
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/xe_vm.c | 41 ++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_vm.h | 3 +++
2 files changed, 44 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 659504ec5a13..7f979d6205f4 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -4356,6 +4356,47 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
kvfree(snap);
}
+/**
+ * xe_vma_supports_access_ctr - Determine if VMA is eligible for access counting
+ * @xe: Pointer to the Xe device structure
+ * @vma: The VMA
+ * @tile: Tile with which VMA bound is associated to
+ *
+ * Note, access counters are not used unless enabled in LRC.
+ */
+bool xe_vma_supports_access_ctr(struct xe_device *xe,
+ struct xe_vma *vma,
+ struct xe_tile *tile)
+{
+ struct xe_bo *bo = xe_vma_bo(vma);
+ struct ttm_resource *res;
+
+ if (!IS_DGFX(xe))
+ return false;
+
+ if (xe_vma_is_cpu_addr_mirror(vma))
+ return true;
+
+ /* userptr or using null vma*/
+ if (!bo)
+ return false;
+
+ res = bo->ttm.resource;
+ /* if for some reason no backing store, nothing to migrate */
+ if (!res)
+ return false;
+
+ /* cannot migrate if single placement */
+ if (bo->placement.num_placement <= 1)
+ return false;
+
+ /* cannot migrate to ourself (already in VRAM local to @tile) */
+ if (!tile->mem.vram || res->mem_type == tile->mem.vram->placement)
+ return false;
+
+ return true;
+}
+
/**
* xe_vma_need_vram_for_atomic - Check if VMA needs VRAM migration for atomic operations
* @xe: Pointer to the Xe device structure
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index 127cecfcb80b..17844ef0f0b7 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -178,6 +178,9 @@ struct xe_vma *xe_vm_find_vma_by_addr(struct xe_vm *vm, u64 page_addr);
int xe_vma_need_vram_for_atomic(struct xe_device *xe, struct xe_vma *vma, bool is_atomic);
+bool xe_vma_supports_access_ctr(struct xe_device *xe, struct xe_vma *vma,
+ struct xe_tile *tile);
+
int xe_vm_alloc_madvise_vma(struct xe_vm *vm, uint64_t addr, uint64_t size);
int xe_vm_alloc_cpu_addr_mirror_vma(struct xe_vm *vm, uint64_t addr, uint64_t size);
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
` (15 preceding siblings ...)
2026-03-18 7:44 ` [RFC 14/15] drm/xe/vm: Add xe_vma_supports_access_ctr() helper Himal Prasad Ghimiray
@ 2026-03-18 7:44 ` Himal Prasad Ghimiray
2026-04-03 0:09 ` Matthew Brost
16 siblings, 1 reply; 37+ messages in thread
From: Himal Prasad Ghimiray @ 2026-03-18 7:44 UTC (permalink / raw)
To: intel-xe
Cc: matthew.brost, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay, Himal Prasad Ghimiray
Set XE_PPGTT_PTE_NC on PTEs for VMAs that can't benefit from access
counter migration hints, avoiding wasteful slot allocation.
Bspec: 67095
Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
---
drivers/gpu/drm/xe/regs/xe_gtt_defs.h | 2 +-
drivers/gpu/drm/xe/xe_pt.c | 11 +++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
index 4d83461e538b..ace3cbcc13d8 100644
--- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
@@ -28,11 +28,11 @@
#define XE_GGTT_PTE_DM BIT_ULL(1)
#define XE_USM_PPGTT_PTE_AE BIT_ULL(10)
#define XE_PPGTT_PTE_DM BIT_ULL(11)
+#define XE_PPGTT_PTE_NC BIT_ULL(5)
#define XE_PDE_64K BIT_ULL(6)
#define XE_PTE_PS64 BIT_ULL(8)
#define XE_PTE_NULL BIT_ULL(9)
#define XE_PAGE_PRESENT BIT_ULL(0)
#define XE_PAGE_RW BIT_ULL(1)
-
#endif
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 2d9ce2c4cb4f..cc8dcdb6649a 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -755,6 +755,17 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
XE_USM_PPGTT_PTE_AE : 0;
}
+ if (!xe_vma_supports_access_ctr(xe, vma, tile)) {
+ xe_walk.default_vram_pte |= XE_PPGTT_PTE_NC;
+ xe_walk.default_system_pte |= XE_PPGTT_PTE_NC;
+ }
+
+ if (range) {
+ xe_walk.default_vram_pte |= XE_PPGTT_PTE_NC;
+ if (!range->base.pages.flags.migrate_devmem)
+ xe_walk.default_system_pte |= XE_PPGTT_PTE_NC;
+ }
+
xe_walk.default_vram_pte |= XE_PPGTT_PTE_DM;
xe_walk.dma_offset = bo ? vram_region_gpu_offset(bo->ttm.resource) : 0;
if (!range)
--
2.34.1
^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue
2026-03-18 7:44 ` [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue Himal Prasad Ghimiray
@ 2026-03-24 14:25 ` Francois Dugast
2026-04-01 14:46 ` Matthew Brost
0 siblings, 1 reply; 37+ messages in thread
From: Francois Dugast @ 2026-03-24 14:25 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, matthew.brost, thomas.hellstrom, stuart.summers,
arvind.yadav, tejas.upadhyay
Hi Himal,
On Wed, Mar 18, 2026 at 01:14:53PM +0530, Himal Prasad Ghimiray wrote:
> Introduce DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension to allow
> userspace to configure access counter notifications per exec queue.
>
> The extension provides:
> - trigger: Access counter trigger threshold
> - notify: Access counter notify threshold
> - granularity: Access counter granularity level
>
> These parameters control hardware access counter behavior for memory
> access pattern tracking and optimization hints. Userspace can configure
> different thresholds per exec queue based on workload characteristics.
>
> UAPI changes:
> - Add drm_xe_exec_queue_set_acc_param structure
> - Add DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension ID
>
> KMD changes:
> - Add exec_queue_user_ext_set_acc_param() handler
> - Store parameters in xe_exec_queue.acc structure
> - Register handler in extension function table
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/xe_exec_queue.c | 27 ++++++++++++++++
> drivers/gpu/drm/xe/xe_exec_queue_types.h | 9 ++++++
> include/uapi/drm/xe_drm.h | 39 ++++++++++++++++++++++++
> 3 files changed, 75 insertions(+)
>
> diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
> index b287d0e0e60a..815e82011c6d 100644
> --- a/drivers/gpu/drm/xe/xe_exec_queue.c
> +++ b/drivers/gpu/drm/xe/xe_exec_queue.c
> @@ -1080,6 +1080,32 @@ static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properti
> return 0;
> }
>
> +static int exec_queue_user_ext_set_acc_param(struct xe_device *xe,
> + struct xe_exec_queue *q,
> + u64 extension, u64 *properties)
> +{
> + u64 __user *address = u64_to_user_ptr(extension);
> + struct drm_xe_exec_queue_set_acc_param ext;
> + int err;
> +
> + if (!xe->info.has_access_counter)
> + return -EINVAL;
> +
> + err = copy_from_user(&ext, address, sizeof(ext));
> + if (XE_IOCTL_DBG(xe, err))
> + return -EFAULT;
> +
> + if (XE_IOCTL_DBG(xe, ext.pad1 || ext.pad2))
> + return -EINVAL;
> +
> + /* Store access counter parameters in exec queue */
> + q->acc.trigger = ext.trigger;
> + q->acc.notify = ext.notify;
> + q->acc.granularity = ext.granularity;
> +
> + return 0;
> +}
> +
> static int exec_queue_user_ext_set_property(struct xe_device *xe,
> struct xe_exec_queue *q,
> u64 extension, u64 *properties)
> @@ -1123,6 +1149,7 @@ typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
>
> static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
> [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
> + [DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM] = exec_queue_user_ext_set_acc_param,
> };
>
> #define MAX_USER_EXTENSIONS 16
> diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> index 8ce78e0b1d50..3f10e3371e5a 100644
> --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
> +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> @@ -239,6 +239,15 @@ struct xe_exec_queue {
>
> /** @replay_state: GPU hang replay state */
> void *replay_state;
> + /** @acc: Access counter parameters */
> + struct {
> + /** @acc.trigger: Access counter trigger threshold */
> + u16 trigger;
> + /** @acc.notify: Access counter notify threshold */
> + u16 notify;
> + /** @acc.granularity: Access counter granularity */
> + u8 granularity;
> + } acc;
>
> /** @ops: submission backend exec queue operations */
> const struct xe_exec_queue_ops *ops;
> diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
> index f8b2afb20540..19447ba10b1d 100644
> --- a/include/uapi/drm/xe_drm.h
> +++ b/include/uapi/drm/xe_drm.h
> @@ -1337,6 +1337,7 @@ struct drm_xe_vm_bind {
> */
> struct drm_xe_exec_queue_create {
> #define DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY 0
> +#define DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM 1
> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY 0
> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE 1
> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE 2
> @@ -1377,6 +1378,44 @@ struct drm_xe_exec_queue_create {
> __u64 reserved[2];
> };
>
> +/** enum drm_xe_access_counter_granularity - Xe access counter granularity */
> +enum drm_xe_access_counter_granularity {
> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
> + DRM_XE_ACCESS_COUNTER_GRANULARITY_128K,
> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
> + DRM_XE_ACCESS_COUNTER_GRANULARITY_2M,
> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
> + DRM_XE_ACCESS_COUNTER_GRANULARITY_16M,
> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
> + DRM_XE_ACCESS_COUNTER_GRANULARITY_64M,
> +};
> +
> +/**
> + * struct drm_xe_exec_queue_set_acc_param - Access counter parameters extension
> + *
> + * Extension to configure access counter notifications for an exec queue.
> + * Used with DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM.
> + */
Sorry if this is already documented elsewhere but I think we would need more
details about access counters from a user perspective:
- why using them?
- what is different when using an exec queue after setting access counters?
- what is the typical flow, maybe with pseudo code?
- how is @trigger used, what is triggered, on what condition?
- how is @notify used, what is notified, on what condition?
- are notifications going to user space or only to internal and trace?
- what happens if access counters are not supported?
- can access counters be reset? removed?
Thanks,
Francois
> +struct drm_xe_exec_queue_set_acc_param {
> + /** @base: base user extension */
> + struct drm_xe_user_extension base;
> +
> + /** @trigger: Access counter trigger threshold */
> + __u16 trigger;
> +
> + /** @notify: Access counter notify threshold */
> + __u16 notify;
> +
> + /** @granularity: granularity, enum @drm_xe_access_counter_granularity */
> + __u8 granularity;
> +
> + /** @pad1: MBZ */
> + __u8 pad1;
> +
> + /** @pad2: MBZ */
> + __u16 pad2;
> +};
> +
> /**
> * struct drm_xe_exec_queue_destroy - Input of &DRM_IOCTL_XE_EXEC_QUEUE_DESTROY
> */
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue
2026-03-24 14:25 ` Francois Dugast
@ 2026-04-01 14:46 ` Matthew Brost
2026-04-01 16:36 ` Ghimiray, Himal Prasad
0 siblings, 1 reply; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 14:46 UTC (permalink / raw)
To: Francois Dugast
Cc: Himal Prasad Ghimiray, intel-xe, thomas.hellstrom, stuart.summers,
arvind.yadav, tejas.upadhyay
On Tue, Mar 24, 2026 at 03:25:09PM +0100, Francois Dugast wrote:
> Hi Himal,
>
> On Wed, Mar 18, 2026 at 01:14:53PM +0530, Himal Prasad Ghimiray wrote:
> > Introduce DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension to allow
> > userspace to configure access counter notifications per exec queue.
> >
> > The extension provides:
> > - trigger: Access counter trigger threshold
> > - notify: Access counter notify threshold
> > - granularity: Access counter granularity level
> >
> > These parameters control hardware access counter behavior for memory
> > access pattern tracking and optimization hints. Userspace can configure
> > different thresholds per exec queue based on workload characteristics.
> >
> > UAPI changes:
> > - Add drm_xe_exec_queue_set_acc_param structure
> > - Add DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension ID
> >
> > KMD changes:
> > - Add exec_queue_user_ext_set_acc_param() handler
> > - Store parameters in xe_exec_queue.acc structure
> > - Register handler in extension function table
> >
> > Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> > ---
> > drivers/gpu/drm/xe/xe_exec_queue.c | 27 ++++++++++++++++
> > drivers/gpu/drm/xe/xe_exec_queue_types.h | 9 ++++++
> > include/uapi/drm/xe_drm.h | 39 ++++++++++++++++++++++++
> > 3 files changed, 75 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
> > index b287d0e0e60a..815e82011c6d 100644
> > --- a/drivers/gpu/drm/xe/xe_exec_queue.c
> > +++ b/drivers/gpu/drm/xe/xe_exec_queue.c
> > @@ -1080,6 +1080,32 @@ static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properti
> > return 0;
> > }
> >
> > +static int exec_queue_user_ext_set_acc_param(struct xe_device *xe,
> > + struct xe_exec_queue *q,
> > + u64 extension, u64 *properties)
> > +{
> > + u64 __user *address = u64_to_user_ptr(extension);
> > + struct drm_xe_exec_queue_set_acc_param ext;
> > + int err;
> > +
> > + if (!xe->info.has_access_counter)
> > + return -EINVAL;
> > +
> > + err = copy_from_user(&ext, address, sizeof(ext));
> > + if (XE_IOCTL_DBG(xe, err))
> > + return -EFAULT;
> > +
> > + if (XE_IOCTL_DBG(xe, ext.pad1 || ext.pad2))
> > + return -EINVAL;
> > +
> > + /* Store access counter parameters in exec queue */
> > + q->acc.trigger = ext.trigger;
> > + q->acc.notify = ext.notify;
> > + q->acc.granularity = ext.granularity;
> > +
> > + return 0;
> > +}
> > +
> > static int exec_queue_user_ext_set_property(struct xe_device *xe,
> > struct xe_exec_queue *q,
> > u64 extension, u64 *properties)
> > @@ -1123,6 +1149,7 @@ typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
> >
> > static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
> > [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
> > + [DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM] = exec_queue_user_ext_set_acc_param,
> > };
> >
> > #define MAX_USER_EXTENSIONS 16
> > diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> > index 8ce78e0b1d50..3f10e3371e5a 100644
> > --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
> > +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
> > @@ -239,6 +239,15 @@ struct xe_exec_queue {
> >
> > /** @replay_state: GPU hang replay state */
> > void *replay_state;
> > + /** @acc: Access counter parameters */
> > + struct {
> > + /** @acc.trigger: Access counter trigger threshold */
> > + u16 trigger;
> > + /** @acc.notify: Access counter notify threshold */
> > + u16 notify;
> > + /** @acc.granularity: Access counter granularity */
> > + u8 granularity;
> > + } acc;
> >
> > /** @ops: submission backend exec queue operations */
> > const struct xe_exec_queue_ops *ops;
> > diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
> > index f8b2afb20540..19447ba10b1d 100644
> > --- a/include/uapi/drm/xe_drm.h
> > +++ b/include/uapi/drm/xe_drm.h
> > @@ -1337,6 +1337,7 @@ struct drm_xe_vm_bind {
> > */
> > struct drm_xe_exec_queue_create {
> > #define DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY 0
> > +#define DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM 1
> > #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY 0
> > #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE 1
> > #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE 2
> > @@ -1377,6 +1378,44 @@ struct drm_xe_exec_queue_create {
> > __u64 reserved[2];
> > };
> >
> > +/** enum drm_xe_access_counter_granularity - Xe access counter granularity */
> > +enum drm_xe_access_counter_granularity {
> > + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
> > + DRM_XE_ACCESS_COUNTER_GRANULARITY_128K,
> > + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
> > + DRM_XE_ACCESS_COUNTER_GRANULARITY_2M,
> > + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
> > + DRM_XE_ACCESS_COUNTER_GRANULARITY_16M,
> > + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
> > + DRM_XE_ACCESS_COUNTER_GRANULARITY_64M,
> > +};
> > +
> > +/**
> > + * struct drm_xe_exec_queue_set_acc_param - Access counter parameters extension
> > + *
> > + * Extension to configure access counter notifications for an exec queue.
> > + * Used with DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM.
> > + */
>
> Sorry if this is already documented elsewhere but I think we would need more
> details about access counters from a user perspective:
>
> - why using them?
> - what is different when using an exec queue after setting access counters?
> - what is the typical flow, maybe with pseudo code?
> - how is @trigger used, what is triggered, on what condition?
> - how is @notify used, what is notified, on what condition?
> - are notifications going to user space or only to internal and trace?
> - what happens if access counters are not supported?
> - can access counters be reset? removed?
+1 to Francois comments. I never really understood the i915 access
counter interfaces on exec queues and without an explaination like
Francois is suggesting, I don't unerstand Xe's uAPI without reverse
engineering the code.
Matt
>
> Thanks,
> Francois
>
> > +struct drm_xe_exec_queue_set_acc_param {
> > + /** @base: base user extension */
> > + struct drm_xe_user_extension base;
> > +
> > + /** @trigger: Access counter trigger threshold */
> > + __u16 trigger;
> > +
> > + /** @notify: Access counter notify threshold */
> > + __u16 notify;
> > +
> > + /** @granularity: granularity, enum @drm_xe_access_counter_granularity */
> > + __u8 granularity;
> > +
> > + /** @pad1: MBZ */
> > + __u8 pad1;
> > +
> > + /** @pad2: MBZ */
> > + __u16 pad2;
> > +};
> > +
> > /**
> > * struct drm_xe_exec_queue_destroy - Input of &DRM_IOCTL_XE_EXEC_QUEUE_DESTROY
> > */
> > --
> > 2.34.1
> >
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue
2026-04-01 14:46 ` Matthew Brost
@ 2026-04-01 16:36 ` Ghimiray, Himal Prasad
0 siblings, 0 replies; 37+ messages in thread
From: Ghimiray, Himal Prasad @ 2026-04-01 16:36 UTC (permalink / raw)
To: Matthew Brost, Francois Dugast
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On 01-04-2026 20:16, Matthew Brost wrote:
> On Tue, Mar 24, 2026 at 03:25:09PM +0100, Francois Dugast wrote:
>> Hi Himal,
>>
>> On Wed, Mar 18, 2026 at 01:14:53PM +0530, Himal Prasad Ghimiray wrote:
>>> Introduce DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension to allow
>>> userspace to configure access counter notifications per exec queue.
>>>
>>> The extension provides:
>>> - trigger: Access counter trigger threshold
>>> - notify: Access counter notify threshold
>>> - granularity: Access counter granularity level
>>>
>>> These parameters control hardware access counter behavior for memory
>>> access pattern tracking and optimization hints. Userspace can configure
>>> different thresholds per exec queue based on workload characteristics.
>>>
>>> UAPI changes:
>>> - Add drm_xe_exec_queue_set_acc_param structure
>>> - Add DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM extension ID
>>>
>>> KMD changes:
>>> - Add exec_queue_user_ext_set_acc_param() handler
>>> - Store parameters in xe_exec_queue.acc structure
>>> - Register handler in extension function table
>>>
>>> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
>>> ---
>>> drivers/gpu/drm/xe/xe_exec_queue.c | 27 ++++++++++++++++
>>> drivers/gpu/drm/xe/xe_exec_queue_types.h | 9 ++++++
>>> include/uapi/drm/xe_drm.h | 39 ++++++++++++++++++++++++
>>> 3 files changed, 75 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
>>> index b287d0e0e60a..815e82011c6d 100644
>>> --- a/drivers/gpu/drm/xe/xe_exec_queue.c
>>> +++ b/drivers/gpu/drm/xe/xe_exec_queue.c
>>> @@ -1080,6 +1080,32 @@ static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properti
>>> return 0;
>>> }
>>>
>>> +static int exec_queue_user_ext_set_acc_param(struct xe_device *xe,
>>> + struct xe_exec_queue *q,
>>> + u64 extension, u64 *properties)
>>> +{
>>> + u64 __user *address = u64_to_user_ptr(extension);
>>> + struct drm_xe_exec_queue_set_acc_param ext;
>>> + int err;
>>> +
>>> + if (!xe->info.has_access_counter)
>>> + return -EINVAL;
>>> +
>>> + err = copy_from_user(&ext, address, sizeof(ext));
>>> + if (XE_IOCTL_DBG(xe, err))
>>> + return -EFAULT;
>>> +
>>> + if (XE_IOCTL_DBG(xe, ext.pad1 || ext.pad2))
>>> + return -EINVAL;
>>> +
>>> + /* Store access counter parameters in exec queue */
>>> + q->acc.trigger = ext.trigger;
>>> + q->acc.notify = ext.notify;
>>> + q->acc.granularity = ext.granularity;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> static int exec_queue_user_ext_set_property(struct xe_device *xe,
>>> struct xe_exec_queue *q,
>>> u64 extension, u64 *properties)
>>> @@ -1123,6 +1149,7 @@ typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
>>>
>>> static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
>>> [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
>>> + [DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM] = exec_queue_user_ext_set_acc_param,
>>> };
>>>
>>> #define MAX_USER_EXTENSIONS 16
>>> diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
>>> index 8ce78e0b1d50..3f10e3371e5a 100644
>>> --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
>>> +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
>>> @@ -239,6 +239,15 @@ struct xe_exec_queue {
>>>
>>> /** @replay_state: GPU hang replay state */
>>> void *replay_state;
>>> + /** @acc: Access counter parameters */
>>> + struct {
>>> + /** @acc.trigger: Access counter trigger threshold */
>>> + u16 trigger;
>>> + /** @acc.notify: Access counter notify threshold */
>>> + u16 notify;
>>> + /** @acc.granularity: Access counter granularity */
>>> + u8 granularity;
>>> + } acc;
>>>
>>> /** @ops: submission backend exec queue operations */
>>> const struct xe_exec_queue_ops *ops;
>>> diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
>>> index f8b2afb20540..19447ba10b1d 100644
>>> --- a/include/uapi/drm/xe_drm.h
>>> +++ b/include/uapi/drm/xe_drm.h
>>> @@ -1337,6 +1337,7 @@ struct drm_xe_vm_bind {
>>> */
>>> struct drm_xe_exec_queue_create {
>>> #define DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY 0
>>> +#define DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM 1
>>> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY 0
>>> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE 1
>>> #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE 2
>>> @@ -1377,6 +1378,44 @@ struct drm_xe_exec_queue_create {
>>> __u64 reserved[2];
>>> };
>>>
>>> +/** enum drm_xe_access_counter_granularity - Xe access counter granularity */
>>> +enum drm_xe_access_counter_granularity {
>>> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
>>> + DRM_XE_ACCESS_COUNTER_GRANULARITY_128K,
>>> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
>>> + DRM_XE_ACCESS_COUNTER_GRANULARITY_2M,
>>> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
>>> + DRM_XE_ACCESS_COUNTER_GRANULARITY_16M,
>>> + /** @DRM_XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
>>> + DRM_XE_ACCESS_COUNTER_GRANULARITY_64M,
>>> +};
>>> +
>>> +/**
>>> + * struct drm_xe_exec_queue_set_acc_param - Access counter parameters extension
>>> + *
>>> + * Extension to configure access counter notifications for an exec queue.
>>> + * Used with DRM_XE_EXEC_QUEUE_EXTENSION_SET_ACC_PARAM.
>>> + */
>>
>> Sorry if this is already documented elsewhere but I think we would need more
>> details about access counters from a user perspective:
>>
>> - why using them?
>> - what is different when using an exec queue after setting access counters?
>> - what is the typical flow, maybe with pseudo code?
>> - how is @trigger used, what is triggered, on what condition?
>> - how is @notify used, what is notified, on what condition?
>> - are notifications going to user space or only to internal and trace?
>> - what happens if access counters are not supported?
>> - can access counters be reset? removed?
>
> +1 to Francois comments. I never really understood the i915 access
> counter interfaces on exec queues and without an explaination like
> Francois is suggesting, I don't unerstand Xe's uAPI without reverse
> engineering the code.
>
> Matt
Hi Francois and Matt,
Thanks for pointing it out. Will add a proper documentation explaining
the use case and all the details in future revisions.
>
>>
>> Thanks,
>> Francois
>>
>>> +struct drm_xe_exec_queue_set_acc_param {
>>> + /** @base: base user extension */
>>> + struct drm_xe_user_extension base;
>>> +
>>> + /** @trigger: Access counter trigger threshold */
>>> + __u16 trigger;
>>> +
>>> + /** @notify: Access counter notify threshold */
>>> + __u16 notify;
>>> +
>>> + /** @granularity: granularity, enum @drm_xe_access_counter_granularity */
>>> + __u8 granularity;
>>> +
>>> + /** @pad1: MBZ */
>>> + __u8 pad1;
>>> +
>>> + /** @pad2: MBZ */
>>> + __u16 pad2;
>>> +};
>>> +
>>> /**
>>> * struct drm_xe_exec_queue_destroy - Input of &DRM_IOCTL_XE_EXEC_QUEUE_DESTROY
>>> */
>>> --
>>> 2.34.1
>>>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
@ 2026-04-01 21:10 ` Matthew Brost
2026-04-01 22:01 ` Matthew Brost
2026-04-01 22:11 ` Matthew Brost
2026-04-02 22:06 ` Matthew Brost
2 siblings, 1 reply; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 21:10 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
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>
Not a complete review - that will take a bit more time but one quick
comment below.
> ---
> 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 */
> + 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);
> + }
You can move the dma_fence_wait()/put outside all of the locks taken here.
xe_pagefault_handle_vma() also unnecessarily waits under locks, but that
will be refactored a bit in [1]. Even there, I should probably move the
dma_fence_wait() completely outside vm->lock, which is held in read mode
after [1].
Best to get the semantics of this new layer right upfront.
Matt
[1] https://patchwork.freedesktop.org/patch/707294/?series=162167&rev=4
> +
> + 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
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer
2026-03-18 7:44 ` [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer Himal Prasad Ghimiray
@ 2026-04-01 21:28 ` Matthew Brost
2026-04-06 4:46 ` Ghimiray, Himal Prasad
0 siblings, 1 reply; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 21:28 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:42PM +0530, Himal Prasad Ghimiray wrote:
> Introduce struct xe_usm_queue, a generic lock-protected circular FIFO
> used by USM consumers (page fault, access counter) to receive fixed-size
> entries from IRQ context and process them via a work_struct.
>
> Provide three inline helpers in xe_usm_queue.h:
> xe_usm_queue_full() - CIRC_SPACE check (caller holds lock)
> xe_usm_queue_pop() - dequeue one entry (acquires lock internally)
> xe_usm_queue_push() - enqueue one entry (caller holds lock)
>
> Suggested-by: Matthew Brost <matthew.brost@intel.com>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
I think this is useful, but right after I suggested it, I changed the
semantics of the page-fault circular buffer to be a single buffer [1]
that multiple workers pull from. This eliminates HoQ blocking (e.g.,
five access events are pushed; the first one takes a really long time,
but the other four are much faster—yet the fifth event gets stuck behind
the long-running one). So maybe start with a single-queue +
multiple-workers-pulling design here? This likely matters considerably
less than the page-fault cases, but overall I think it’s a better
structure.
Also, maybe the xe_usm_queue can be reused by page faults, but the
management of that queue actually gets more convoluted in [2] to
implement the page-fault cache that greatly speeds up storms of faults.
So maybe drop the second patch in the series for now to avoid conflicts,
and if we can converge on a shared xe_usm_queue implementation later,
great..
Matt
[1] https://patchwork.freedesktop.org/patch/707293/?series=162167&rev=4
[2] https://patchwork.freedesktop.org/patch/707300/?series=162167&rev=4
> ---
> drivers/gpu/drm/xe/xe_usm_queue.h | 100 ++++++++++++++++++++++++++++++
> 1 file changed, 100 insertions(+)
> create mode 100644 drivers/gpu/drm/xe/xe_usm_queue.h
>
> diff --git a/drivers/gpu/drm/xe/xe_usm_queue.h b/drivers/gpu/drm/xe/xe_usm_queue.h
> new file mode 100644
> index 000000000000..5dc2d692d8bb
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_usm_queue.h
> @@ -0,0 +1,100 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#ifndef _XE_USM_QUEUE_H_
> +#define _XE_USM_QUEUE_H_
> +
> +#include <linux/circ_buf.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
> +
> +/**
> + * struct xe_usm_queue - Generic USM circular FIFO queue
> + *
> + * A lock-protected circular buffer used by both the page fault consumer and
> + * the access counter consumer. Producers push fixed-size entries from IRQ
> + * context; a work_struct processes them asynchronously.
> + */
> +struct xe_usm_queue {
> + /**
> + * @data: Raw byte buffer backing the queue, protected by @lock
> + */
> + void *data;
> + /** @size: Total size of @data in bytes */
> + u32 size;
> + /** @head: Write cursor in bytes, moved by producer, protected by @lock */
> + u32 head;
> + /** @tail: Read cursor in bytes, moved by consumer, protected by @lock */
> + u32 tail;
> + /** @lock: Protects the queue head/tail */
> + spinlock_t lock;
> + /** @worker: Work item scheduled to drain the queue */
> + struct work_struct worker;
> +};
> +
> +/**
> + * xe_usm_queue_full - Check whether the queue has no room for one more entry
> + * @q: queue
> + * @size: exact size of one entry in bytes (sizeof the entry struct)
> + *
> + * Must be called with @q->lock held.
> + *
> + * Return: true if the queue is full
> + */
> +static inline bool xe_usm_queue_full(struct xe_usm_queue *q, u32 size)
> +{
> + u32 entry_size = roundup_pow_of_two(size);
> +
> + lockdep_assert_held(&q->lock);
> +
> + return CIRC_SPACE(q->head, q->tail, q->size) <= entry_size;
> +}
> +/**
> + * xe_usm_queue_pop - Pop one entry from the queue into @out
> + * @q: queue
> + * @out: destination buffer, must be at least @size bytes
> + * @size: exact size of one entry in bytes (sizeof the entry struct)
> + *
> + * Acquires @q->lock internally with spin_lock_irq().
> + *
> + * Return: true if an entry was dequeued, false if the queue was empty
> + */
> +static inline bool xe_usm_queue_pop(struct xe_usm_queue *q, void *out,
> + u32 size)
> +{
> + u32 entry_size = roundup_pow_of_two(size);
> + bool found = false;
> +
> + spin_lock_irq(&q->lock);
> + if (q->tail != q->head) {
> + memcpy(out, q->data + q->tail, size);
> + q->tail = (q->tail + entry_size) % q->size;
> + found = true;
> + }
> + spin_unlock_irq(&q->lock);
> +
> + return found;
> +}
> +
> +/**
> + * xe_usm_queue_push - Push one entry into the queue
> + * @q: queue
> + * @in: source buffer, must be at least @size bytes
> + * @size: size of one entry of in bytes
> + *
> + * Must be called with @q->lock held.
> + * Caller must check xe_usm_queue_full() before calling.
> + */
> +static inline void xe_usm_queue_push(struct xe_usm_queue *q, const void *in,
> + u32 size)
> +{
> + u32 entry_size = roundup_pow_of_two(size);
> +
> + lockdep_assert_held(&q->lock);
> +
> + memcpy(q->data + q->head, in, size);
> + q->head = (q->head + entry_size) % q->size;
> +}
> +#endif
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work
2026-04-01 21:10 ` Matthew Brost
@ 2026-04-01 22:01 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 22:01 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Apr 01, 2026 at 02:10:35PM -0700, Matthew Brost wrote:
> 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>
>
> Not a complete review - that will take a bit more time but one quick
> comment below.
>
Sorry more thoughts in a stream.
> > ---
> > 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 */
> > + 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);
> > + }
>
> You can move the dma_fence_wait()/put outside all of the locks taken here.
> xe_pagefault_handle_vma() also unnecessarily waits under locks, but that
> will be refactored a bit in [1]. Even there, I should probably move the
> dma_fence_wait() completely outside vm->lock, which is held in read mode
> after [1].
>
> Best to get the semantics of this new layer right upfront.
>
Actually, now that I think about it, you never need to wait here, given
that migrations and binds are fully pipelined operations and we don’t
ack an access-counter event with an H2G.
What will race is a possible page fault while we’re moving the VMA’s
memory. So I think we should just attach a fence to the VMA (under
vm->lock in write mode for now), which the page-fault handler can wait
on before the xe_vm_has_valid_gpu_mapping check in
xe_pagefault_handle_vma(). You can drop the fence there, or if a VMA
still has a fence when it is destroyed, drop the reference at that
point. This should be a relatively small amount of memory (for the
fence), so if we don’t free it for a while, I don’t think it’s a big
deal..
Another hazard is when multiple access-counter events map to the same
VMA, so if an unsignaled ‘access-counter move’ fence is found, we should
simply drop the access-counter event.
> Matt
>
> [1] https://patchwork.freedesktop.org/patch/707294/?series=162167&rev=4
>
> > +
> > + 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;
> > +
So here - as discussed [1], let's have a single queue /w multiple workers
pulling access counters events.
[1] https://patchwork.freedesktop.org/patch/712591/?series=163429&rev=1
> > + 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);
I think scheduling access-counter events on gt_to_xe(ac.gt)->usm.pf_wq
is correct, but since this is shared with page faults—and those are
always higher priority—we should service at most one access-counter
event here to give page-fault work items a chance to jump the line.
So maybe something like this instead of a while loop:
if (xe_usm_queue_peak())
queue_work();
Matt
> > + break;
> > + }
> > + }
> > +#undef USM_QUEUE_MAX_RUNTIME_MS
> > }
> >
> > static int xe_access_counter_queue_init(struct xe_device *xe,
> > --
> > 2.34.1
> >
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper
2026-03-18 7:44 ` [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper Himal Prasad Ghimiray
@ 2026-04-01 22:03 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 22:03 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:47PM +0530, Himal Prasad Ghimiray wrote:
> Move xe_pagefault_begin to xe_vm.c as xe_vma_lock_and_validate for reuse
> in access counter processing.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
> ---
> drivers/gpu/drm/xe/xe_pagefault.c | 22 ++--------------------
> drivers/gpu/drm/xe/xe_vm.c | 31 +++++++++++++++++++++++++++++++
> drivers/gpu/drm/xe/xe_vm.h | 3 +++
> 3 files changed, 36 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
> index 67dd07927684..8c159e3512ee 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
> @@ -45,24 +45,6 @@ static int xe_pagefault_entry_size(void)
> return roundup_pow_of_two(sizeof(struct xe_pagefault));
> }
>
> -static int xe_pagefault_begin(struct drm_exec *exec, struct xe_vma *vma,
> - struct xe_vram_region *vram, bool need_vram_move)
> -{
> - struct xe_bo *bo = xe_vma_bo(vma);
> - struct xe_vm *vm = xe_vma_vm(vma);
> - int err;
> -
> - err = xe_vm_lock_vma(exec, vma);
> - if (err)
> - return err;
> -
> - if (!bo)
> - return 0;
> -
> - return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
> - xe_bo_validate(bo, vm, true, exec);
> -}
> -
> static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma,
> bool atomic)
> {
> @@ -103,8 +85,8 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma,
> /* 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_pagefault_begin(&exec, vma, tile->mem.vram,
> - needs_vram == 1);
> + err = xe_vma_lock_and_validate(&exec, vma, tile->mem.vram,
> + needs_vram == 1);
> drm_exec_retry_on_contention(&exec);
> xe_validation_retry_on_oom(&ctx, &err);
> if (err)
> diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
> index 5572e12c2a7e..659504ec5a13 100644
> --- a/drivers/gpu/drm/xe/xe_vm.c
> +++ b/drivers/gpu/drm/xe/xe_vm.c
> @@ -1170,6 +1170,37 @@ int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma)
> return err;
> }
>
> +/**
> + * xe_vma_lock_and_validate - Lock Vma and Validate bo location
> + * @exec: drm execution context
> + * @vma: VMA to prepare
> + * @vram: target VRAM region
> + * @need_vram_move: true if BO must be moved to VRAM
> + *
> + * Locks the VMA and its associated BO, then ensures the BO is in the correct
> + * memory location for GPU access. If need_vram_move is true, migrates the BO
> + * to VRAM; otherwise validates it in its current location.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +int xe_vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
> + struct xe_vram_region *vram, bool need_vram_move)
> +{
> + struct xe_bo *bo = xe_vma_bo(vma);
> + struct xe_vm *vm = xe_vma_vm(vma);
> + int err;
> +
> + err = xe_vm_lock_vma(exec, vma);
> + if (err)
> + return err;
> +
> + if (!bo)
> + return 0;
> +
> + return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
> + xe_bo_validate(bo, vm, true, exec);
> +}
> +
> static void xe_vma_destroy_unlocked(struct xe_vma *vma)
> {
> struct xe_device *xe = xe_vma_vm(vma)->xe;
> diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
> index 0bc7ed23eeae..127cecfcb80b 100644
> --- a/drivers/gpu/drm/xe/xe_vm.h
> +++ b/drivers/gpu/drm/xe/xe_vm.h
> @@ -271,6 +271,9 @@ static inline void xe_vm_reactivate_rebind(struct xe_vm *vm)
>
> int xe_vm_lock_vma(struct drm_exec *exec, struct xe_vma *vma);
>
> +int xe_vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
> + struct xe_vram_region *vram, bool need_vram_move);
> +
> int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
> unsigned int num_fences);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
2026-04-01 21:10 ` Matthew Brost
@ 2026-04-01 22:11 ` Matthew Brost
2026-04-02 22:06 ` Matthew Brost
2 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-01 22:11 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
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
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 11/15] drm/xe: Add xe_guc_access_counter layer
2026-03-18 7:44 ` [RFC 11/15] drm/xe: Add xe_guc_access_counter layer Himal Prasad Ghimiray
@ 2026-04-02 21:27 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-02 21:27 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:52PM +0530, Himal Prasad Ghimiray wrote:
> Add GuC to host (G2H) access counter notification handler to parse
> GuC firmware messages into struct xe_access_counter and forward to
> xe_access_counter_handler for processing.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/Makefile | 1 +
> drivers/gpu/drm/xe/xe_guc_access_counter.c | 62 ++++++++++++++++++++++
> drivers/gpu/drm/xe/xe_guc_access_counter.h | 15 ++++++
> drivers/gpu/drm/xe/xe_guc_ct.c | 4 ++
> 4 files changed, 82 insertions(+)
> create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.c
> create mode 100644 drivers/gpu/drm/xe/xe_guc_access_counter.h
>
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 92d8d6e4a447..296b3cba0b89 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -74,6 +74,7 @@ xe-y += xe_access_counter.o \
> xe_guc_id_mgr.o \
> xe_guc_klv_helpers.o \
> xe_guc_log.o \
> + xe_guc_access_counter.o \
> xe_guc_pagefault.o \
> xe_guc_pc.o \
> xe_guc_rc.o \
> diff --git a/drivers/gpu/drm/xe/xe_guc_access_counter.c b/drivers/gpu/drm/xe/xe_guc_access_counter.c
> new file mode 100644
> index 000000000000..2158400bc50a
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_guc_access_counter.c
> @@ -0,0 +1,62 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#include "xe_guc_access_counter.h"
> +
> +#include "xe_access_counter.h"
> +#include "xe_device.h"
> +#include "xe_gt.h"
> +#include "xe_guc.h"
> +#include "xe_guc_fwif.h"
> +
> +/**
> + * xe_guc_access_counter_handler() - G2H access counter handler
> + * @guc: GuC object
> + * @msg: G2H message
> + * @len: Length of G2H message
> + *
> + * Parse GuC to host (G2H) message into a struct xe_access_counter and forward
> + * onto the Xe access counter layer.
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +int xe_guc_access_counter_handler(struct xe_guc *guc, u32 *msg, u32 len)
> +{
> + struct xe_access_counter ac;
> + struct xe_device *xe = guc_to_xe(guc);
> + int i;
> +
> +#define GUC_ACC_MSG_LEN_DW \
> + (sizeof(struct xe_guc_acc_desc) / sizeof(u32))
> +
> + BUILD_BUG_ON(GUC_ACC_MSG_LEN_DW > XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW);
> +
> + if (len != GUC_ACC_MSG_LEN_DW)
> + return -EPROTO;
> +
> + ac.gt = guc_to_gt(guc);
> +
> + /* Parse access counter descriptor */
> + ac.consumer.granularity = FIELD_GET(ACC_GRANULARITY, msg[2]);
> + ac.consumer.sub_granularity = FIELD_GET(ACC_SUBG_HI, msg[1]) << 31 |
> + FIELD_GET(ACC_SUBG_LO, msg[0]);
Would it be better to move the math in xe_access_counter_get_vma() for
calculating page_va into this layer and simply pass in page_va (and
perhaps a generic length)? That would better isolate the details of the
GuC access counter interface. It would also ensure that, if we wire up a
different hardware access counter interface in the future, the common
access counter layer can remain unchanged.
> + ac.consumer.counter_type = FIELD_GET(ACC_TYPE, msg[0]);
Should we reject bad count types here? I think that makes more sense.
The only reason we don’t reject bad page faults in the G2H handler is
because page faults issue an ACK, whereas we don’t ACK access counters.
> + ac.consumer.va_range_base = ((u64)(msg[3] & ACC_VIRTUAL_ADDR_RANGE_HI) << 32) |
> + (msg[2] & ACC_VIRTUAL_ADDR_RANGE_LO);
> + /* xe3: Use ASID and engine info */
> + ac.consumer.xe3.asid = FIELD_GET(ACC_ASID, msg[1]);
> + ac.consumer.xe3.engine_class = FIELD_GET(ACC_ENG_CLASS, msg[1]);
> + ac.consumer.xe3.engine_instance = FIELD_GET(ACC_ENG_INSTANCE, msg[1]);
> + ac.consumer.xe3.vfid = FIELD_GET(ACC_VFID, msg[2]);
> +
> + /* Store producer message for potential acknowledgment */
> + ac.producer.private = guc;
> + for (i = 0; i < GUC_ACC_MSG_LEN_DW; ++i)
> + ac.producer.msg[i] = msg[i];
Do we need this? That is, we don’t currently ACK access counters—are
there plans to change this? If there no immediate plans, I'd drop this
for now.
Matt
> +
> +#undef GUC_ACC_MSG_LEN_DW
> +
> + return xe_access_counter_handler(xe, &ac);
> +}
> diff --git a/drivers/gpu/drm/xe/xe_guc_access_counter.h b/drivers/gpu/drm/xe/xe_guc_access_counter.h
> new file mode 100644
> index 000000000000..1ac8e76398d2
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_guc_access_counter.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#ifndef _XE_GUC_ACCESS_COUNTER_H_
> +#define _XE_GUC_ACCESS_COUNTER_H_
> +
> +#include <linux/types.h>
> +
> +struct xe_guc;
> +
> +int xe_guc_access_counter_handler(struct xe_guc *guc, u32 *msg, u32 len);
> +
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
> index a11cff7a20be..8ac0938f7a28 100644
> --- a/drivers/gpu/drm/xe/xe_guc_ct.c
> +++ b/drivers/gpu/drm/xe/xe_guc_ct.c
> @@ -26,6 +26,7 @@
> #include "xe_gt_sriov_pf_monitor.h"
> #include "xe_guc.h"
> #include "xe_guc_log.h"
> +#include "xe_guc_access_counter.h"
> #include "xe_guc_pagefault.h"
> #include "xe_guc_relay.h"
> #include "xe_guc_submit.h"
> @@ -1630,6 +1631,9 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
> case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC:
> ret = xe_guc_pagefault_handler(guc, payload, adj_len);
> break;
> + case XE_GUC_ACTION_ACCESS_COUNTER_NOTIFY:
> + ret = xe_guc_access_counter_handler(guc, payload, adj_len);
> + break;
> case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
> ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
> break;
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 03/15] drm/xe: Stub out new access_counter layer
2026-03-18 7:44 ` [RFC 03/15] drm/xe: Stub out new access_counter layer Himal Prasad Ghimiray
@ 2026-04-02 21:46 ` Matthew Brost
2026-04-06 5:28 ` Ghimiray, Himal Prasad
0 siblings, 1 reply; 37+ messages in thread
From: Matthew Brost @ 2026-04-02 21:46 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:44PM +0530, Himal Prasad Ghimiray wrote:
> Add access counter infrastructure with type definitions, header files,
> and stub implementation. This follows a two-layer producer-consumer
> architecture similar to the pagefault layer.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/Makefile | 3 +-
> drivers/gpu/drm/xe/xe_access_counter.c | 55 +++++++++
> drivers/gpu/drm/xe/xe_access_counter.h | 17 +++
> drivers/gpu/drm/xe/xe_access_counter_types.h | 123 +++++++++++++++++++
> 4 files changed, 197 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.c
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.h
> create mode 100644 drivers/gpu/drm/xe/xe_access_counter_types.h
>
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 3a3f9f22d42a..92d8d6e4a447 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -32,7 +32,8 @@ $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe
>
> # core driver code
>
> -xe-y += xe_bb.o \
> +xe-y += xe_access_counter.o \
> + xe_bb.o \
> xe_bo.o \
> xe_bo_evict.o \
> xe_dep_scheduler.o \
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> new file mode 100644
> index 000000000000..f3a8a93b5135
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#include <linux/circ_buf.h>
> +
> +#include <drm/drm_exec.h>
> +#include <drm/drm_managed.h>
> +
> +#include "xe_access_counter.h"
> +#include "xe_access_counter_types.h"
> +#include "xe_device.h"
> +
> +/**
> + * DOC: Xe access counters
> + *
> + * Xe access counters are handled in two layers with one-way communication.
> + * The producer layer interacts with hardware or firmware to receive and parse
> + * access counter notifications into struct xe_access_counter, then forwards them
> + * to the consumer. The consumer layer services the notifications (e.g., memory
> + * migration hints, binding decisions). No acknowledgment is sent back to the
> + * producer. The consumer uses an access counter queue sized to absorb all potential
> + * notifications and a multi-threaded worker to process them. Multiple producers
> + * are supported, with a single shared consumer.
> + *
> + * xe_access_counter.c implements the consumer layer.
> + */
> +
> +/**
> + * xe_access_counter_init - Initialize access counter consumer layer
> + * @xe: xe device
> + *
> + * Return: 0 on success, negative error code on error
> + */
> +int xe_access_counter_init(struct xe_device *xe)
> +{
> + /* Stub implementation - to be filled in */
> + return 0;
> +}
> +
> +/**
> + * xe_access_counter_handler - Handle an access counter notification
> + * @xe: xe device
> + * @ac: access counter notification
> + *
> + * Process an access counter notification from the producer layer.
> + *
> + * Return: 0 on success, negative error code on error
> + */
> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
> +{
> + /* Stub implementation - to be filled in */
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.h b/drivers/gpu/drm/xe/xe_access_counter.h
> new file mode 100644
> index 000000000000..b3a331687f13
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#ifndef _XE_ACCESS_COUNTER_H_
> +#define _XE_ACCESS_COUNTER_H_
> +
> +struct xe_device;
> +struct xe_gt;
> +struct xe_access_counter;
> +
> +int xe_access_counter_init(struct xe_device *xe);
> +
> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac);
> +
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_access_counter_types.h b/drivers/gpu/drm/xe/xe_access_counter_types.h
> new file mode 100644
> index 000000000000..74b903b9461b
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_access_counter_types.h
> @@ -0,0 +1,123 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Intel Corporation
> + */
> +
> +#ifndef _XE_ACCESS_COUNTER_TYPES_H_
> +#define _XE_ACCESS_COUNTER_TYPES_H_
> +
> +#include <linux/sizes.h>
> +#include <linux/types.h>
> +
> +struct xe_gt;
> +struct xe_access_counter;
> +
> +/** enum xe_access_counter_type - Xe access counter type */
> +enum xe_access_counter_type {
> + /** @XE_ACCESS_COUNTER_TYPE_TRIGGER */
> + XE_ACCESS_COUNTER_TYPE_TRIGGER = 0,
> + /** @XE_ACCESS_COUNTER_TYPE_NOTIFY*/
> + XE_ACCESS_COUNTER_TYPE_NOTIFY = 1,
> +};
What’s the difference between the types? Patch 8 handles both of them in
exactly the same way, which may be correct or fine, but without kernel
documentation explaining the distinction between the types here, it’s
hard to reason about it.
> +
> +/** enum xe_access_counter_granularity - Xe access counter granularity */
> +enum xe_access_counter_granularity {
> + /** @XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_128K = 0,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_2M = 1,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_16M = 2,
> + /** @XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
> + XE_ACCESS_COUNTER_GRANULARITY_64M = 3,
> +};
> +
> +static inline int xe_access_counter_granularity_in_byte(int val)
> +{
> + switch (val) {
> + case XE_ACCESS_COUNTER_GRANULARITY_128K:
> + return SZ_128K;
> + case XE_ACCESS_COUNTER_GRANULARITY_2M:
> + return SZ_2M;
> + case XE_ACCESS_COUNTER_GRANULARITY_16M:
> + return SZ_16M;
> + case XE_ACCESS_COUNTER_GRANULARITY_64M:
> + return SZ_64M;
> + default:
> + return 0;
> + }
> +}
As discussed in [1], I think the granularity handling can be moved into
the GuC layer, so there’s no need to include public definitions here.
[1] https://patchwork.freedesktop.org/patch/712600/?series=163429&rev=1#comment_1318525
> +
> +/**
> + * struct xe_access_counter - Xe access counter
> + *
> + * Generic access counter structure for communication between producer and consumer.
> + * Carefully sized to be 64 bytes. Upon a device access counter notification, the
> + * producer populates this structure, and the consumer copies it into the access
> + * counter queue for deferred handling.
> + */
> +struct xe_access_counter {
> + /**
> + * @gt: GT of access counter
> + */
> + struct xe_gt *gt;
> + /**
> + * @consumer: State for the software handling the access counter.
> + * Populated by the producer and may be modified by the consumer to
> + * communicate information back to the producer upon acknowledgment.
> + */
> + struct {
> + /** @consumer.va_range_base: base virtual address of range */
> + u64 va_range_base;
> + /** @consumer.sub_granularity: sub-granularity */
> + u32 sub_granularity;
> + /**
> + * @consumer.counter_type: counter type, u8 rather than enum to
> + * keep size compact
> + */
> + u8 counter_type;
> + /**
> + * @consumer.granularity: access granularity, u8 rather than enum
> + * to keep size compact
> + */
> + u8 granularity;
> + /** @consumer.reserved: reserved bits for alignment */
> + u8 reserved[2];
> + /** @consumer: Platform-specific fields */
> + union {
> + /** @consumer.xe3: Xe3-specific fields */
> + struct {
> + /** @consumer.xe3.asid: address space ID */
> + u32 asid;
> + /** @consumer.xe3.engine_class: engine class */
> + u8 engine_class;
> + /** @consumer.xe3.engine_instance: engine instance */
> + u8 engine_instance;
> + /** @consumer.xe3.vfid: VFID */
> + u8 vfid;
> + /** @consumer.xe3.reserved: reserved bits */
> + u8 reserved;
> + } xe3;
> + };
Can we drop the xe3 prefix here to keep this interface generic across
platforms?
Also, a question: xe3 implies these are only valid on xe3+, so do access
counters not work on prior platforms? For example, without a valid ASID,
the common layer can’t find a VMA.
> + } consumer;
> + /**
> + * @producer: State for the producer (i.e., HW/FW interface). Populated
> + * by the producer and should not be modified—or even inspected—by the
> + * consumer, except for calling operations.
> + */
> + struct {
> + /** @producer.private: private pointer */
> + void *private;
> +#define XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW 4
> + /**
> + * @producer.msg: access counter message, used by producer in
> + * acknowledgment to formulate response to HW/FW interface.
> + * Included in the access counter message because the producer
> + * typically receives the notification in a context where memory
> + * cannot be allocated (e.g., atomic context or the reclaim path).
> + */
> + u32 msg[XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW];
> + } producer;
As discussed in [1], unless we have plans to ACK access counters in the
near future, I would just drop this.
Matt
> +};
> +
> +#endif
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device
2026-03-18 7:44 ` [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device Himal Prasad Ghimiray
@ 2026-04-02 21:50 ` Matthew Brost
2026-04-07 6:41 ` Ghimiray, Himal Prasad
0 siblings, 1 reply; 37+ messages in thread
From: Matthew Brost @ 2026-04-02 21:50 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:48PM +0530, Himal Prasad Ghimiray wrote:
> Move xe_pagefault_asid_to_vm() to xe_device.c as
> xe_device_asid_to_fault_vm() for reuse in access counter handling.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/xe_device.c | 25 +++++++++++++++++++++++++
> drivers/gpu/drm/xe/xe_device.h | 1 +
> drivers/gpu/drm/xe/xe_pagefault.c | 16 +---------------
> 3 files changed, 27 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> index 8773c2d2f69a..ba2f0e345257 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -1399,3 +1399,28 @@ struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid)
>
> return vm;
> }
> +
> +/**
> + * xe_device_asid_to_fault_vm() - Find FAULT VM from ASID
> + * @xe: the &xe_device
> + * @asid: Address space ID
> + *
> + * Find a FAULTING VM from ASID and take a reference to VM which
> + * caller must drop. Reclaim safe.
> + *
> + * Return: VM on success, ERR_PTR on failure
> + */
> +struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid)
I think the recently merged xe_pagefault_save_to_vm() function can reuse
this function now too.
Patch LGTM to though.
Matt
> +{
> + struct xe_vm *vm;
> +
> + down_read(&xe->usm.lock);
> + vm = xa_load(&xe->usm.asid_to_vm, asid);
> + if (vm && xe_vm_in_fault_mode(vm))
> + xe_vm_get(vm);
> + else
> + vm = ERR_PTR(-EINVAL);
> + up_read(&xe->usm.lock);
> +
> + return vm;
> +}
> diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
> index c4d267002661..e746f5c7cce0 100644
> --- a/drivers/gpu/drm/xe/xe_device.h
> +++ b/drivers/gpu/drm/xe/xe_device.h
> @@ -209,6 +209,7 @@ int xe_is_injection_active(void);
> bool xe_is_xe_file(const struct file *file);
>
> struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid);
> +struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid);
>
> /*
> * Occasionally it is seen that the G2H worker starts running after a delay of more than
> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
> index 8c159e3512ee..a4ee9393e4af 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
> @@ -121,20 +121,6 @@ xe_pagefault_access_is_atomic(enum xe_pagefault_access_type access_type)
> return (access_type & XE_PAGEFAULT_ACCESS_TYPE_MASK) == XE_PAGEFAULT_ACCESS_TYPE_ATOMIC;
> }
>
> -static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid)
> -{
> - struct xe_vm *vm;
> -
> - down_read(&xe->usm.lock);
> - vm = xa_load(&xe->usm.asid_to_vm, asid);
> - if (vm && xe_vm_in_fault_mode(vm))
> - xe_vm_get(vm);
> - else
> - vm = ERR_PTR(-EINVAL);
> - up_read(&xe->usm.lock);
> -
> - return vm;
> -}
>
> static int xe_pagefault_service(struct xe_pagefault *pf)
> {
> @@ -149,7 +135,7 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
> if (pf->consumer.fault_type_level == XE_PAGEFAULT_TYPE_LEVEL_NACK)
> return -EFAULT;
>
> - vm = xe_pagefault_asid_to_vm(xe, pf->consumer.asid);
> + vm = xe_device_asid_to_fault_vm(xe, pf->consumer.asid);
> if (IS_ERR(vm))
> return PTR_ERR(vm);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
2026-04-01 21:10 ` Matthew Brost
2026-04-01 22:11 ` Matthew Brost
@ 2026-04-02 22:06 ` Matthew Brost
2 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-02 22:06 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
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>
Again apoligize for streams of replies but one question.
> ---
> 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);
I can’t find any documentation on how the granularity works—do you have
a bspec link? Or, if there’s an internal document, could you send me a
message with a link? Without documentation, this may be a dumb
question...
Can the granularity ever indicate that VA + length is the hot region to
migrate? If so, wouldn’t it be possible for multiple VMAs or ranges to
need migration? In that case, we should handle it—probably as an
iterative process, re-queuing work items to migrate a single VMA/range
in order to unblock higher-priority page faults that may be occurring.
Matt
> +}
> +
> +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 */
> + 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
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting
2026-03-18 7:44 ` [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting Himal Prasad Ghimiray
@ 2026-04-03 0:09 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-03 0:09 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:56PM +0530, Himal Prasad Ghimiray wrote:
> Set XE_PPGTT_PTE_NC on PTEs for VMAs that can't benefit from access
> counter migration hints, avoiding wasteful slot allocation.
>
> Bspec: 67095
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/regs/xe_gtt_defs.h | 2 +-
> drivers/gpu/drm/xe/xe_pt.c | 11 +++++++++++
> 2 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
> index 4d83461e538b..ace3cbcc13d8 100644
> --- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
> +++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
> @@ -28,11 +28,11 @@
> #define XE_GGTT_PTE_DM BIT_ULL(1)
> #define XE_USM_PPGTT_PTE_AE BIT_ULL(10)
> #define XE_PPGTT_PTE_DM BIT_ULL(11)
> +#define XE_PPGTT_PTE_NC BIT_ULL(5)
> #define XE_PDE_64K BIT_ULL(6)
> #define XE_PTE_PS64 BIT_ULL(8)
> #define XE_PTE_NULL BIT_ULL(9)
>
> #define XE_PAGE_PRESENT BIT_ULL(0)
> #define XE_PAGE_RW BIT_ULL(1)
> -
> #endif
> diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
> index 2d9ce2c4cb4f..cc8dcdb6649a 100644
> --- a/drivers/gpu/drm/xe/xe_pt.c
> +++ b/drivers/gpu/drm/xe/xe_pt.c
> @@ -755,6 +755,17 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
> XE_USM_PPGTT_PTE_AE : 0;
> }
>
> + if (!xe_vma_supports_access_ctr(xe, vma, tile)) {
> + xe_walk.default_vram_pte |= XE_PPGTT_PTE_NC;
> + xe_walk.default_system_pte |= XE_PPGTT_PTE_NC;
> + }
> +
> + if (range) {
> + xe_walk.default_vram_pte |= XE_PPGTT_PTE_NC;
> + if (!range->base.pages.flags.migrate_devmem)
This isn’t quite right for multi-GPU. We don’t have a madvise policy for
access counters yet—but we likely should define these semantics as part
of this series. My thinking is that if madvise sets a specific GPU as
the preferred location, other GPUs should clear the NC bit so they don’t
generate access-counter events, since the user has explicitly indicated
where the memory should live. In other words, we need a way to say “this
memory has a preferred location—disable access counters accordingly.”
pages.flags.migrate_devmem is not that. I think this point is fairly
straightforward to agree on.
Now, what should the policy be if no preferred location is set? I’d
suggest defaulting to first-touch placement, then migrating once an
access counter becomes hot.
Looking further ahead, we could implement a policy like “don’t migrate
on first device,” e.g., leave pages in system memory initially, and only
migrate them once access counters on a device become hot. NVIDIA does
something similar. This would likely be handled in a follow-on series
with new uAPI, but I think we should all agree on the first two points
above before going there.
Whatever we agree upon for SVM access counters polices, let's also be
sure update any relavent kernel doc.
Also, in general, let’s avoid touching GPUSVM internals in Xe. If we do
need to, we should at minimum add an xe_svm.h helper layer that we can
eventually move into GPUSVM.
Lastly, default_vram_pte is correct for now. However, once we land UAL,
default_vram_pte will also be used for ranges located on a remote
device. It might make sense to add a helper for setting
xe_walk.default_vram_pte that blindly sets XE_PPGTT_PTE_NC, with a
comment explaining the UAL use case so we don’t forget to update this
later.
Matt
> + xe_walk.default_system_pte |= XE_PPGTT_PTE_NC;
> + }
> +
> xe_walk.default_vram_pte |= XE_PPGTT_PTE_DM;
> xe_walk.dma_offset = bo ? vram_region_gpu_offset(bo->ttm.resource) : 0;
> if (!range)
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger
2026-03-18 7:44 ` [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger Himal Prasad Ghimiray
@ 2026-04-03 0:25 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-03 0:25 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:51PM +0530, Himal Prasad Ghimiray wrote:
> Migrate ranges to local vram and setup pte for gpu access.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/xe_access_counter.c | 30 ++++++-------
> drivers/gpu/drm/xe/xe_svm.c | 59 +++++++++++++++++++-------
> drivers/gpu/drm/xe/xe_svm.h | 4 ++
> 3 files changed, 63 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> index f93618faab02..ba15f21c6803 100644
> --- a/drivers/gpu/drm/xe/xe_access_counter.c
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -13,6 +13,7 @@
> #include "xe_device.h"
> #include "xe_gt_printk.h"
> #include "xe_hw_engine.h"
> +#include "xe_svm.h"
> #include "xe_trace_bo.h"
> #include "xe_usm_queue.h"
> #include "xe_vm.h"
> @@ -42,20 +43,14 @@ 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)
> +static u64 xe_access_counter_get_va(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);
> - }
> + if (ac->consumer.granularity != XE_ACCESS_COUNTER_GRANULARITY_128K)
> + return ac->consumer.va_range_base;
>
> - return xe_vm_find_overlapping_vma(vm, page_va, SZ_4K);
> + return ac->consumer.va_range_base +
> + (ffs(ac->consumer.sub_granularity) - 1) *
> + xe_access_counter_sub_granularity_in_byte(ac->consumer.granularity);
> }
>
> static void xe_access_counter_print(struct xe_access_counter *ac)
> @@ -88,6 +83,7 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
> struct dma_fence *fence;
> struct xe_vm *vm;
> struct xe_vma *vma;
> + u64 page_va;
> int err = 0;
>
> if (ac->consumer.counter_type > XE_ACCESS_COUNTER_TYPE_NOTIFY)
> @@ -104,7 +100,8 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
> goto unlock_vm;
> }
> /* Lookup VMA */
> - vma = xe_access_counter_get_vma(vm, ac);
> + page_va = xe_access_counter_get_va(ac);
> + vma = xe_vm_find_vma_by_addr(vm, page_va);
> if (!vma) {
> err = -EINVAL;
> goto unlock_vm;
> @@ -112,9 +109,12 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
>
> trace_xe_vma_acc(vma, ac->consumer.counter_type);
>
> - /* TODO: Handle svm vma's */
> - if (xe_vma_has_no_bo(vma))
> + if (xe_vma_has_no_bo(vma)) {
> + if (xe_vma_is_cpu_addr_mirror(vma))
> + err = xe_svm_range_setup(vm, vma, gt, page_va,
> + false, true);
Can we split out access-counter handling for SVM versus VMAs into
separate functions, similar to how page faults are handled?
For example:
211 if (xe_vma_is_cpu_addr_mirror(vma))
212 err = xe_svm_handle_pagefault(vm, vma, gt,
213 pf->consumer.page_addr, atomic);
214 else
215 err = xe_pagefault_handle_vma(gt, vma, atomic);
I think this would make the code slightly more maintainable and clearer.
Also, can we add a flags argument to xe_svm_range_setup instead of
passing two booleans?
Arvind just did something similar for vma_lock_and_validate [1], and I
think Xe should move in this direction—avoiding multiple boolean
arguments, as in Arvind’s changes. It much more clear at the caller what
via flags rather than bools.
Matt
[1] https://patchwork.freedesktop.org/patch/714449/?series=156651&rev=11
> goto unlock_vm;
> + }
>
> /* Lock VM and BOs dma-resv */
> xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
> diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
> index a91c84487a67..fe0f86a2d0bf 100644
> --- a/drivers/gpu/drm/xe/xe_svm.c
> +++ b/drivers/gpu/drm/xe/xe_svm.c
> @@ -1186,9 +1186,9 @@ DECL_SVM_RANGE_US_STATS(get_pages, GET_PAGES)
> DECL_SVM_RANGE_US_STATS(bind, BIND)
> DECL_SVM_RANGE_US_STATS(fault, PAGEFAULT)
>
> -static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> - struct xe_gt *gt, u64 fault_addr,
> - bool need_vram)
> +static int __xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
> + struct xe_gt *gt, u64 fault_addr,
> + bool need_vram, bool acc_ctr_trigger)
> {
> int devmem_possible = IS_DGFX(vm->xe) &&
> IS_ENABLED(CONFIG_DRM_XE_PAGEMAP);
> @@ -1196,7 +1196,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> .read_only = xe_vma_read_only(vma),
> .devmem_possible = devmem_possible,
> .check_pages_threshold = devmem_possible ? SZ_64K : 0,
> - .devmem_only = need_vram && devmem_possible,
> + .devmem_only = (need_vram || acc_ctr_trigger) && devmem_possible,
> .timeslice_ms = need_vram && devmem_possible ?
> vm->xe->atomic_svm_timeslice_ms : 0,
> };
> @@ -1213,7 +1213,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> lockdep_assert_held_write(&vm->lock);
> xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
>
> - xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_PAGEFAULT_COUNT, 1);
> + if (!acc_ctr_trigger)
> + xe_gt_stats_incr(gt, XE_GT_STATS_ID_SVM_PAGEFAULT_COUNT, 1);
>
> retry:
> /* Always process UNMAPs first so view SVM ranges is current */
> @@ -1229,7 +1230,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> if (IS_ERR(range))
> return PTR_ERR(range);
>
> - xe_svm_range_fault_count_stats_incr(gt, range);
> + if (!acc_ctr_trigger)
> + xe_svm_range_fault_count_stats_incr(gt, range);
>
> if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) {
> err = -EACCES;
> @@ -1244,6 +1246,10 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
>
> range_debug(range, "PAGE FAULT");
>
> + if (acc_ctr_trigger && !range->base.pages.flags.migrate_devmem) {
> + goto out;
> + }
> +
> if (--migrate_try_count >= 0 &&
> xe_svm_range_needs_migrate_to_vram(range, vma, dpagemap)) {
> ktime_t migrate_start = xe_gt_stats_ktime_get();
> @@ -1307,6 +1313,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> }
>
> xe_svm_range_get_pages_us_stats_incr(gt, range, get_pages_start);
> +
> range_debug(range, "PAGE FAULT - BIND");
>
> bind_start = xe_gt_stats_ktime_get();
> @@ -1347,21 +1354,22 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> }
>
> /**
> - * xe_svm_handle_pagefault() - SVM handle page fault
> + * xe_svm_range_setup - Setup range for GPU access
> * @vm: The VM.
> * @vma: The CPU address mirror VMA.
> - * @gt: The gt upon the fault occurred.
> - * @fault_addr: The GPU fault address.
> + * @gt: The gt for which binding.
> + * @addr: Addr for which need to bind svm range.
> * @atomic: The fault atomic access bit.
> + * @acc_ctr_trigger: If true, always migrate to local device memory.
> *
> * Create GPU bindings for a SVM page fault. Optionally migrate to device
> * memory.
> *
> * Return: 0 on success, negative error code on error.
> */
> -int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> - struct xe_gt *gt, u64 fault_addr,
> - bool atomic)
> +int xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
> + struct xe_gt *gt, u64 addr,
> + bool atomic, bool acc_ctr_trigger)
> {
> int need_vram, ret;
> retry:
> @@ -1369,14 +1377,15 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> if (need_vram < 0)
> return need_vram;
>
> - ret = __xe_svm_handle_pagefault(vm, vma, gt, fault_addr,
> - need_vram ? true : false);
> + ret = __xe_svm_range_setup(vm, vma, gt, addr,
> + need_vram ? true : false,
> + acc_ctr_trigger);
> if (ret == -EAGAIN) {
> /*
> * Retry once on -EAGAIN to re-lookup the VMA, as the original VMA
> * may have been split by xe_svm_range_set_default_attr.
> */
> - vma = xe_vm_find_vma_by_addr(vm, fault_addr);
> + vma = xe_vm_find_vma_by_addr(vm, addr);
> if (!vma)
> return -EINVAL;
>
> @@ -1385,6 +1394,26 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> return ret;
> }
>
> +/**
> + * xe_svm_handle_pagefault() - SVM handle page fault
> + * @vm: The VM.
> + * @vma: The CPU address mirror VMA.
> + * @gt: The gt upon the fault occurred.
> + * @fault_addr: The GPU fault address.
> + * @atomic: The fault atomic access bit.
> + *
> + * Create GPU bindings for a SVM page fault. Optionally migrate to device
> + * memory.
> + *
> + * Return: 0 on success, negative error code on error.
> + */
> +int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> + struct xe_gt *gt, u64 fault_addr,
> + bool atomic)
> +{
> + return xe_svm_range_setup(vm, vma, gt, fault_addr, atomic, false);
> +}
> +
> /**
> * xe_svm_has_mapping() - SVM has mappings
> * @vm: The VM.
> diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
> index b7b8eeacf196..50861d93b12f 100644
> --- a/drivers/gpu/drm/xe/xe_svm.h
> +++ b/drivers/gpu/drm/xe/xe_svm.h
> @@ -85,6 +85,10 @@ void xe_svm_fini(struct xe_vm *vm);
>
> void xe_svm_close(struct xe_vm *vm);
>
> +int xe_svm_range_setup(struct xe_vm *vm, struct xe_vma *vma,
> + struct xe_gt *gt, u64 addr,
> + bool atomic, bool acc_ctr_trigger);
> +
> int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
> struct xe_gt *gt, u64 fault_addr,
> bool atomic);
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications
2026-03-18 7:44 ` [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications Himal Prasad Ghimiray
@ 2026-04-03 1:01 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-03 1:01 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:50PM +0530, Himal Prasad Ghimiray wrote:
> Trace VMA access counter notifications with asid, address range, and
> counter type (TRIGGER or NOTIFY) to aid debugging of migration hints.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
> ---
> drivers/gpu/drm/xe/xe_access_counter.c | 7 ++++--
> drivers/gpu/drm/xe/xe_trace_bo.h | 32 +++++++++++++++++++++++---
> 2 files changed, 34 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> index 9eb9917d8da7..f93618faab02 100644
> --- a/drivers/gpu/drm/xe/xe_access_counter.c
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -13,6 +13,7 @@
> #include "xe_device.h"
> #include "xe_gt_printk.h"
> #include "xe_hw_engine.h"
> +#include "xe_trace_bo.h"
> #include "xe_usm_queue.h"
> #include "xe_vm.h"
>
> @@ -109,6 +110,8 @@ static int xe_access_counter_service(struct xe_access_counter *ac)
> goto unlock_vm;
> }
>
> + trace_xe_vma_acc(vma, ac->consumer.counter_type);
> +
> /* TODO: Handle svm vma's */
> if (xe_vma_has_no_bo(vma))
> goto unlock_vm;
> @@ -153,7 +156,7 @@ static void xe_access_counter_queue_work_func(struct work_struct *w)
> #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())) {
> + while (xe_usm_queue_pop(ac_queue, &ac, sizeof(struct xe_access_counter))) {
> int err;
>
> if (!ac.gt) /* Access counter squashed during reset */
> @@ -239,7 +242,7 @@ int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac
> spin_lock_irqsave(&ac_queue->lock, flags);
> full = xe_usm_queue_full(ac_queue, sizeof(struct xe_access_counter));
> if (!full) {
> - xe_usm_queue_push(ac_queue, ac, xe_access_counter_entry_size());
> + xe_usm_queue_push(ac_queue, ac, sizeof(struct xe_access_counter));
> queue_work(xe->usm.pf_wq, &ac_queue->worker);
> } else {
> drm_warn(&xe->drm,
> diff --git a/drivers/gpu/drm/xe/xe_trace_bo.h b/drivers/gpu/drm/xe/xe_trace_bo.h
> index 86323cf3be2c..eae5a5d0dbdc 100644
> --- a/drivers/gpu/drm/xe/xe_trace_bo.h
> +++ b/drivers/gpu/drm/xe/xe_trace_bo.h
> @@ -12,6 +12,7 @@
> #include <linux/tracepoint.h>
> #include <linux/types.h>
>
> +#include "xe_access_counter_types.h"
> #include "xe_bo.h"
> #include "xe_bo_types.h"
> #include "xe_vm.h"
> @@ -125,9 +126,34 @@ DEFINE_EVENT(xe_vma, xe_vma_pagefault,
> TP_ARGS(vma)
> );
>
> -DEFINE_EVENT(xe_vma, xe_vma_acc,
> - TP_PROTO(struct xe_vma *vma),
> - TP_ARGS(vma)
> +TRACE_EVENT(xe_vma_acc,
> + TP_PROTO(struct xe_vma *vma, u8 counter_type),
> + TP_ARGS(vma, counter_type),
> +
> + TP_STRUCT__entry(
> + __string(dev, __dev_name_vma(vma))
> + __field(struct xe_vma *, vma)
> + __field(struct xe_vm *, vm)
> + __field(u32, asid)
> + __field(u64, start)
> + __field(u64, end)
> + __field(u8, counter_type)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(dev);
> + __entry->vma = vma;
> + __entry->vm = xe_vma_vm(vma);
> + __entry->asid = xe_vma_vm(vma)->usm.asid;
> + __entry->start = xe_vma_start(vma);
> + __entry->end = xe_vma_end(vma) - 1;
> + __entry->counter_type = counter_type;
> + ),
> +
> + TP_printk("dev=%s, vma=%p, vm=%p, asid=0x%05x, start=0x%012llx, end=0x%012llx, type=%s",
> + __get_str(dev), __entry->vma, __entry->vm,
> + __entry->asid, __entry->start, __entry->end,
> + __entry->counter_type == XE_ACCESS_COUNTER_TYPE_NOTIFY ? "NOTIFY" : "TRIGGER")
> );
>
> DEFINE_EVENT(xe_vma, xe_vma_bind,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 05/15] drm/xe: Implement xe_access_counter_handler
2026-03-18 7:44 ` [RFC 05/15] drm/xe: Implement xe_access_counter_handler Himal Prasad Ghimiray
@ 2026-04-03 2:06 ` Matthew Brost
0 siblings, 0 replies; 37+ messages in thread
From: Matthew Brost @ 2026-04-03 2:06 UTC (permalink / raw)
To: Himal Prasad Ghimiray
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On Wed, Mar 18, 2026 at 01:14:46PM +0530, Himal Prasad Ghimiray wrote:
> Enqueue access counter notifications to the appropriate queue based on
> ASID and schedule worker to process them.
>
> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
> ---
> drivers/gpu/drm/xe/xe_access_counter.c | 23 ++++++++++++++++++++---
> 1 file changed, 20 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
> index cafcc42897f8..a2ce9dc45d05 100644
> --- a/drivers/gpu/drm/xe/xe_access_counter.c
> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
> @@ -87,12 +87,29 @@ int xe_access_counter_init(struct xe_device *xe)
> * @xe: xe device
> * @ac: access counter notification
> *
> - * Process an access counter notification from the producer layer.
> + * Sink the access counter notification to a queue (i.e., a memory buffer) and
> + * queue a worker to service it.
> *
> * Return: 0 on success, negative error code on error
> */
> int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
> {
> - /* Stub implementation - to be filled in */
> - return 0;
> + struct xe_usm_queue *ac_queue = xe->usm.ac_queue +
> + (ac->consumer.xe3.asid % XE_ACCESS_COUNTER_QUEUE_COUNT);
So again, let’s go with a single-queue [1], multiple-worker design.
Also, do not pick a worker based on ASID hashing, because once we move
to fine-grained locking [2], we can service access counters using
vm->lock in read mode.
Yes, BO-based migrations still need the VM dma-resv lock, so we would
serialize there, but given how BO migrations and binds are pipelined,
this ends up being a relatively small window unless we wait on the
migration under VM's dma-resv lock, which we don't need to do. SVM or
userptr (assuming we implement this eventually) access-counter migration
would remain pretty much fully parallel on the same VM.
Matt
[1] https://patchwork.freedesktop.org/patch/712591/?series=163429&rev=1#comment_1317960
[2] https://patchwork.freedesktop.org/patch/707294/?series=162167&rev=4
> + unsigned long flags;
> + bool full;
> +
> + spin_lock_irqsave(&ac_queue->lock, flags);
> + full = xe_usm_queue_full(ac_queue, sizeof(struct xe_access_counter));
> + if (!full) {
> + xe_usm_queue_push(ac_queue, ac, xe_access_counter_entry_size());
> + queue_work(xe->usm.pf_wq, &ac_queue->worker);
> + } else {
> + drm_warn(&xe->drm,
> + "AccessCounter Queue (%d) full, shouldn't be possible\n",
> + ac->consumer.xe3.asid % XE_ACCESS_COUNTER_QUEUE_COUNT);
> + }
> + spin_unlock_irqrestore(&ac_queue->lock, flags);
> +
> + return full ? -ENOSPC : 0;
> }
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer
2026-04-01 21:28 ` Matthew Brost
@ 2026-04-06 4:46 ` Ghimiray, Himal Prasad
0 siblings, 0 replies; 37+ messages in thread
From: Ghimiray, Himal Prasad @ 2026-04-06 4:46 UTC (permalink / raw)
To: Matthew Brost
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On 02-04-2026 02:58, Matthew Brost wrote:
> On Wed, Mar 18, 2026 at 01:14:42PM +0530, Himal Prasad Ghimiray wrote:
>> Introduce struct xe_usm_queue, a generic lock-protected circular FIFO
>> used by USM consumers (page fault, access counter) to receive fixed-size
>> entries from IRQ context and process them via a work_struct.
>>
>> Provide three inline helpers in xe_usm_queue.h:
>> xe_usm_queue_full() - CIRC_SPACE check (caller holds lock)
>> xe_usm_queue_pop() - dequeue one entry (acquires lock internally)
>> xe_usm_queue_push() - enqueue one entry (caller holds lock)
>>
>> Suggested-by: Matthew Brost <matthew.brost@intel.com>
>> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
>
> I think this is useful, but right after I suggested it, I changed the
> semantics of the page-fault circular buffer to be a single buffer [1]
> that multiple workers pull from. This eliminates HoQ blocking (e.g.,
> five access events are pushed; the first one takes a really long time,
> but the other four are much faster—yet the fifth event gets stuck behind
> the long-running one). So maybe start with a single-queue +
> multiple-workers-pulling design here? This likely matters considerably
> less than the page-fault cases, but overall I think it’s a better
> structure.
>
> Also, maybe the xe_usm_queue can be reused by page faults, but the
> management of that queue actually gets more convoluted in [2] to
> implement the page-fault cache that greatly speeds up storms of faults.
> So maybe drop the second patch in the series for now to avoid conflicts,
> and if we can converge on a shared xe_usm_queue implementation later,
> great..
Sure will change this implementation to support 1 queue : N worker, and
will drop patch 2.
Thanks
>
> Matt
>
> [1] https://patchwork.freedesktop.org/patch/707293/?series=162167&rev=4
> [2] https://patchwork.freedesktop.org/patch/707300/?series=162167&rev=4
>
>> ---
>> drivers/gpu/drm/xe/xe_usm_queue.h | 100 ++++++++++++++++++++++++++++++
>> 1 file changed, 100 insertions(+)
>> create mode 100644 drivers/gpu/drm/xe/xe_usm_queue.h
>>
>> diff --git a/drivers/gpu/drm/xe/xe_usm_queue.h b/drivers/gpu/drm/xe/xe_usm_queue.h
>> new file mode 100644
>> index 000000000000..5dc2d692d8bb
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_usm_queue.h
>> @@ -0,0 +1,100 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2026 Intel Corporation
>> + */
>> +
>> +#ifndef _XE_USM_QUEUE_H_
>> +#define _XE_USM_QUEUE_H_
>> +
>> +#include <linux/circ_buf.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/workqueue.h>
>> +
>> +/**
>> + * struct xe_usm_queue - Generic USM circular FIFO queue
>> + *
>> + * A lock-protected circular buffer used by both the page fault consumer and
>> + * the access counter consumer. Producers push fixed-size entries from IRQ
>> + * context; a work_struct processes them asynchronously.
>> + */
>> +struct xe_usm_queue {
>> + /**
>> + * @data: Raw byte buffer backing the queue, protected by @lock
>> + */
>> + void *data;
>> + /** @size: Total size of @data in bytes */
>> + u32 size;
>> + /** @head: Write cursor in bytes, moved by producer, protected by @lock */
>> + u32 head;
>> + /** @tail: Read cursor in bytes, moved by consumer, protected by @lock */
>> + u32 tail;
>> + /** @lock: Protects the queue head/tail */
>> + spinlock_t lock;
>> + /** @worker: Work item scheduled to drain the queue */
>> + struct work_struct worker;
>> +};
>> +
>> +/**
>> + * xe_usm_queue_full - Check whether the queue has no room for one more entry
>> + * @q: queue
>> + * @size: exact size of one entry in bytes (sizeof the entry struct)
>> + *
>> + * Must be called with @q->lock held.
>> + *
>> + * Return: true if the queue is full
>> + */
>> +static inline bool xe_usm_queue_full(struct xe_usm_queue *q, u32 size)
>> +{
>> + u32 entry_size = roundup_pow_of_two(size);
>> +
>> + lockdep_assert_held(&q->lock);
>> +
>> + return CIRC_SPACE(q->head, q->tail, q->size) <= entry_size;
>> +}
>> +/**
>> + * xe_usm_queue_pop - Pop one entry from the queue into @out
>> + * @q: queue
>> + * @out: destination buffer, must be at least @size bytes
>> + * @size: exact size of one entry in bytes (sizeof the entry struct)
>> + *
>> + * Acquires @q->lock internally with spin_lock_irq().
>> + *
>> + * Return: true if an entry was dequeued, false if the queue was empty
>> + */
>> +static inline bool xe_usm_queue_pop(struct xe_usm_queue *q, void *out,
>> + u32 size)
>> +{
>> + u32 entry_size = roundup_pow_of_two(size);
>> + bool found = false;
>> +
>> + spin_lock_irq(&q->lock);
>> + if (q->tail != q->head) {
>> + memcpy(out, q->data + q->tail, size);
>> + q->tail = (q->tail + entry_size) % q->size;
>> + found = true;
>> + }
>> + spin_unlock_irq(&q->lock);
>> +
>> + return found;
>> +}
>> +
>> +/**
>> + * xe_usm_queue_push - Push one entry into the queue
>> + * @q: queue
>> + * @in: source buffer, must be at least @size bytes
>> + * @size: size of one entry of in bytes
>> + *
>> + * Must be called with @q->lock held.
>> + * Caller must check xe_usm_queue_full() before calling.
>> + */
>> +static inline void xe_usm_queue_push(struct xe_usm_queue *q, const void *in,
>> + u32 size)
>> +{
>> + u32 entry_size = roundup_pow_of_two(size);
>> +
>> + lockdep_assert_held(&q->lock);
>> +
>> + memcpy(q->data + q->head, in, size);
>> + q->head = (q->head + entry_size) % q->size;
>> +}
>> +#endif
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 03/15] drm/xe: Stub out new access_counter layer
2026-04-02 21:46 ` Matthew Brost
@ 2026-04-06 5:28 ` Ghimiray, Himal Prasad
0 siblings, 0 replies; 37+ messages in thread
From: Ghimiray, Himal Prasad @ 2026-04-06 5:28 UTC (permalink / raw)
To: Matthew Brost
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On 03-04-2026 03:16, Matthew Brost wrote:
> On Wed, Mar 18, 2026 at 01:14:44PM +0530, Himal Prasad Ghimiray wrote:
>> Add access counter infrastructure with type definitions, header files,
>> and stub implementation. This follows a two-layer producer-consumer
>> architecture similar to the pagefault layer.
>>
>> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
>> ---
>> drivers/gpu/drm/xe/Makefile | 3 +-
>> drivers/gpu/drm/xe/xe_access_counter.c | 55 +++++++++
>> drivers/gpu/drm/xe/xe_access_counter.h | 17 +++
>> drivers/gpu/drm/xe/xe_access_counter_types.h | 123 +++++++++++++++++++
>> 4 files changed, 197 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.c
>> create mode 100644 drivers/gpu/drm/xe/xe_access_counter.h
>> create mode 100644 drivers/gpu/drm/xe/xe_access_counter_types.h
>>
>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
>> index 3a3f9f22d42a..92d8d6e4a447 100644
>> --- a/drivers/gpu/drm/xe/Makefile
>> +++ b/drivers/gpu/drm/xe/Makefile
>> @@ -32,7 +32,8 @@ $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe
>>
>> # core driver code
>>
>> -xe-y += xe_bb.o \
>> +xe-y += xe_access_counter.o \
>> + xe_bb.o \
>> xe_bo.o \
>> xe_bo_evict.o \
>> xe_dep_scheduler.o \
>> diff --git a/drivers/gpu/drm/xe/xe_access_counter.c b/drivers/gpu/drm/xe/xe_access_counter.c
>> new file mode 100644
>> index 000000000000..f3a8a93b5135
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_access_counter.c
>> @@ -0,0 +1,55 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2026 Intel Corporation
>> + */
>> +
>> +#include <linux/circ_buf.h>
>> +
>> +#include <drm/drm_exec.h>
>> +#include <drm/drm_managed.h>
>> +
>> +#include "xe_access_counter.h"
>> +#include "xe_access_counter_types.h"
>> +#include "xe_device.h"
>> +
>> +/**
>> + * DOC: Xe access counters
>> + *
>> + * Xe access counters are handled in two layers with one-way communication.
>> + * The producer layer interacts with hardware or firmware to receive and parse
>> + * access counter notifications into struct xe_access_counter, then forwards them
>> + * to the consumer. The consumer layer services the notifications (e.g., memory
>> + * migration hints, binding decisions). No acknowledgment is sent back to the
>> + * producer. The consumer uses an access counter queue sized to absorb all potential
>> + * notifications and a multi-threaded worker to process them. Multiple producers
>> + * are supported, with a single shared consumer.
>> + *
>> + * xe_access_counter.c implements the consumer layer.
>> + */
>> +
>> +/**
>> + * xe_access_counter_init - Initialize access counter consumer layer
>> + * @xe: xe device
>> + *
>> + * Return: 0 on success, negative error code on error
>> + */
>> +int xe_access_counter_init(struct xe_device *xe)
>> +{
>> + /* Stub implementation - to be filled in */
>> + return 0;
>> +}
>> +
>> +/**
>> + * xe_access_counter_handler - Handle an access counter notification
>> + * @xe: xe device
>> + * @ac: access counter notification
>> + *
>> + * Process an access counter notification from the producer layer.
>> + *
>> + * Return: 0 on success, negative error code on error
>> + */
>> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac)
>> +{
>> + /* Stub implementation - to be filled in */
>> + return 0;
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_access_counter.h b/drivers/gpu/drm/xe/xe_access_counter.h
>> new file mode 100644
>> index 000000000000..b3a331687f13
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_access_counter.h
>> @@ -0,0 +1,17 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2026 Intel Corporation
>> + */
>> +
>> +#ifndef _XE_ACCESS_COUNTER_H_
>> +#define _XE_ACCESS_COUNTER_H_
>> +
>> +struct xe_device;
>> +struct xe_gt;
>> +struct xe_access_counter;
>> +
>> +int xe_access_counter_init(struct xe_device *xe);
>> +
>> +int xe_access_counter_handler(struct xe_device *xe, struct xe_access_counter *ac);
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/xe/xe_access_counter_types.h b/drivers/gpu/drm/xe/xe_access_counter_types.h
>> new file mode 100644
>> index 000000000000..74b903b9461b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_access_counter_types.h
>> @@ -0,0 +1,123 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2026 Intel Corporation
>> + */
>> +
>> +#ifndef _XE_ACCESS_COUNTER_TYPES_H_
>> +#define _XE_ACCESS_COUNTER_TYPES_H_
>> +
>> +#include <linux/sizes.h>
>> +#include <linux/types.h>
>> +
>> +struct xe_gt;
>> +struct xe_access_counter;
>> +
>> +/** enum xe_access_counter_type - Xe access counter type */
>> +enum xe_access_counter_type {
>> + /** @XE_ACCESS_COUNTER_TYPE_TRIGGER */
>> + XE_ACCESS_COUNTER_TYPE_TRIGGER = 0,
>> + /** @XE_ACCESS_COUNTER_TYPE_NOTIFY*/
>> + XE_ACCESS_COUNTER_TYPE_NOTIFY = 1,
>> +};
>
> What’s the difference between the types? Patch 8 handles both of them in
> exactly the same way, which may be correct or fine, but without kernel
> documentation explaining the distinction between the types here, it’s
> hard to reason about it.
>
Will add kernel doc in next rev.
>> +
>> +/** enum xe_access_counter_granularity - Xe access counter granularity */
>> +enum xe_access_counter_granularity {
>> + /** @XE_ACCESS_COUNTER_GRANULARITY_128K: 128K granularity */
>> + XE_ACCESS_COUNTER_GRANULARITY_128K = 0,
>> + /** @XE_ACCESS_COUNTER_GRANULARITY_2M: 2M granularity */
>> + XE_ACCESS_COUNTER_GRANULARITY_2M = 1,
>> + /** @XE_ACCESS_COUNTER_GRANULARITY_16M: 16M granularity */
>> + XE_ACCESS_COUNTER_GRANULARITY_16M = 2,
>> + /** @XE_ACCESS_COUNTER_GRANULARITY_64M: 64M granularity */
>> + XE_ACCESS_COUNTER_GRANULARITY_64M = 3,
>> +};
>> +
>> +static inline int xe_access_counter_granularity_in_byte(int val)
>> +{
>> + switch (val) {
>> + case XE_ACCESS_COUNTER_GRANULARITY_128K:
>> + return SZ_128K;
>> + case XE_ACCESS_COUNTER_GRANULARITY_2M:
>> + return SZ_2M;
>> + case XE_ACCESS_COUNTER_GRANULARITY_16M:
>> + return SZ_16M;
>> + case XE_ACCESS_COUNTER_GRANULARITY_64M:
>> + return SZ_64M;
>> + default:
>> + return 0;
>> + }
>> +}
>
> As discussed in [1], I think the granularity handling can be moved into
> the GuC layer, so there’s no need to include public definitions here.
>
> [1] https://patchwork.freedesktop.org/patch/712600/?series=163429&rev=1#comment_1318525
Makes sense, will move it to guc_layer, probably xe_access_counter_type
can also be moved to guc layer.
>
>> +
>> +/**
>> + * struct xe_access_counter - Xe access counter
>> + *
>> + * Generic access counter structure for communication between producer and consumer.
>> + * Carefully sized to be 64 bytes. Upon a device access counter notification, the
>> + * producer populates this structure, and the consumer copies it into the access
>> + * counter queue for deferred handling.
>> + */
>> +struct xe_access_counter {
>> + /**
>> + * @gt: GT of access counter
>> + */
>> + struct xe_gt *gt;
>> + /**
>> + * @consumer: State for the software handling the access counter.
>> + * Populated by the producer and may be modified by the consumer to
>> + * communicate information back to the producer upon acknowledgment.
>> + */
>> + struct {
>> + /** @consumer.va_range_base: base virtual address of range */
>> + u64 va_range_base;
>> + /** @consumer.sub_granularity: sub-granularity */
>> + u32 sub_granularity;
>> + /**
>> + * @consumer.counter_type: counter type, u8 rather than enum to
>> + * keep size compact
>> + */
>> + u8 counter_type;
>> + /**
>> + * @consumer.granularity: access granularity, u8 rather than enum
>> + * to keep size compact
>> + */
>> + u8 granularity;
>> + /** @consumer.reserved: reserved bits for alignment */
>> + u8 reserved[2];
>> + /** @consumer: Platform-specific fields */
>> + union {
>> + /** @consumer.xe3: Xe3-specific fields */
>> + struct {
>> + /** @consumer.xe3.asid: address space ID */
>> + u32 asid;
>> + /** @consumer.xe3.engine_class: engine class */
>> + u8 engine_class;
>> + /** @consumer.xe3.engine_instance: engine instance */
>> + u8 engine_instance;
>> + /** @consumer.xe3.vfid: VFID */
>> + u8 vfid;
>> + /** @consumer.xe3.reserved: reserved bits */
>> + u8 reserved;
>> + } xe3;
>> + };
>
> Can we drop the xe3 prefix here to keep this interface generic across
> platforms?
Yes we can drop xe3 prefix, but we need differentiatior since interface
will be HW dependent.
>
> Also, a question: xe3 implies these are only valid on xe3+, so do access
> counters not work on prior platforms?
For example, without a valid ASID,
> the common layer can’t find a VMA.
Will explore this.
>
>> + } consumer;
>> + /**
>> + * @producer: State for the producer (i.e., HW/FW interface). Populated
>> + * by the producer and should not be modified—or even inspected—by the
>> + * consumer, except for calling operations.
>> + */
>> + struct {
>> + /** @producer.private: private pointer */
>> + void *private;
>> +#define XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW 4
>> + /**
>> + * @producer.msg: access counter message, used by producer in
>> + * acknowledgment to formulate response to HW/FW interface.
>> + * Included in the access counter message because the producer
>> + * typically receives the notification in a context where memory
>> + * cannot be allocated (e.g., atomic context or the reclaim path).
>> + */
>> + u32 msg[XE_ACCESS_COUNTER_PRODUCER_MSG_LEN_DW];
>> + } producer;
>
> As discussed in [1], unless we have plans to ACK access counters in the
> near future, I would just drop this.
The ACK is not available rightnow, I left it for future usecases, will
remove it.
>
> Matt
>
>> +};
>> +
>> +#endif
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device
2026-04-02 21:50 ` Matthew Brost
@ 2026-04-07 6:41 ` Ghimiray, Himal Prasad
0 siblings, 0 replies; 37+ messages in thread
From: Ghimiray, Himal Prasad @ 2026-04-07 6:41 UTC (permalink / raw)
To: Matthew Brost
Cc: intel-xe, thomas.hellstrom, stuart.summers, arvind.yadav,
tejas.upadhyay
On 03-04-2026 03:20, Matthew Brost wrote:
> On Wed, Mar 18, 2026 at 01:14:48PM +0530, Himal Prasad Ghimiray wrote:
>> Move xe_pagefault_asid_to_vm() to xe_device.c as
>> xe_device_asid_to_fault_vm() for reuse in access counter handling.
>>
>> Signed-off-by: Himal Prasad Ghimiray <himal.prasad.ghimiray@intel.com>
>> ---
>> drivers/gpu/drm/xe/xe_device.c | 25 +++++++++++++++++++++++++
>> drivers/gpu/drm/xe/xe_device.h | 1 +
>> drivers/gpu/drm/xe/xe_pagefault.c | 16 +---------------
>> 3 files changed, 27 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
>> index 8773c2d2f69a..ba2f0e345257 100644
>> --- a/drivers/gpu/drm/xe/xe_device.c
>> +++ b/drivers/gpu/drm/xe/xe_device.c
>> @@ -1399,3 +1399,28 @@ struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid)
>>
>> return vm;
>> }
>> +
>> +/**
>> + * xe_device_asid_to_fault_vm() - Find FAULT VM from ASID
>> + * @xe: the &xe_device
>> + * @asid: Address space ID
>> + *
>> + * Find a FAULTING VM from ASID and take a reference to VM which
>> + * caller must drop. Reclaim safe.
>> + *
>> + * Return: VM on success, ERR_PTR on failure
>> + */
>> +struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid)
>
> I think the recently merged xe_pagefault_save_to_vm() function can reuse
> this function now too.
For xe_pagefault_save_to_vm we need to use existing function
xe_device_asid_to_vm instead of xe_device_asid_to_fault_vm(). Will
submit patch for same too.
Thanks.
>
> Patch LGTM to though.
>
> Matt
>
>> +{
>> + struct xe_vm *vm;
>> +
>> + down_read(&xe->usm.lock);
>> + vm = xa_load(&xe->usm.asid_to_vm, asid);
>> + if (vm && xe_vm_in_fault_mode(vm))
>> + xe_vm_get(vm);
>> + else
>> + vm = ERR_PTR(-EINVAL);
>> + up_read(&xe->usm.lock);
>> +
>> + return vm;
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
>> index c4d267002661..e746f5c7cce0 100644
>> --- a/drivers/gpu/drm/xe/xe_device.h
>> +++ b/drivers/gpu/drm/xe/xe_device.h
>> @@ -209,6 +209,7 @@ int xe_is_injection_active(void);
>> bool xe_is_xe_file(const struct file *file);
>>
>> struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid);
>> +struct xe_vm *xe_device_asid_to_fault_vm(struct xe_device *xe, u32 asid);
>>
>> /*
>> * Occasionally it is seen that the G2H worker starts running after a delay of more than
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
>> index 8c159e3512ee..a4ee9393e4af 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault.c
>> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
>> @@ -121,20 +121,6 @@ xe_pagefault_access_is_atomic(enum xe_pagefault_access_type access_type)
>> return (access_type & XE_PAGEFAULT_ACCESS_TYPE_MASK) == XE_PAGEFAULT_ACCESS_TYPE_ATOMIC;
>> }
>>
>> -static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid)
>> -{
>> - struct xe_vm *vm;
>> -
>> - down_read(&xe->usm.lock);
>> - vm = xa_load(&xe->usm.asid_to_vm, asid);
>> - if (vm && xe_vm_in_fault_mode(vm))
>> - xe_vm_get(vm);
>> - else
>> - vm = ERR_PTR(-EINVAL);
>> - up_read(&xe->usm.lock);
>> -
>> - return vm;
>> -}
>>
>> static int xe_pagefault_service(struct xe_pagefault *pf)
>> {
>> @@ -149,7 +135,7 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>> if (pf->consumer.fault_type_level == XE_PAGEFAULT_TYPE_LEVEL_NACK)
>> return -EFAULT;
>>
>> - vm = xe_pagefault_asid_to_vm(xe, pf->consumer.asid);
>> + vm = xe_device_asid_to_fault_vm(xe, pf->consumer.asid);
>> if (IS_ERR(vm))
>> return PTR_ERR(vm);
>>
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2026-04-07 6:41 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18 7:44 [RFC 00/15] drm/xe: Access counter consumer layer Himal Prasad Ghimiray
2026-03-18 7:34 ` ✗ CI.checkpatch: warning for " Patchwork
2026-03-18 7:35 ` ✗ CI.KUnit: failure " Patchwork
2026-03-18 7:44 ` [RFC 01/15] drm/xe: Add xe_usm_queue generic USM circular buffer Himal Prasad Ghimiray
2026-04-01 21:28 ` Matthew Brost
2026-04-06 4:46 ` Ghimiray, Himal Prasad
2026-03-18 7:44 ` [RFC 02/15] drm/xe/pagefault: Use xe_usm_queue helpers Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 03/15] drm/xe: Stub out new access_counter layer Himal Prasad Ghimiray
2026-04-02 21:46 ` Matthew Brost
2026-04-06 5:28 ` Ghimiray, Himal Prasad
2026-03-18 7:44 ` [RFC 04/15] drm/xe: Implement xe_access_counter_init Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 05/15] drm/xe: Implement xe_access_counter_handler Himal Prasad Ghimiray
2026-04-03 2:06 ` Matthew Brost
2026-03-18 7:44 ` [RFC 06/15] drm/xe: Extract xe_vma_lock_and_validate helper Himal Prasad Ghimiray
2026-04-01 22:03 ` Matthew Brost
2026-03-18 7:44 ` [RFC 07/15] drm/xe: Move ASID to FAULT VM lookup to xe_device Himal Prasad Ghimiray
2026-04-02 21:50 ` Matthew Brost
2026-04-07 6:41 ` Ghimiray, Himal Prasad
2026-03-18 7:44 ` [RFC 08/15] drm/xe: Implement xe_access_counter_queue_work Himal Prasad Ghimiray
2026-04-01 21:10 ` Matthew Brost
2026-04-01 22:01 ` Matthew Brost
2026-04-01 22:11 ` Matthew Brost
2026-04-02 22:06 ` Matthew Brost
2026-03-18 7:44 ` [RFC 09/15] drm/xe/trace: Add xe_vma_acc trace event for access counter notifications Himal Prasad Ghimiray
2026-04-03 1:01 ` Matthew Brost
2026-03-18 7:44 ` [RFC 10/15] drm/xe/svm: Handle svm ranges on access ctr trigger Himal Prasad Ghimiray
2026-04-03 0:25 ` Matthew Brost
2026-03-18 7:44 ` [RFC 11/15] drm/xe: Add xe_guc_access_counter layer Himal Prasad Ghimiray
2026-04-02 21:27 ` Matthew Brost
2026-03-18 7:44 ` [RFC 12/15] drm/xe/uapi: Add access counter parameter extension for exec queue Himal Prasad Ghimiray
2026-03-24 14:25 ` Francois Dugast
2026-04-01 14:46 ` Matthew Brost
2026-04-01 16:36 ` Ghimiray, Himal Prasad
2026-03-18 7:44 ` [RFC 13/15] drm/xe/lrc: Pass exec_queue to xe_lrc_create for access counter params Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 14/15] drm/xe/vm: Add xe_vma_supports_access_ctr() helper Himal Prasad Ghimiray
2026-03-18 7:44 ` [RFC 15/15] drm/xe/pt: Set NC PTE bit for VMAs ineligible for access counting Himal Prasad Ghimiray
2026-04-03 0:09 ` Matthew Brost
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox