From: Matthew Brost <matthew.brost@intel.com>
To: intel-xe@lists.freedesktop.org
Cc: Maciej Patelczyk <maciej.patelczyk@intel.com>
Subject: [PATCH v8 04/12] drm/xe: Use a single page-fault queue with multiple workers
Date: Fri, 24 Jul 2026 16:25:53 -0700 [thread overview]
Message-ID: <20260724232601.1753977-5-matthew.brost@intel.com> (raw)
In-Reply-To: <20260724232601.1753977-1-matthew.brost@intel.com>
With fine-grained page-fault locking, it no longer makes sense to
maintain multiple page-fault queues, as we no longer hash queues based
on the VM’s ASID. Multiple workers can pull page faults from a single
queue, eliminating any head-of-queue blocking. Refactor the structures
and code to use a single shared queue.
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
---
drivers/gpu/drm/xe/xe_device_types.h | 12 +++---
drivers/gpu/drm/xe/xe_pagefault.c | 51 +++++++++++++------------
drivers/gpu/drm/xe/xe_pagefault_types.h | 17 ++++++++-
3 files changed, 49 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 159ee16ee6d5..bacbe70ea541 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -303,8 +303,8 @@ struct xe_device {
struct xarray asid_to_vm;
/** @usm.next_asid: next ASID, used to cyclical alloc asids */
u32 next_asid;
- /** @usm.current_pf_queue: current page fault queue */
- u32 current_pf_queue;
+ /** @usm.current_pf_work: current page fault work item */
+ u32 current_pf_work;
/** @usm.lock: protects UM state */
struct rw_semaphore lock;
/** @usm.pagefault_wq: page fault work queue, unbound, high priority */
@@ -316,9 +316,11 @@ struct xe_device {
* yields the best bandwidth utilization of the kernel paging
* engine.
*/
-#define XE_PAGEFAULT_QUEUE_COUNT 4
- /** @usm.pf_queue: Page fault queues */
- struct xe_pagefault_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
+#define XE_PAGEFAULT_WORK_COUNT 4
+ /** @usm.pf_workers: Page fault workers */
+ struct xe_pagefault_work pf_workers[XE_PAGEFAULT_WORK_COUNT];
+ /** @usm.pf_queue: Page fault queue */
+ struct xe_pagefault_queue pf_queue;
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
/** @usm.dpagemap_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 fd7ef0718153..dd34ec2cb166 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -287,8 +287,10 @@ static void xe_pagefault_save_to_vm(struct xe_device *xe, struct xe_pagefault *p
static void xe_pagefault_queue_work(struct work_struct *w)
{
- struct xe_pagefault_queue *pf_queue =
- container_of(w, typeof(*pf_queue), worker);
+ struct xe_pagefault_work *pf_work =
+ container_of(w, typeof(*pf_work), work);
+ struct xe_device *xe = pf_work->xe;
+ struct xe_pagefault_queue *pf_queue = &xe->usm.pf_queue;
struct xe_pagefault pf;
unsigned long threshold;
@@ -318,7 +320,7 @@ static void xe_pagefault_queue_work(struct work_struct *w)
pf.producer.ops->ack_fault(&pf, err);
if (time_after(jiffies, threshold)) {
- queue_work(gt_to_xe(pf.gt)->usm.pagefault_wq, w);
+ queue_work(xe->usm.pagefault_wq, w);
break;
}
}
@@ -363,7 +365,6 @@ static int xe_pagefault_queue_init(struct xe_device *xe,
xe_pagefault_entry_size(), total_num_eus, pf_queue->size);
spin_lock_init(&pf_queue->lock);
- INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work);
pf_queue->data = drmm_kzalloc(&xe->drm, pf_queue->size, GFP_KERNEL);
if (!pf_queue->data)
@@ -397,22 +398,28 @@ int xe_pagefault_init(struct xe_device *xe)
xe->usm.pagefault_wq = alloc_workqueue("xe_page_fault_work_queue",
WQ_UNBOUND | WQ_HIGHPRI,
- XE_PAGEFAULT_QUEUE_COUNT);
+ XE_PAGEFAULT_WORK_COUNT);
if (!xe->usm.pagefault_wq)
return -ENOMEM;
xe->usm.prefetch_wq = alloc_workqueue("xe_prefetch_work_queue",
WQ_UNBOUND,
- XE_PAGEFAULT_QUEUE_COUNT);
+ XE_PAGEFAULT_WORK_COUNT);
if (!xe->usm.prefetch_wq) {
err = -ENOMEM;
goto err_pagefault_wq;
}
- for (i = 0; i < XE_PAGEFAULT_QUEUE_COUNT; ++i) {
- err = xe_pagefault_queue_init(xe, xe->usm.pf_queue + i);
- if (err)
- goto err_out;
+ err = xe_pagefault_queue_init(xe, &xe->usm.pf_queue);
+ if (err)
+ goto err_out;
+
+ for (i = 0; i < XE_PAGEFAULT_WORK_COUNT; ++i) {
+ struct xe_pagefault_work *pf_work = xe->usm.pf_workers + i;
+
+ pf_work->xe = xe;
+ pf_work->id = i;
+ INIT_WORK(&pf_work->work, xe_pagefault_queue_work);
}
return devm_add_action_or_reset(xe->drm.dev, xe_pagefault_fini, xe);
@@ -456,10 +463,7 @@ static void xe_pagefault_queue_reset(struct xe_device *xe, struct xe_gt *gt,
*/
void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt)
{
- int i;
-
- for (i = 0; i < XE_PAGEFAULT_QUEUE_COUNT; ++i)
- xe_pagefault_queue_reset(xe, gt, xe->usm.pf_queue + i);
+ xe_pagefault_queue_reset(xe, gt, &xe->usm.pf_queue);
}
static bool xe_pagefault_queue_full(struct xe_pagefault_queue *pf_queue)
@@ -474,13 +478,11 @@ static bool xe_pagefault_queue_full(struct xe_pagefault_queue *pf_queue)
* This function can race with multiple page fault producers, but worst case we
* stick a page fault on the same queue for consumption.
*/
-static int xe_pagefault_queue_index(struct xe_device *xe)
+static int xe_pagefault_work_index(struct xe_device *xe)
{
- u32 old_pf_queue = READ_ONCE(xe->usm.current_pf_queue);
-
- WRITE_ONCE(xe->usm.current_pf_queue, (old_pf_queue + 1));
+ lockdep_assert_held(&xe->usm.pf_queue.lock);
- return old_pf_queue % XE_PAGEFAULT_QUEUE_COUNT;
+ return xe->usm.current_pf_work++ % XE_PAGEFAULT_WORK_COUNT;
}
/**
@@ -495,22 +497,23 @@ static int xe_pagefault_queue_index(struct xe_device *xe)
*/
int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf)
{
- int queue_index = xe_pagefault_queue_index(xe);
- struct xe_pagefault_queue *pf_queue = xe->usm.pf_queue + queue_index;
+ struct xe_pagefault_queue *pf_queue = &xe->usm.pf_queue;
unsigned long flags;
+ int work_index;
bool full;
spin_lock_irqsave(&pf_queue->lock, flags);
+ work_index = xe_pagefault_work_index(xe);
full = xe_pagefault_queue_full(pf_queue);
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;
- queue_work(xe->usm.pagefault_wq, &pf_queue->worker);
+ queue_work(xe->usm.pagefault_wq,
+ &xe->usm.pf_workers[work_index].work);
} else {
drm_warn(&xe->drm,
- "PageFault Queue (%d) full, shouldn't be possible\n",
- queue_index);
+ "PageFault Queue full, shouldn't be possible\n");
}
spin_unlock_irqrestore(&pf_queue->lock, flags);
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index c4ee625b93dd..64ab4ea8807b 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -131,8 +131,21 @@ struct xe_pagefault_queue {
u32 tail;
/** @lock: protects page fault queue */
spinlock_t lock;
- /** @worker: to process page faults */
- struct work_struct worker;
+};
+
+/**
+ * struct xe_pagefault_work - Xe page fault work item (consumer)
+ *
+ * Represents a worker that pops a &struct xe_pagefault from the page fault
+ * queue and processes it.
+ */
+struct xe_pagefault_work {
+ /** @xe: Back-pointer to the Xe device */
+ struct xe_device *xe;
+ /** @id: Identifier for this work item */
+ int id;
+ /** @work: Work item used to process the page fault */
+ struct work_struct work;
};
#endif
--
2.34.1
next prev parent reply other threads:[~2026-07-24 23:26 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 23:25 [PATCH v8 00/12] Fine grained fault locking, threaded prefetch, storm cache Matthew Brost
2026-07-24 23:25 ` [PATCH v8 01/12] drm/xe: Fine grained page fault locking Matthew Brost
2026-07-24 23:25 ` [PATCH v8 02/12] drm/xe: Allow prefetch-only VM bind IOCTLs to use VM read lock Matthew Brost
2026-07-27 14:33 ` Francois Dugast
2026-07-24 23:25 ` [PATCH v8 03/12] drm/xe: Thread prefetch of SVM ranges Matthew Brost
2026-07-27 16:41 ` Francois Dugast
2026-07-27 19:21 ` Matthew Brost
2026-07-27 19:46 ` Matthew Brost
2026-07-27 16:49 ` Francois Dugast
2026-07-27 19:11 ` Matthew Brost
2026-07-24 23:25 ` Matthew Brost [this message]
2026-07-24 23:25 ` [PATCH v8 05/12] drm/xe: Add num_pf_work modparam Matthew Brost
2026-07-24 23:25 ` [PATCH v8 06/12] drm/xe: Engine class and instance into a u8 Matthew Brost
2026-07-24 23:25 ` [PATCH v8 07/12] drm/xe: Track pagefault worker runtime Matthew Brost
2026-07-24 23:25 ` [PATCH v8 08/12] drm/xe: Chain page faults via queue-resident cache to avoid fault storms Matthew Brost
2026-07-29 12:31 ` Francois Dugast
2026-07-29 18:20 ` Matthew Brost
2026-07-24 23:25 ` [PATCH v8 09/12] drm/xe: Add pagefault chaining stats Matthew Brost
2026-07-24 23:25 ` [PATCH v8 10/12] drm/xe: Add debugfs pagefault_info Matthew Brost
2026-07-24 23:26 ` [PATCH v8 11/12] drm/xe: batch CT pagefault acks with periodic flush Matthew Brost
2026-07-24 23:26 ` [PATCH v8 12/12] drm/xe: Track parallel page fault activity in GT stats Matthew Brost
2026-07-24 23:32 ` ✗ CI.checkpatch: warning for Fine grained fault locking, threaded prefetch, storm cache (rev8) Patchwork
2026-07-24 23:33 ` ✓ CI.KUnit: success " Patchwork
2026-07-25 0:17 ` ✓ Xe.CI.BAT: " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260724232601.1753977-5-matthew.brost@intel.com \
--to=matthew.brost@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=maciej.patelczyk@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox