From: sashiko-bot@kernel.org
To: "Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
intel-xe@lists.freedesktop.org
Subject: Re: [PATCH v4 3/4] drm/xe: Split the bo shrinker into purgeable and non-purgeable shrinkers
Date: Fri, 16 Oct 2026 13:53:46 +0000 [thread overview]
Message-ID: <sashiko-outbox-174414@kernel.org> (raw)
In-Reply-To: <20261016133523.3622-4-thomas.hellstrom@linux.intel.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] drm/xe: Use-After-Free of shrinker during DRM device teardown
- [High] drm/xe: O(N^2) CPU lockup in purgeable shrinker due to uncounted LRU skips
--
commit 1e9348c2fe4768de2cf24e4dd9b6188400c84043
Author: Thomas Hellström <thomas.hellstrom@linux.intel.com>
drm/xe: Split the bo shrinker into purgeable and non-purgeable shrinkers
This commit splits the single xe_shrinker into two independent shrinker
instances: one for purgeable objects and another for non-purgeable
(shrinkable) objects. This allows the shrinker core to apply different
reclaim pressures based on the varying costs of reclaiming these object
types.
> diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c
> index 378022c37a57c..8e3fe675e0f34 100644
> --- a/drivers/gpu/drm/xe/xe_shrinker.c
> +++ b/drivers/gpu/drm/xe/xe_shrinker.c
[ ... ]
> @@ -216,63 +238,52 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
[ ... ]
> -static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
> +static unsigned long xe_shrinker_scan_purgeable(struct shrinker *shrink,
> + struct shrink_control *sc)
> {
> struct xe_shrinker *shrinker = to_xe_shrinker(shrink);
> struct ttm_operation_ctx ctx = {
> .interruptible = false,
> .no_wait_gpu = ttm_bo_shrink_avoid_wait(),
> };
> - unsigned long nr_to_scan, nr_scanned = 0, freed = 0;
> + unsigned long nr_to_scan = sc->nr_to_scan, nr_scanned = 0, freed = 0;
> struct xe_bo_shrink_flags shrink_flags = {
> .purge = true,
> - /* Don't request writeback without __GFP_IO. */
> - .writeback = !ctx.no_wait_gpu && (sc->gfp_mask & __GFP_IO),
> };
> - bool runtime_pm;
> - bool purgeable;
> - bool can_backup = !!(sc->gfp_mask & __GFP_FS);
> -
> - nr_to_scan = sc->nr_to_scan;
> -
> - read_lock(&shrinker->lock);
> - purgeable = !!shrinker->purgeable_pages;
> - read_unlock(&shrinker->lock);
>
> - /* Might need runtime PM. Try to wake early if it looks like it. */
> - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
> -
> - if (purgeable && nr_scanned < nr_to_scan)
> - xe_shrinker_walk(shrinker, &ctx, shrink_flags,
> - nr_to_scan, &nr_scanned, &freed);
> + xe_shrinker_walk(shrinker, &ctx, shrink_flags, nr_to_scan, &nr_scanned, &freed);
>
> sc->nr_scanned = nr_scanned;
[Severity: High]
Can this cause an O(N^2) CPU lockup in the purgeable shrinker?
When the memory reclaim core (do_shrink_slab) calls this purgeable scan
routine under memory pressure, xe_shrinker_walk() iterates the LRU list.
However, non-purgeable BOs return -EBUSY and are skipped without advancing
the scanned counter:
drivers/gpu/drm/xe/xe_bo.c:xe_bo_shrink() {
...
if (!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE) ||
(flags.purge && !xe_tt->purgeable))
return -EBUSY;
...
}
drivers/gpu/drm/xe/xe_shrinker.c:__xe_shrinker_walk() {
...
lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
/* Like the TTM LRU walk, skip bos we can't shrink now. */
if (lret == -EBUSY)
continue;
...
}
Since the LRU cursor is initialized locally on each call and non-purgeable
BOs do not increment nr_scanned, every batch rescans all leading
non-purgeable BOs without significantly advancing sc->nr_scanned.
Will this force the shrinker core to repeatedly call this function,
processing the same leading non-purgeable BOs over and over again?
> - if (nr_scanned >= nr_to_scan || !can_backup)
> - goto out;
>
> - /* If we didn't wake before, try to do it now if needed. */
> - if (!runtime_pm)
> - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, true, 0, can_backup);
> + return nr_scanned ? freed : SHRINK_STOP;
> +}
[ ... ]
> @@ -294,6 +305,7 @@ static void xe_shrinker_fini(struct drm_device *drm, void *arg)
> xe_assert(shrinker->xe, !shrinker->purgeable_pages);
> xe_assert(shrinker->xe, !shrinker->populated_tts);
> shrinker_free(shrinker->shrink);
> + shrinker_free(shrinker->purge_shrink);
> flush_work(&shrinker->pm_worker);
> kfree(shrinker);
> }
[Severity: High]
Does this kfree(shrinker) leave xe->mem.shrinker as a dangling pointer?
Because drmm actions execute in LIFO order upon device release,
xe_shrinker_fini() is called before xe_device_destroy().
Later, xe_device_destroy() runs and calls xe_bo_dev_fini() which flushes
the async_free workqueue. Pending BOs in the workqueue will have their
final references dropped, leading to xe_ttm_tt_account_subtract():
drivers/gpu/drm/xe/xe_bo.c:xe_ttm_tt_account_subtract() {
...
if (xe_tt->purgeable)
xe_shrinker_mod_pages(xe->mem.shrinker, 0, -(long)tt->num_pages, -1);
else
xe_shrinker_mod_pages(xe->mem.shrinker, -(long)tt->num_pages, 0, -1);
...
}
Can this lead to a use-after-free and memory corruption when driver unloads?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016133523.3622-1-thomas.hellstrom@linux.intel.com?part=3
next prev parent reply other threads:[~2026-10-16 13:53 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 13:35 [PATCH v4 0/4] drm/xe: Adjust the bo shrinker batch size Thomas Hellström
2026-10-16 13:35 ` [PATCH v4 1/4] drm/xe: Don't abort the shrinker walk on -EBUSY Thomas Hellström
2026-10-16 13:44 ` sashiko-bot
2026-10-16 13:55 ` Matthew Brost
2026-10-16 13:35 ` [PATCH v4 2/4] drm/xe: Track number of populated ttm_tts in the shrinker Thomas Hellström
2026-10-16 14:26 ` Matthew Brost
2026-10-16 13:35 ` [PATCH v4 3/4] drm/xe: Split the bo shrinker into purgeable and non-purgeable shrinkers Thomas Hellström
2026-10-16 13:53 ` sashiko-bot [this message]
2026-10-16 14:09 ` Thomas Hellström
2026-10-16 18:41 ` Matthew Brost
2026-10-16 18:16 ` Matthew Brost
2026-10-16 13:35 ` [PATCH v4 4/4] drm/xe: Update non-purgeable shrinker batch size based on average BO size Thomas Hellström
2026-10-16 13:45 ` sashiko-bot
2026-10-16 14:06 ` Thomas Hellström
2026-10-16 18:42 ` Matthew Brost
2026-10-16 18:44 ` Matthew Brost
2026-10-16 13:45 ` ✓ CI.KUnit: success for drm/xe: Adjust the bo shrinker batch size (rev4) Patchwork
2026-10-16 14:37 ` ✓ Xe.CI.BAT: " Patchwork
2026-10-16 22:14 ` ✓ Xe.CI.FULL: " 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=sashiko-outbox-174414@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=sashiko-reviews@lists.linux.dev \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.