From: Matthew Brost <matthew.brost@intel.com>
To: "Thomas Hellström" <thomas.hellstrom@linux.intel.com>
Cc: <intel-xe@lists.freedesktop.org>
Subject: Re: [PATCH v6 24/24] drm/xe: Document ULLS for migration jobs
Date: Wed, 9 Sep 2026 10:53:06 -0700 [thread overview]
Message-ID: <aqGdAl+RM5X07May@gsse-cloud1.jf.intel.com> (raw)
In-Reply-To: <ebda3d9ad695bf5fcb69c67d98afcd2098c5acad.camel@linux.intel.com>
On Wed, Sep 09, 2026 at 11:01:50AM +0200, Thomas Hellström wrote:
> On Fri, 2026-09-04 at 14:16 -0700, Matthew Brost wrote:
> > Add a kernel-doc DOC section at the top of xe_migrate.c describing
> > the
> > Ultra Low Latency Submission (ULLS) scheme used for migration jobs.
> >
> > Cover the motivation (removing the H2G / GuC / context switch latency
> > from the page fault and SVM prefetch critical paths), the platform
> > requirements, the LRC PPHWSP semaphore layout and its relationship to
> > the migration queue job count, the fixed ULLS job size and why it is
> > needed, the ring preamble / postamble emitted by the ring ops
> > including
> > the in-ring tail update, the semaphore-only submission fast path in
> > the
> > GuC backend, and the enter / delayed exit flow along with the ULLS
> > job
> > flags.
> >
> > Hook the new section into Documentation/gpu/xe/xe_migrate.rst.
> >
> > Signed-off-by: Matthew Brost <matthew.brost@intel.com>
> > Assisted-by: Github-Copilot:Claude-opus-5
> > ---
> > Documentation/gpu/xe/xe_migrate.rst | 3 +
> > drivers/gpu/drm/xe/xe_migrate.c | 146
> > ++++++++++++++++++++++++++++
> > 2 files changed, 149 insertions(+)
> >
> > diff --git a/Documentation/gpu/xe/xe_migrate.rst
> > b/Documentation/gpu/xe/xe_migrate.rst
> > index f92faec0ac94..d297ee53a582 100644
> > --- a/Documentation/gpu/xe/xe_migrate.rst
> > +++ b/Documentation/gpu/xe/xe_migrate.rst
> > @@ -6,3 +6,6 @@ Migrate Layer
> >
> > .. kernel-doc:: drivers/gpu/drm/xe/xe_migrate_doc.h
> > :doc: Migrate Layer
> > +
> > +.. kernel-doc:: drivers/gpu/drm/xe/xe_migrate.c
> > + :doc: ULLS (Ultra Low Latency Submission) for migration jobs
> > diff --git a/drivers/gpu/drm/xe/xe_migrate.c
> > b/drivers/gpu/drm/xe/xe_migrate.c
> > index 3bc78f761f23..94ad1e7e8bc4 100644
> > --- a/drivers/gpu/drm/xe/xe_migrate.c
> > +++ b/drivers/gpu/drm/xe/xe_migrate.c
> > @@ -48,6 +48,152 @@
> > #include "xe_vm.h"
> > #include "xe_vram.h"
> >
> > +/**
> > + * DOC: ULLS (Ultra Low Latency Submission) for migration jobs
> > + *
> > + * Migration jobs issued on behalf of GPU page faults and SVM
> > prefetches sit
> > + * directly in the critical path of a stalled GPU workload. The
> > dominant cost
> > + * of such a job is not the copy or clear itself but the submission
> > latency:
> > + * the H2G round trip to GuC, the GuC scheduling decision, and the
> > hardware
> > + * context switch required to place the migration LRC on an engine.
> > + *
> > + * ULLS removes that cost by keeping the migration context resident
> > and
> > + * *running* on the hardware engine across jobs. Instead of the ring
> > going
> > + * empty and the context being switched out between jobs, the tail
> > of every
> > + * ULLS job parks the engine on a semaphore wait for the *next*
> > job's
> > + * semaphore, and then advances the ring tail itself. Submitting the
> > next job
> > + * therefore costs the CPU a single write to signal that semaphore -
> > no H2G,
> > + * no GuC round trip, no context switch, no MMIO.
>
> This all assumes the migration LRC empties between jobs. How common is
> that to the case where a new job can modify the ring tail before the
All I have so far is data from UMD stream access benchmarks, which show
roughly a 50% increase across the board, GT statistics showing a 20-30
µs latency reduction per copy job across various IGTs, and a prefetch
bandwidth IGT showing approximately a 7 GB/s bandwidth increase on our
highest-end BMG part. All of these results point to excessive
context-switching overhead, as the ring must either go idle or initiate
a context switch.
> previous job finished? Will the HW autotail feature affect the
> usefulness of the ULLS migration jobs?
>
The auto-tail feature appears to be based on the same concept: allowing
contexts to spin on LRC tail updates until they are context-switched
out. The documentation is fairly sparse, though. I found bspec67276 and
HSD 220160875, which seem to indicate that this is supported on BMG. Do
you know if there is better documentation available?
I also haven't been able to find any KMD patches that enable this, which
seems a bit odd. If we can get auto-tail to provide roughly the same
benefits without impacting other clients, it may be worth investigating.
In my opinion, though, that should be done as a follow-up and added to
the backlog.
The ULLS patches are thoroughly tested, relatively small in terms of
both lines of code and complexity, and can be reverted if an alternative
solution proves to be equally effective.
> Also worth adding is a discussion around semaphore context switch-out
> when stalled, like whether we're inhibiting that explicitly, whether
> the engine is assumed to be single-context etc.
Let me add that. I don't disable context switch-out, and having a single
queue on the engine is not explicitly required. However, for practical
purposes, the idea falls apart with more than one queue, since you don't
want to delay another queue from being switched in while a semaphore is
spinning for the duration of the timeslice period (1 ms by default).
My idea was that if we need more than one queue on the paging engine,
the other queues would detect that ULLS is running and issue an
early-exit ULLS job before their submission. Likewise, we would elide
ULLS entry whenever other queues are non-idle.
Matt
>
>
> > + *
> > + * Requirements
> > + * ------------
> > + *
> > + * ULLS is only used on DGFX with USM support (where a hardware
> > engine is
> > + * reserved exclusively for migration jobs). Because the engine is
> > spinning
> > + * on a semaphore while ULLS is active, it can not be shared with
> > user
> > + * submissions. It can also be disabled at load time with the
> > + * ``xe.ulls_enable`` module parameter.
>
> Update if decide to use per-device sysfs entry.
>
> Otherwise LGTM.
>
> /Thomas
>
>
>
>
> > + *
> > + * Fixed size jobs
> > + * ---------------
> > + *
> > + * A job updates the ring tail to cover its successor, but it is
> > emitted long
> > + * before that successor exists, so it can not know how much ring
> > the
> > + * successor will occupy. Every ULLS job is therefore padded out to
> > exactly
> > + * ULLS_JOB_SIZE_BYTES, which lets the next tail be computed
> > arithmetically
> > + * from where the current job started.
> > + *
> > + * This is why the shorter jobs still have to reach the same size:
> > the "last"
> > + * job skips the batch buffers and the postamble, and pads the
> > difference with
> > + * MI_NOOP. The "first" job is not covered by any predecessor's tail
> > update
> > + * and so is unconstrained, but is padded anyway to keep the
> > arithmetic
> > + * uniform.
> > + *
> > + * Leaving ULLS mode always goes through a "last" job, which emits
> > no tail
> > + * update, so an ordinary variable length migration job never
> > follows a
> > + * prediction.
> > + *
> > + * Semaphores
> > + * ----------
> > + *
> > + * The semaphores live in the driver-defined portion of the
> > migration LRC's
> > + * PPHWSP (see LRC_ULLS_PPHWSP_OFFSET, mutually exclusive with the
> > parallel
> > + * submission area). There are LRC_MIGRATION_ULLS_SEMAPHORE_COUNT of
> > them and
> > + * a job's semaphore is selected by ``seqno % COUNT``, so the
> > semaphore ring
> > + * wraps with the job seqnos. To guarantee a job can never overwrite
> > the
> > + * semaphore of a job still in flight, the GuC backend caps the
> > migration
> > + * queue's scheduler job count at LRC_MIGRATION_ULLS_SEMAPHORE_COUNT
> > - 1.
> > + *
> > + * Ring layout of a ULLS job
> > + * -------------------------
> > + *
> > + * Emitted by emit_migration_job_gen12() in xe_ring_ops.c::
> > + *
> > + * preamble: clear semaphore[seqno] (reuse for a later
> > wrap)
> > + * <copy timestamp, start seqno store>
> > + * <batch buffer start(s)> (skipped on
> > first/last job)
> > + * <seqno write + user interrupt>
> > + * postamble: SDI saved ring tail = end of next job
> > + * LRI RING_TAIL = end of next job
> > + * wait on semaphore[seqno + 1]
> > + * (skipped on the last
> > job)
> > + * pad: MI_NOOP up to ULLS_JOB_SIZE_DW
> > + *
> > + * The preamble clears the current job's semaphore so it can be
> > reused once
> > + * the seqno space wraps. The postamble is what keeps the engine
> > busy: it
> > + * advances the ring tail over the next job and then blocks on that
> > job's
> > + * semaphore, which is only signaled when the job is actually
> > submitted. It
> > + * advances the saved tail as well as the tail register, keeping the
> > two in
> > + * step without any help from the CPU, so a context save and restore
> > can not
> > + * rewind the tail behind work which has already been published.
> > + *
> > + * The tail register write must be non-posted, i.e. it must not
> > carry
> > + * MI_LRI_FORCE_POSTED. Posted, the new tail is free to land after
> > the command
> > + * streamer has already drained the rest of the job, at which point
> > the command
> > + * streamer sees head == the old tail and parks as though the ring
> > were empty.
> > + * A parked context can be switched off the hardware, and the fast
> > path below
> > + * has no H2G with which to ask GuC to bring it back.
> > + *
> > + * The tail is published ahead of the semaphore wait rather than
> > after it so
> > + * that the non-posted write drains while the engine is parked
> > anyway, keeping
> > + * a register round trip off the path between the semaphore being
> > signaled and
> > + * the next job running.
> > + *
> > + * Submission fast path
> > + * --------------------
> > + *
> > + * In submit_exec_queue() (xe_guc_submit.c), a ULLS job that is not
> > the first
> > + * one reduces to::
> > + *
> > + * xe_lrc_set_ulls_semaphore(lrc, seqno); release
> > previous job
> > + *
> > + * The XE_GUC_ACTION_SCHED_CONTEXT H2G is suppressed, and so is the
> > write of
> > + * the saved ring tail: the previous job's postamble has already
> > published
> > + * this job's tail both in the tail register and in the context
> > image, so the
> > + * semaphore signal is all that is left. The previous job's
> > semaphore wait is
> > + * satisfied and the engine walks straight into this job.
> > + *
> > + * This does assume the context stays resident for as long as ULLS
> > mode is
> > + * active. Nothing else is scheduled on the reserved engine, so the
> > only ways
> > + * off the hardware are the "last" job below, or a reset - and a
> > migration job
> > + * failing already wedges the device.
> > + *
> > + * Enter / exit
> > + * ------------
> > + *
> > + * xe_migrate_ulls_enter() is called from the page fault handler and
> > from the
> > + * SVM prefetch path, i.e. exactly where low latency migration
> > matters. It
> > + * takes a PM runtime reference (the device must not suspend while
> > the engine
> > + * spins), then submits a "first" ULLS job. That first job carries
> > no batch
> > + * buffer; it exists only to get the context onto the hardware
> > through the
> > + * normal GuC path and to leave the engine waiting on the next
> > semaphore,
> > + * pipelining the GuC/HW context switch out of the critical path.
> > + *
> > + * No forcewake reference is required. Nothing in the fast path
> > touches MMIO,
> > + * and the engine keeps itself awake for as long as it is executing
> > the ring.
> > + * Not needing host MMIO access is also what lets ULLS run on SRIOV
> > VFs.
> > + *
> > + * Keeping an engine spinning costs power, so ULLS is not left
> > enabled
> > + * indefinitely. Every enter and every ULLS job submission re-arms
> > + * @xe_migrate.ulls.exit_work with a ULLS_EXIT_JIFFIES delay. When
> > it fires
> > + * with the queue idle, it submits a "last" ULLS job - again with no
> > batch
> > + * buffer and, crucially, with no postamble semaphore wait or tail
> > update -
> > + * which lets the ring drain so the context can be switched off the
> > hardware.
> > + * The PM reference is then dropped. If the queue was not idle, the
> > worker
> > + * simply re-arms itself.
> > + *
> > + * Job state
> > + * ---------
> > + *
> > + * The state above is communicated to the ring ops and GuC backend
> > via
> > + * @xe_sched_job.ulls, set under @xe_migrate.job_mutex:
> > + *
> > + * - %ULLS_NONE: job submitted outside of ULLS mode
> > + * - %ULLS_ENTER: job that enters ULLS mode
> > + * - %ULLS_ACTIVE: job submitted while in ULLS mode
> > + * - %ULLS_EXIT: job that exits ULLS mode
> > + */
> > +
> > /**
> > * struct xe_migrate - migrate context.
> > */
next prev parent reply other threads:[~2026-09-09 17:53 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 21:15 [PATCH v6 00/24] CPU binds and ULLS on migration queue Matthew Brost
2026-09-04 21:15 ` [PATCH v6 01/24] drm/xe: Drop struct xe_migrate_pt_update argument from populate/clear vfuns Matthew Brost
2026-09-04 21:15 ` [PATCH v6 02/24] drm/xe: Add xe_migrate_update_pgtables_cpu_execute helper Matthew Brost
2026-09-04 21:28 ` sashiko-bot
2026-09-04 21:15 ` [PATCH v6 03/24] drm/xe: Decouple exec queue idle check from LRC Matthew Brost
2026-09-04 21:15 ` [PATCH v6 04/24] drm/xe: Add job count to GuC exec queue snapshot Matthew Brost
2026-09-04 21:23 ` sashiko-bot
2026-09-04 21:15 ` [PATCH v6 05/24] drm/xe: Update xe_bo_put_deferred arguments to include writeback flag Matthew Brost
2026-09-04 21:15 ` [PATCH v6 06/24] drm/xe: Add XE_BO_FLAG_PUT_VM_ASYNC Matthew Brost
2026-09-04 21:33 ` sashiko-bot
2026-09-11 13:10 ` Francois Dugast
2026-09-11 19:54 ` Matthew Brost
2026-09-12 0:27 ` Matthew Brost
2026-09-04 21:15 ` [PATCH v6 07/24] drm/xe: Update scheduler job layer to support PT jobs Matthew Brost
2026-09-04 21:37 ` sashiko-bot
2026-09-11 15:24 ` Francois Dugast
2026-09-11 19:25 ` Matthew Brost
2026-09-04 21:15 ` [PATCH v6 08/24] drm/xe: Add helpers to access PT ops Matthew Brost
2026-09-04 21:15 ` [PATCH v6 09/24] drm/xe: Add struct xe_pt_job_ops Matthew Brost
2026-09-04 21:40 ` sashiko-bot
2026-09-04 21:15 ` [PATCH v6 10/24] drm/xe: Update GuC submission backend to run PT jobs Matthew Brost
2026-09-04 21:39 ` sashiko-bot
2026-09-04 21:16 ` [PATCH v6 11/24] drm/xe: Store level in struct xe_vm_pgtable_update Matthew Brost
2026-09-04 21:16 ` [PATCH v6 12/24] drm/xe: Don't use migrate exec queue for page fault binds Matthew Brost
2026-09-04 21:16 ` [PATCH v6 13/24] drm/xe: Enable CPU binds for jobs Matthew Brost
2026-09-04 21:44 ` sashiko-bot
2026-09-04 21:16 ` [PATCH v6 14/24] drm/xe: Remove unused arguments from xe_migrate_pt_update_ops Matthew Brost
2026-09-04 21:16 ` [PATCH v6 15/24] drm/xe: Make bind queues operate cross-tile Matthew Brost
2026-09-04 21:16 ` [PATCH v6 16/24] drm/xe: Add CPU bind layer Matthew Brost
2026-09-04 21:50 ` sashiko-bot
2026-09-04 21:16 ` [PATCH v6 17/24] drm/xe: Add device flag to enable PT mirroring across tiles Matthew Brost
2026-09-04 21:40 ` sashiko-bot
2026-09-04 21:16 ` [PATCH v6 18/24] drm/xe: Add ULLS support to LRC Matthew Brost
2026-09-04 21:16 ` [PATCH v6 19/24] drm/xe: Add ULLS migration job support to migration layer Matthew Brost
2026-09-04 21:40 ` sashiko-bot
2026-09-04 21:16 ` [PATCH v6 20/24] drm/xe: Add ULLS migration job support to ring ops Matthew Brost
2026-09-04 21:16 ` [PATCH v6 21/24] drm/xe: Add ULLS migration job support to GuC submission Matthew Brost
2026-09-04 21:16 ` [PATCH v6 22/24] drm/xe: Enter ULLS for migration jobs upon page fault or SVM prefetch Matthew Brost
2026-09-04 21:16 ` [PATCH v6 23/24] drm/xe: Add modparam to enable / disable ULLS on migrate queue Matthew Brost
2026-09-09 8:03 ` Thomas Hellström
2026-09-09 18:11 ` Matthew Brost
2026-09-04 21:16 ` [PATCH v6 24/24] drm/xe: Document ULLS for migration jobs Matthew Brost
2026-09-09 9:01 ` Thomas Hellström
2026-09-09 17:53 ` Matthew Brost [this message]
2026-09-04 21:24 ` ✗ CI.checkpatch: warning for CPU binds and ULLS on migration queue (rev8) Patchwork
2026-09-04 21:26 ` ✓ CI.KUnit: success " Patchwork
2026-09-04 22:16 ` ✓ Xe.CI.BAT: " Patchwork
2026-09-05 3:34 ` ✗ Xe.CI.FULL: failure " 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=aqGdAl+RM5X07May@gsse-cloud1.jf.intel.com \
--to=matthew.brost@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=thomas.hellstrom@linux.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