diff options
| author | Zhenhao Wan <whi4ed0g@gmail.com> | 2026-08-11 22:28:51 +0800 |
|---|---|---|
| committer | Danilo Krummrich <dakr@kernel.org> | 2026-09-01 11:09:24 +0200 |
| commit | c2256c044a1df39c8aad4dd2d6f709b2533e2d7a (patch) | |
| tree | c3850fc14a1acd9e66ddab6bcba7534b28d242c2 | |
| parent | caa1bc2a0a6ca19dcb90bbf88208b0fe2decd66f (diff) | |
| download | linux-next-c2256c044a1df39c8aad4dd2d6f709b2533e2d7a.tar.gz linux-next-c2256c044a1df39c8aad4dd2d6f709b2533e2d7a.zip | |
drm/nouveau/dmem: fix callocated underflow on large folio split
nouveau_dmem_folio_free() drops chunk->callocated once per freed folio,
while a large (compound) device-private folio is only counted once when
it is allocated. When such a folio is split, the mm core invokes
->folio_split() (nouveau_dmem_folio_split()) once for each new
sub-folio, but the hook only fixes up the sub-folio metadata and leaves
chunk->callocated unchanged.
Each resulting sub-folio is later freed separately, so after a split
the single allocation (+1) is met by N frees (-N), leaving
chunk->callocated short by N-1. On the first split/free cycle it
underflows: WARN_ON(!chunk->callocated) fires, the unsigned counter
wraps and never returns to zero, so the chunk can no longer be
reclaimed (nouveau_dmem_fini() also warns on the leaked count).
Account for the new sub-folio in the split hook, under the same lock as
nouveau_dmem_folio_free(), so the count stays balanced.
Fixes: c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory migration")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Assisted-by: Claude:claude-opus-5
Cc: stable@vger.kernel.org
Signed-off-by: Zhenhao Wan <whi4ed0g@gmail.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Link: https://patch.msgid.link/20260811-b4-nouveau-dmem-thp-fixes-v1-2-2cdf9860af2a@gmail.com
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dmem.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index d2abee3efb9a..ad4570c50be7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -279,11 +279,25 @@ err: static void nouveau_dmem_folio_split(struct folio *head, struct folio *tail) { + struct nouveau_dmem_chunk *chunk; + struct nouveau_dmem *dmem; + if (tail == NULL) return; tail->pgmap = head->pgmap; tail->mapping = head->mapping; folio_set_zone_device_data(tail, folio_zone_device_data(head)); + + /* + * The split hands out a new independently-freeable folio that will + * later be released via nouveau_dmem_folio_free(); account for it so + * chunk->callocated stays balanced. + */ + chunk = nouveau_page_to_chunk(&head->page); + dmem = chunk->drm->dmem; + spin_lock(&dmem->lock); + chunk->callocated++; + spin_unlock(&dmem->lock); } static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = { |
