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] drm/xe: Tie BO TTM LRU priority to DONTNEED/WILLNEED madvise state
Date: Tue, 20 Oct 2026 12:13:17 -0700 [thread overview]
Message-ID: <ate9TVShDPYAF2L/@gsse-cloud1.jf.intel.com> (raw)
In-Reply-To: <bd07d276ef1a575fc8bdcd37e1dd80552fb52a69.camel@linux.intel.com>
On Tue, Oct 20, 2026 at 09:00:09PM +0200, Thomas Hellström wrote:
> On Tue, 2026-10-20 at 09:51 -0700, Matthew Brost wrote:
> > The DRM_XE_MADVISE ioctl lets userspace mark a VMA's BO as DONTNEED,
> > signalling that its contents are not needed right now and may be
> > discarded under memory pressure (see madvise_purgeable() in
> > xe_vm_madvise.c), or restore it back to WILLNEED. This is a stronger,
> > explicit signal than a plain unbind: userspace has told us the BO is
> > not
> > just unused for now, but that it is actively fine to lose the data.
> > There is no reason to keep such a BO competing for LRU space with
> > actively used buffers, and conversely, once userspace marks it
> > WILLNEED
> > again it is back in active use and should be reprioritized
> > accordingly.
> >
> > Give xe two TTM LRU priority bands and tie them to that state:
> > XE_BO_PRIORITY_LOW for DONTNEED BOs and XE_BO_PRIORITY_NORMAL for
> > everything else. Since the TTM LRU is walked in ascending priority
> > order, DONTNEED BOs become the first candidates for eviction and
> > shrinking. The transition is driven from xe_bo_set_purgeable_state(),
> > the single point through which a BO's aggregate purgeable state
> > actually
> > moves between WILLNEED and DONTNEED (PURGED is terminal and left at
> > XE_BO_PRIORITY_LOW).
> >
> > Changing bo->ttm.priority cannot be done by a plain store: private,
> > user
> > BOs have bo->ttm.bulk_move set to their VM's LRU bulk-move range,
> > which
> > tracks a contiguous [first, last] run of resources per (mem_type,
> > priority) bucket. Writing the priority while the BO is still attached
> > would leave the resource linked in the old priority's bucket while
> > the
> > bulk-move code looks it up under the new one, corrupting both. Add
> > xe_bo_update_ttm_priority(), which temporarily detaches the BO from
> > its
> > bulk-move range, updates the priority with a plain non-bulk LRU
> > update,
> > and reattaches it into the new priority's bucket.
> > xe_bo_set_purgeable_state() is always called with the BO's dma-resv
> > held, so this is safe.
> >
> > With DONTNEED BOs now clustered at XE_BO_PRIORITY_LOW, the shrinker's
> > purge pass no longer needs to walk each memory type's whole LRU:
> > nothing
> > beyond XE_BO_PRIORITY_LOW can possibly be purgeable, so continuing
> > past
> > it just wastes time trylocking BOs that can never be purged in this
> > pass. Break out of the per-mem_type LRU walk in __xe_shrinker_walk()
> > once a BO's priority rises above XE_BO_PRIORITY_LOW during the purge
> > pass. Behavior of the non-purge (regular shrink) pass is unchanged.
> >
> > Assisted-by: LLM
> > Signed-off-by: Matthew Brost <matthew.brost@intel.com>
> > ---
> > drivers/gpu/drm/xe/xe_bo.c | 46
> > ++++++++++++++++++++++++++++++++
> > drivers/gpu/drm/xe/xe_bo_types.h | 8 +++++-
> > drivers/gpu/drm/xe/xe_shrinker.c | 3 +++
> > 3 files changed, 56 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> > index 6d2ac986e98a..b723addf071d 100644
> > --- a/drivers/gpu/drm/xe/xe_bo.c
> > +++ b/drivers/gpu/drm/xe/xe_bo.c
> > @@ -1086,6 +1086,38 @@ static void
> > xe_bo_set_purgeable_shrinker(struct xe_bo *bo,
> > }
> > }
> >
> > +/*
> > + * Change @bo's TTM LRU priority to @priority. @bo's dma-resv must
> > be held.
> > + *
> > + * Private, user BOs of non-fault-mode VMs have bo->ttm.bulk_move
> > set to their
> > + * VM's LRU bulk-move range (see xe_bo_create_user()), which tracks
> > a
> > + * contiguous [first, last] run of resources per (mem_type,
> > priority) bucket.
> > + * Writing bo->ttm.priority while the BO is still attached to that
> > range would
> > + * leave the resource linked in the old priority's bucket while the
> > bulk-move
> > + * code looks it up under the new one, corrupting both buckets.
> > Temporarily
> > + * detaching the BO from its bulk-move range moves it with a plain,
> > non-bulk
> > + * LRU update instead, and reattaching afterwards re-inserts it into
> > the new
> > + * priority's bucket.
> > + */
> > +static void xe_bo_update_ttm_priority(struct xe_bo *bo, int
> > priority)
> > +{
> > + struct ttm_lru_bulk_move *bulk = bo->ttm.bulk_move;
> > +
> > + xe_bo_assert_held(bo);
> > +
> > + if (bo->ttm.priority == priority)
> > + return;
>
> Hm, what about (pseudo code)
>
> spin_lock(lru_lock);
> bo->ttm.priority = priority;
> ttm_bo_move_to_lru_tail(&bo->ttm);
> spin_unlock(lru_lock);
>
> If that works, we could also shorten the comment a bit.
>
This doesn't work. IIRC this explodes in TTM, hence the way this coded.
> > +
> > + if (bulk)
> > + ttm_bo_set_bulk_move(&bo->ttm, NULL);
> > +
> > + bo->ttm.priority = priority;
> > + ttm_bo_move_to_lru_tail_unlocked(&bo->ttm);
> > +
> > + if (bulk)
> > + ttm_bo_set_bulk_move(&bo->ttm, bulk);
> > +}
> > +
> > /**
> > * xe_bo_set_purgeable_state() - Set BO purgeable state with
> > validation
> > * @bo: Buffer object
> > @@ -1095,6 +1127,13 @@ static void
> > xe_bo_set_purgeable_shrinker(struct xe_bo *bo,
> > * transitions. Once a BO is PURGED, it cannot transition to any
> > other state.
> > * Invalid transitions are caught with xe_assert(). Shrinker page
> > accounting
> > * is updated automatically.
> > + *
> > + * A WILLNEED -> DONTNEED transition also lowers the BO's TTM LRU
> > priority to
> > + * XE_BO_PRIORITY_LOW: userspace has signaled it does not need these
> > contents
> > + * again soon, so make it one of the first buffers considered for
> > + * eviction/shrinking. The shrinker's purge pass relies on this
> > clustering to
> > + * bound its LRU walk. Conversely, a DONTNEED -> WILLNEED transition
> > restores
> > + * XE_BO_PRIORITY_NORMAL, since the BO is back in active use.
>
> Is it possible to make this comment less verbose and focus on what the
> function is doing. Perhaps we could put the LRU priorities and what the
> shrinker relies on in a separate DOC section?
>
Ok, let me adjust this.
Matt
>
>
> Thanks,
> Thomas
>
>
>
>
> > */
> > void xe_bo_set_purgeable_state(struct xe_bo *bo,
> > enum xe_madv_purgeable_state
> > new_state)
> > @@ -1112,6 +1151,13 @@ void xe_bo_set_purgeable_state(struct xe_bo
> > *bo,
> > xe_assert(xe, !(bo->purgeable.state ==
> > XE_MADV_PURGEABLE_PURGED &&
> > new_state != XE_MADV_PURGEABLE_PURGED));
> >
> > + if (new_state == XE_MADV_PURGEABLE_DONTNEED &&
> > + bo->purgeable.state == XE_MADV_PURGEABLE_WILLNEED)
> > + xe_bo_update_ttm_priority(bo, XE_BO_PRIORITY_LOW);
> > + else if (new_state == XE_MADV_PURGEABLE_WILLNEED &&
> > + bo->purgeable.state == XE_MADV_PURGEABLE_DONTNEED)
> > + xe_bo_update_ttm_priority(bo,
> > XE_BO_PRIORITY_NORMAL);
> > +
> > bo->purgeable.state = new_state;
> > xe_bo_set_purgeable_shrinker(bo, new_state);
> > }
> > diff --git a/drivers/gpu/drm/xe/xe_bo_types.h
> > b/drivers/gpu/drm/xe/xe_bo_types.h
> > index 8ec4a01a0092..0265fb314c77 100644
> > --- a/drivers/gpu/drm/xe/xe_bo_types.h
> > +++ b/drivers/gpu/drm/xe/xe_bo_types.h
> > @@ -24,7 +24,13 @@ struct xe_exec_queue;
> >
> > #define XE_BO_MAX_PLACEMENTS 3
> >
> > -/* TODO: To be selected with VM_MADVISE */
> > +/*
> > + * TTM LRU priority bands. The LRU is walked in ascending priority
> > order, so
> > + * XE_BO_PRIORITY_LOW objects are the first candidates for eviction
> > and
> > + * shrinking. BOs advised DONTNEED through DRM_XE_MADVISE live at
> > + * XE_BO_PRIORITY_LOW, everything else at XE_BO_PRIORITY_NORMAL.
> > + */
> > +#define XE_BO_PRIORITY_LOW 0
> > #define XE_BO_PRIORITY_NORMAL 1
> >
> > /**
> > diff --git a/drivers/gpu/drm/xe/xe_shrinker.c
> > b/drivers/gpu/drm/xe/xe_shrinker.c
> > index 772dab9b3ed7..c86455ea08d1 100644
> > --- a/drivers/gpu/drm/xe/xe_shrinker.c
> > +++ b/drivers/gpu/drm/xe/xe_shrinker.c
> > @@ -133,6 +133,9 @@ static int __xe_shrinker_walk(struct xe_shrinker
> > *shrinker,
> > }
> >
> > ttm_bo_lru_for_each_reserved_guarded(&curs, man,
> > &arg, ttm_bo) {
> > + if (flags.purge && ttm_bo->priority >
> > XE_BO_PRIORITY_LOW)
> > + break;
> > +
> > if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
> > continue;
> >
next prev parent reply other threads:[~2026-10-20 19:13 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 16:51 [PATCH] drm/xe: Tie BO TTM LRU priority to DONTNEED/WILLNEED madvise state Matthew Brost
2026-10-20 17:03 ` ✗ CI.KUnit: failure for " Patchwork
2026-10-20 17:15 ` [PATCH] " Matthew Auld
2026-10-20 17:48 ` ✓ CI.KUnit: success for drm/xe: Tie BO TTM LRU priority to DONTNEED/WILLNEED madvise state (rev2) Patchwork
2026-10-20 18:27 ` ✓ Xe.CI.BAT: " Patchwork
2026-10-20 19:00 ` [PATCH] drm/xe: Tie BO TTM LRU priority to DONTNEED/WILLNEED madvise state Thomas Hellström
2026-10-20 19:13 ` Matthew Brost [this message]
2026-10-20 20:57 ` Thomas Hellström
2026-10-20 22:51 ` ✗ Xe.CI.FULL: failure for drm/xe: Tie BO TTM LRU priority to DONTNEED/WILLNEED madvise state (rev2) 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=ate9TVShDPYAF2L/@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