* [PATCH v2] Memory leak error in qxl unbind
@ 2026-06-28 15:40 Óscar Megía López
2026-06-28 15:46 ` David Kahurani
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Óscar Megía López @ 2026-06-28 15:40 UTC (permalink / raw)
To: Christian Koenig
Cc: Óscar Megía López, Huang Rui, Matthew Auld,
Matthew Brost, dri-devel, linux-kernel-mentees
I discovered OOM after run script below around two hours in my computer:
while :; do
echo [pci qxl id] > /sys/bus/pci/drivers/qxl/unbind
echo [pci qxl id] > /sys/bus/pci/drivers/qxl/bind
done
If you run 10000 times above script kmemleak does not report any
memory leak, but if you run above script about two hours several
OOM ocurs and at the end kernel panic.
The OOM isn't just a simple leak; it's a refcount corruption that renders
the list_lru fix dead code after the first mid-init failure.
Here's the chain:
Bug 1: ttm_global_init ignores ttm_pool_mgr_init() return
If shrinker_alloc() fails under memory pressure, ttm_pool_mgr_init
returns -ENOMEM with pool types already initialized (64 list_lru_init
calls done). ttm_global_init ignored this and returned 0, leaving orphaned
pool types with a NULL mm_shrinker.
Fix: Check ret from ttm_pool_mgr_init; if non-zero, goto out cleans up
refcount + debugfs.
Bug 2: ttm_pool_mgr_init leaks pool types on shrinker_alloc failure
If shrinker_alloc fails after all 64 pool types were list_lru_init'd,
the function returned -ENOMEM without undoing them. With Bug 1 now
triggering proper error handling, this undo is necessary.
Fix: err_shrinker: label that finalizes + destroys all 64 pool types
before returning.
You must apply the patch from the link "[PATCH v2] drm/qxl: fix
use-after-free to qxl_irq_handler in PCI mode" before testing.
If you don't apply this patch, you will get a UAF error when running
the script above.
Assisted-by: OpenCode:1.17.8-Big Pickle
Link: https://lore.kernel.org/virtualization/20260627105445.89827-2-megia.oscar@gmail.com/T/#u
Signed-off-by: Óscar Megía López <megia.oscar@gmail.com>
---
drivers/gpu/drm/ttm/ttm_device.c | 5 ++++-
drivers/gpu/drm/ttm/ttm_pool.c | 37 ++++++++++++++++++++++++++------
include/drm/ttm/ttm_pool.h | 2 ++
3 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
index d3bfb9a696a7..c880a0430363 100644
--- a/drivers/gpu/drm/ttm/ttm_device.c
+++ b/drivers/gpu/drm/ttm/ttm_device.c
@@ -96,7 +96,10 @@ static int ttm_global_init(void)
>> PAGE_SHIFT;
num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
- ttm_pool_mgr_init(num_pages);
+ ret = ttm_pool_mgr_init(num_pages);
+ if (ret)
+ goto out;
+
ttm_tt_mgr_init(num_pages, num_dma32);
glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32 |
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 278bbe7a11ad..e0c82804a07d 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -1198,6 +1198,17 @@ void ttm_pool_fini(struct ttm_pool *pool)
* that no shrinker is concurrently freeing pages from the pool.
*/
ttm_pool_synchronize_shrinkers();
+
+ for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
+ for (j = 0; j < NR_PAGE_ORDERS; ++j) {
+ struct ttm_pool_type *pt;
+
+ pt = ttm_pool_select_type(pool, i, j);
+ if (pt != &pool->caching[i].orders[j])
+ continue;
+ list_lru_destroy(&pt->pages);
+ }
+ }
}
EXPORT_SYMBOL(ttm_pool_fini);
@@ -1386,6 +1397,7 @@ static inline u64 ttm_get_node_memory_size(int nid)
int ttm_pool_mgr_init(unsigned long num_pages)
{
unsigned int i;
+ int ret = 0;
int nid;
for_each_node(nid) {
@@ -1423,8 +1435,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
#endif
mm_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE, "drm-ttm_pool");
- if (!mm_shrinker)
- return -ENOMEM;
+ if (!mm_shrinker) {
+ ret = -ENOMEM;
+ goto err_shrinker;
+ }
mm_shrinker->count_objects = ttm_pool_shrinker_count;
mm_shrinker->scan_objects = ttm_pool_shrinker_scan;
@@ -1434,6 +1448,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
shrinker_register(mm_shrinker);
return 0;
+
+err_shrinker:
+ ttm_pool_type_fini_and_list_lru_destroy();
+ return ret;
}
/**
@@ -1442,17 +1460,24 @@ int ttm_pool_mgr_init(unsigned long num_pages)
* Cleanup the global pools and unregister the MM shrinker.
*/
void ttm_pool_mgr_fini(void)
+{
+ shrinker_free(mm_shrinker);
+ ttm_pool_type_fini_and_list_lru_destroy();
+ WARN_ON(!list_empty(&shrinker_list));
+}
+
+void ttm_pool_type_fini_and_list_lru_destroy(void)
{
unsigned int i;
for (i = 0; i < NR_PAGE_ORDERS; ++i) {
ttm_pool_type_fini(&global_write_combined[i]);
+ list_lru_destroy(&global_write_combined[i].pages);
ttm_pool_type_fini(&global_uncached[i]);
-
+ list_lru_destroy(&global_uncached[i].pages);
ttm_pool_type_fini(&global_dma32_write_combined[i]);
+ list_lru_destroy(&global_dma32_write_combined[i].pages);
ttm_pool_type_fini(&global_dma32_uncached[i]);
+ list_lru_destroy(&global_dma32_uncached[i].pages);
}
-
- shrinker_free(mm_shrinker);
- WARN_ON(!list_empty(&shrinker_list));
}
diff --git a/include/drm/ttm/ttm_pool.h b/include/drm/ttm/ttm_pool.h
index 26ee592e1994..bda8e816a206 100644
--- a/include/drm/ttm/ttm_pool.h
+++ b/include/drm/ttm/ttm_pool.h
@@ -97,4 +97,6 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
int ttm_pool_mgr_init(unsigned long num_pages);
void ttm_pool_mgr_fini(void);
+void ttm_pool_type_fini_and_list_lru_destroy(void);
+
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] Memory leak error in qxl unbind
2026-06-28 15:40 [PATCH v2] Memory leak error in qxl unbind Óscar Megía López
@ 2026-06-28 15:46 ` David Kahurani
2026-06-29 7:21 ` sashiko-bot
2026-06-29 8:37 ` Christian König
2 siblings, 0 replies; 4+ messages in thread
From: David Kahurani @ 2026-06-28 15:46 UTC (permalink / raw)
To: Óscar Megía López
Cc: Christian Koenig, Huang Rui, Matthew Auld, Matthew Brost,
dri-devel, linux-kernel-mentees, qemu-devel
On Sun, Jun 28, 2026 at 6:41 PM Óscar Megía López <megia.oscar@gmail.com> wrote:
>
> I discovered OOM after run script below around two hours in my computer:
>
> while :; do
> echo [pci qxl id] > /sys/bus/pci/drivers/qxl/unbind
> echo [pci qxl id] > /sys/bus/pci/drivers/qxl/bind
> done
>
> If you run 10000 times above script kmemleak does not report any
> memory leak, but if you run above script about two hours several
> OOM ocurs and at the end kernel panic.
>
> The OOM isn't just a simple leak; it's a refcount corruption that renders
> the list_lru fix dead code after the first mid-init failure.
>
> Here's the chain:
>
> Bug 1: ttm_global_init ignores ttm_pool_mgr_init() return
> If shrinker_alloc() fails under memory pressure, ttm_pool_mgr_init
> returns -ENOMEM with pool types already initialized (64 list_lru_init
> calls done). ttm_global_init ignored this and returned 0, leaving orphaned
> pool types with a NULL mm_shrinker.
>
> Fix: Check ret from ttm_pool_mgr_init; if non-zero, goto out cleans up
> refcount + debugfs.
>
> Bug 2: ttm_pool_mgr_init leaks pool types on shrinker_alloc failure
> If shrinker_alloc fails after all 64 pool types were list_lru_init'd,
> the function returned -ENOMEM without undoing them. With Bug 1 now
> triggering proper error handling, this undo is necessary.
>
> Fix: err_shrinker: label that finalizes + destroys all 64 pool types
> before returning.
>
> You must apply the patch from the link "[PATCH v2] drm/qxl: fix
> use-after-free to qxl_irq_handler in PCI mode" before testing.
> If you don't apply this patch, you will get a UAF error when running
> the script above.
>
> Assisted-by: OpenCode:1.17.8-Big Pickle
> Link: https://lore.kernel.org/virtualization/20260627105445.89827-2-megia.oscar@gmail.com/T/#u
> Signed-off-by: Óscar Megía López <megia.oscar@gmail.com>
> ---
> drivers/gpu/drm/ttm/ttm_device.c | 5 ++++-
> drivers/gpu/drm/ttm/ttm_pool.c | 37 ++++++++++++++++++++++++++------
> include/drm/ttm/ttm_pool.h | 2 ++
> 3 files changed, 37 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
> index d3bfb9a696a7..c880a0430363 100644
> --- a/drivers/gpu/drm/ttm/ttm_device.c
> +++ b/drivers/gpu/drm/ttm/ttm_device.c
> @@ -96,7 +96,10 @@ static int ttm_global_init(void)
> >> PAGE_SHIFT;
> num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
>
> - ttm_pool_mgr_init(num_pages);
> + ret = ttm_pool_mgr_init(num_pages);
> + if (ret)
> + goto out;
> +
> ttm_tt_mgr_init(num_pages, num_dma32);
>
> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32 |
> diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
> index 278bbe7a11ad..e0c82804a07d 100644
> --- a/drivers/gpu/drm/ttm/ttm_pool.c
> +++ b/drivers/gpu/drm/ttm/ttm_pool.c
> @@ -1198,6 +1198,17 @@ void ttm_pool_fini(struct ttm_pool *pool)
> * that no shrinker is concurrently freeing pages from the pool.
> */
> ttm_pool_synchronize_shrinkers();
> +
> + for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
> + for (j = 0; j < NR_PAGE_ORDERS; ++j) {
> + struct ttm_pool_type *pt;
> +
> + pt = ttm_pool_select_type(pool, i, j);
> + if (pt != &pool->caching[i].orders[j])
> + continue;
> + list_lru_destroy(&pt->pages);
> + }
> + }
> }
> EXPORT_SYMBOL(ttm_pool_fini);
>
> @@ -1386,6 +1397,7 @@ static inline u64 ttm_get_node_memory_size(int nid)
> int ttm_pool_mgr_init(unsigned long num_pages)
> {
> unsigned int i;
> + int ret = 0;
>
> int nid;
> for_each_node(nid) {
> @@ -1423,8 +1435,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> #endif
>
> mm_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE, "drm-ttm_pool");
> - if (!mm_shrinker)
> - return -ENOMEM;
> + if (!mm_shrinker) {
> + ret = -ENOMEM;
> + goto err_shrinker;
> + }
>
> mm_shrinker->count_objects = ttm_pool_shrinker_count;
> mm_shrinker->scan_objects = ttm_pool_shrinker_scan;
> @@ -1434,6 +1448,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> shrinker_register(mm_shrinker);
>
> return 0;
> +
> +err_shrinker:
> + ttm_pool_type_fini_and_list_lru_destroy();
> + return ret;
> }
>
> /**
> @@ -1442,17 +1460,24 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> * Cleanup the global pools and unregister the MM shrinker.
> */
> void ttm_pool_mgr_fini(void)
> +{
> + shrinker_free(mm_shrinker);
> + ttm_pool_type_fini_and_list_lru_destroy();
> + WARN_ON(!list_empty(&shrinker_list));
> +}
> +
> +void ttm_pool_type_fini_and_list_lru_destroy(void)
> {
> unsigned int i;
>
> for (i = 0; i < NR_PAGE_ORDERS; ++i) {
> ttm_pool_type_fini(&global_write_combined[i]);
> + list_lru_destroy(&global_write_combined[i].pages);
> ttm_pool_type_fini(&global_uncached[i]);
> -
> + list_lru_destroy(&global_uncached[i].pages);
> ttm_pool_type_fini(&global_dma32_write_combined[i]);
> + list_lru_destroy(&global_dma32_write_combined[i].pages);
> ttm_pool_type_fini(&global_dma32_uncached[i]);
> + list_lru_destroy(&global_dma32_uncached[i].pages);
> }
> -
> - shrinker_free(mm_shrinker);
> - WARN_ON(!list_empty(&shrinker_list));
> }
> diff --git a/include/drm/ttm/ttm_pool.h b/include/drm/ttm/ttm_pool.h
> index 26ee592e1994..bda8e816a206 100644
> --- a/include/drm/ttm/ttm_pool.h
> +++ b/include/drm/ttm/ttm_pool.h
> @@ -97,4 +97,6 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
> int ttm_pool_mgr_init(unsigned long num_pages);
> void ttm_pool_mgr_fini(void);
>
> +void ttm_pool_type_fini_and_list_lru_destroy(void);
> +
> #endif
> --
> 2.54.0
>
>
This bug is specific so Qemu, so...
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] Memory leak error in qxl unbind
2026-06-28 15:40 [PATCH v2] Memory leak error in qxl unbind Óscar Megía López
2026-06-28 15:46 ` David Kahurani
@ 2026-06-29 7:21 ` sashiko-bot
2026-06-29 8:37 ` Christian König
2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-06-29 7:21 UTC (permalink / raw)
To: Óscar Megía López; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Unchecked `list_lru_init()` return value in `ttm_pool_type_init()` causes a deterministic NULL pointer dereference in the newly added error path.
- [High] Concurrent debugfs access during module unload or initialization failure causes a NULL pointer dereference due to a teardown race introduced by the patch.
Pre-existing issues:
- [High] `ttm_global_init()` leaks the registered shrinker and list_lrus if `alloc_page()` fails.
--
commit e23de1fd240385312484194a68f533845be6969a
Author: Óscar Megía López <megia.oscar@gmail.com>
Memory leak error in qxl unbind
This commit fixes a memory leak and refcount corruption during OOM conditions
by adding error handling for ttm_pool_mgr_init() in ttm_global_init() and
properly cleaning up pool types if shrinker_alloc() fails.
Link: https://lore.kernel.org/virtualization/20260627105445.89827-2-megia.oscar@gmail.com/T/#u
diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
index d3bfb9a696a74..c880a04303635 100644
--- a/drivers/gpu/drm/ttm/ttm_device.c
+++ b/drivers/gpu/drm/ttm/ttm_device.c
@@ -96,7 +96,10 @@ static int ttm_global_init(void)
>> PAGE_SHIFT;
num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
- ttm_pool_mgr_init(num_pages);
+ ret = ttm_pool_mgr_init(num_pages);
+ if (ret)
+ goto out;
+
ttm_tt_mgr_init(num_pages, num_dma32);
glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32 |
[Severity: High]
This is a pre-existing issue, but does the error path for alloc_page() leak
the registered shrinker?
If alloc_page() fails in ttm_global_init(), the code jumps to out: and returns
-ENOMEM without calling ttm_pool_mgr_fini(). This permanently leaks the
previously registered mm_shrinker.
Because the global mm_shrinker pointer is overwritten on a subsequent retry,
the leaked shrinker is never freed. When the ttm module is unloaded, the
leaked shrinker remains registered with the MM subsystem. Any subsequent memory
pressure will invoke the shrinker callback pointing to unloaded module text,
crashing the system.
[ ... ]
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 278bbe7a11add..e0c82804a07d5 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
[ ... ]
@@ -1434,6 +1448,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
shrinker_register(mm_shrinker);
return 0;
+
+err_shrinker:
+ ttm_pool_type_fini_and_list_lru_destroy();
[Severity: High]
Can ttm_pool_type_fini_and_list_lru_destroy() cause a NULL pointer dereference
here if list_lru_init() previously failed?
ttm_pool_type_init() ignores the return value of list_lru_init(). If it fails
due to memory pressure, pt->pages.node remains NULL.
This new err_shrinker path invokes ttm_pool_type_fini(), which unconditionally
calls list_lru_walk(). list_lru_walk() dereferences lru->node, which would
crash the system on the NULL pointer.
+ return ret;
}
/**
@@ -1442,17 +1460,24 @@ int ttm_pool_mgr_init(unsigned long num_pages)
* Cleanup the global pools and unregister the MM shrinker.
*/
void ttm_pool_mgr_fini(void)
+{
+ shrinker_free(mm_shrinker);
+ ttm_pool_type_fini_and_list_lru_destroy();
[Severity: High]
Does this introduce a race condition with debugfs access?
By destroying the list_lru structures here, their nodes are set to NULL.
However, ttm_pool_mgr_fini() executes before debugfs_remove() in
ttm_global_release() (and before the out: label cleanup in ttm_global_init()).
If a user concurrently reads the /sys/kernel/debug/ttm/page_pool file,
ttm_pool_debugfs_globals_show() invokes list_lru_count(). Since
list_lru_destroy() has already set lru->node to NULL, the count function will
dereference the NULL pointer.
+ WARN_ON(!list_empty(&shrinker_list));
+}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260628154051.232214-1-megia.oscar@gmail.com?part=1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] Memory leak error in qxl unbind
2026-06-28 15:40 [PATCH v2] Memory leak error in qxl unbind Óscar Megía López
2026-06-28 15:46 ` David Kahurani
2026-06-29 7:21 ` sashiko-bot
@ 2026-06-29 8:37 ` Christian König
2 siblings, 0 replies; 4+ messages in thread
From: Christian König @ 2026-06-29 8:37 UTC (permalink / raw)
To: Óscar Megía López
Cc: Huang Rui, Matthew Auld, Matthew Brost, dri-devel,
linux-kernel-mentees
On 6/28/26 17:40, Óscar Megía López wrote:
> I discovered OOM after run script below around two hours in my computer:
>
> while :; do
> echo [pci qxl id] > /sys/bus/pci/drivers/qxl/unbind
> echo [pci qxl id] > /sys/bus/pci/drivers/qxl/bind
> done
>
> If you run 10000 times above script kmemleak does not report any
> memory leak, but if you run above script about two hours several
> OOM ocurs and at the end kernel panic.
>
> The OOM isn't just a simple leak; it's a refcount corruption that renders
> the list_lru fix dead code after the first mid-init failure.
>
> Here's the chain:
>
> Bug 1: ttm_global_init ignores ttm_pool_mgr_init() return
> If shrinker_alloc() fails under memory pressure, ttm_pool_mgr_init
> returns -ENOMEM with pool types already initialized (64 list_lru_init
> calls done). ttm_global_init ignored this and returned 0, leaving orphaned
> pool types with a NULL mm_shrinker.
>
> Fix: Check ret from ttm_pool_mgr_init; if non-zero, goto out cleans up
> refcount + debugfs.
>
> Bug 2: ttm_pool_mgr_init leaks pool types on shrinker_alloc failure
> If shrinker_alloc fails after all 64 pool types were list_lru_init'd,
> the function returned -ENOMEM without undoing them. With Bug 1 now
> triggering proper error handling, this undo is necessary.
>
> Fix: err_shrinker: label that finalizes + destroys all 64 pool types
> before returning.
>
> You must apply the patch from the link "[PATCH v2] drm/qxl: fix
> use-after-free to qxl_irq_handler in PCI mode" before testing.
> If you don't apply this patch, you will get a UAF error when running
> the script above.
>
> Assisted-by: OpenCode:1.17.8-Big Pickle
> Link: https://lore.kernel.org/virtualization/20260627105445.89827-2-megia.oscar@gmail.com/T/#u
> Signed-off-by: Óscar Megía López <megia.oscar@gmail.com>
> ---
> drivers/gpu/drm/ttm/ttm_device.c | 5 ++++-
> drivers/gpu/drm/ttm/ttm_pool.c | 37 ++++++++++++++++++++++++++------
> include/drm/ttm/ttm_pool.h | 2 ++
> 3 files changed, 37 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
> index d3bfb9a696a7..c880a0430363 100644
> --- a/drivers/gpu/drm/ttm/ttm_device.c
> +++ b/drivers/gpu/drm/ttm/ttm_device.c
> @@ -96,7 +96,10 @@ static int ttm_global_init(void)
> >> PAGE_SHIFT;
> num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT));
>
> - ttm_pool_mgr_init(num_pages);
> + ret = ttm_pool_mgr_init(num_pages);
> + if (ret)
> + goto out;
> +
Please put that into a separate patch.
> ttm_tt_mgr_init(num_pages, num_dma32);
>
> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32 |
> diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
> index 278bbe7a11ad..e0c82804a07d 100644
> --- a/drivers/gpu/drm/ttm/ttm_pool.c
> +++ b/drivers/gpu/drm/ttm/ttm_pool.c
> @@ -1198,6 +1198,17 @@ void ttm_pool_fini(struct ttm_pool *pool)
> * that no shrinker is concurrently freeing pages from the pool.
> */
> ttm_pool_synchronize_shrinkers();
> +
> + for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
> + for (j = 0; j < NR_PAGE_ORDERS; ++j) {
> + struct ttm_pool_type *pt;
> +
> + pt = ttm_pool_select_type(pool, i, j);
> + if (pt != &pool->caching[i].orders[j])
> + continue;
> + list_lru_destroy(&pt->pages);
> + }
> + }
> }
> EXPORT_SYMBOL(ttm_pool_fini);
>
> @@ -1386,6 +1397,7 @@ static inline u64 ttm_get_node_memory_size(int nid)
> int ttm_pool_mgr_init(unsigned long num_pages)
> {
> unsigned int i;
> + int ret = 0;
>
> int nid;
> for_each_node(nid) {
> @@ -1423,8 +1435,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> #endif
>
> mm_shrinker = shrinker_alloc(SHRINKER_NUMA_AWARE, "drm-ttm_pool");
> - if (!mm_shrinker)
> - return -ENOMEM;
> + if (!mm_shrinker) {
> + ret = -ENOMEM;
> + goto err_shrinker;
> + }
>
> mm_shrinker->count_objects = ttm_pool_shrinker_count;
> mm_shrinker->scan_objects = ttm_pool_shrinker_scan;
> @@ -1434,6 +1448,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> shrinker_register(mm_shrinker);
>
> return 0;
> +
> +err_shrinker:
> + ttm_pool_type_fini_and_list_lru_destroy();
> + return ret;
Instead of the error handling move shrinker_alloc() before initializing the pool types.
> }
>
> /**
> @@ -1442,17 +1460,24 @@ int ttm_pool_mgr_init(unsigned long num_pages)
> * Cleanup the global pools and unregister the MM shrinker.
> */
> void ttm_pool_mgr_fini(void)
> +{
> + shrinker_free(mm_shrinker);
> + ttm_pool_type_fini_and_list_lru_destroy();
> + WARN_ON(!list_empty(&shrinker_list));
> +}
> +
> +void ttm_pool_type_fini_and_list_lru_destroy(void)
> {
> unsigned int i;
>
> for (i = 0; i < NR_PAGE_ORDERS; ++i) {
> ttm_pool_type_fini(&global_write_combined[i]);
> + list_lru_destroy(&global_write_combined[i].pages);
> ttm_pool_type_fini(&global_uncached[i]);
> -
> + list_lru_destroy(&global_uncached[i].pages);
> ttm_pool_type_fini(&global_dma32_write_combined[i]);
> + list_lru_destroy(&global_dma32_write_combined[i].pages);
> ttm_pool_type_fini(&global_dma32_uncached[i]);
> + list_lru_destroy(&global_dma32_uncached[i].pages);
> }
> -
> - shrinker_free(mm_shrinker);
> - WARN_ON(!list_empty(&shrinker_list));
> }
> diff --git a/include/drm/ttm/ttm_pool.h b/include/drm/ttm/ttm_pool.h
> index 26ee592e1994..bda8e816a206 100644
> --- a/include/drm/ttm/ttm_pool.h
> +++ b/include/drm/ttm/ttm_pool.h
> @@ -97,4 +97,6 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
> int ttm_pool_mgr_init(unsigned long num_pages);
> void ttm_pool_mgr_fini(void);
>
> +void ttm_pool_type_fini_and_list_lru_destroy(void);
That function doesn't need to be exported and so doesn't belong into the header.
Regards,
Christian.
> +
> #endif
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-29 8:38 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-28 15:40 [PATCH v2] Memory leak error in qxl unbind Óscar Megía López
2026-06-28 15:46 ` David Kahurani
2026-06-29 7:21 ` sashiko-bot
2026-06-29 8:37 ` Christian König
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.