If the caller passes a non-empty blocks list, __alloc_contig_try_harder()
unconditionally frees it on failure or during retry.

Pass the local allocated list instead and only splice it onto the caller's
list upon success.

Assisted-by: LLM
Reported-by: Shashiko <[email protected]>
Closes: https://sashiko.dev/#/bug/linux-fa721daf-7333-48b5-a26b-624c7f5791d7
Fixes: 0a1844bf0b53 ("drm/buddy: Improve contiguous memory allocation")
Signed-off-by: Matthew Auld <[email protected]>
Cc: Arunpravin Paneer Selvam <[email protected]>
Cc: Joel Fernandes <[email protected]>
Cc: <[email protected]>
---
 drivers/gpu/buddy.c                | 20 ++++++++++++++------
 drivers/gpu/tests/gpu_buddy_test.c |  9 +++++++++
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index 2f2aaadafe35..a86f454fbb40 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -2022,10 +2022,16 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 
        if (order > mm->max_order || size > mm->size) {
                if ((flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) &&
-                   !(flags & GPU_BUDDY_RANGE_ALLOCATION))
-                       return __alloc_contig_try_harder(mm, original_size,
-                                                        original_min_size,
-                                                        flags, blocks);
+                   !(flags & GPU_BUDDY_RANGE_ALLOCATION)) {
+                       err = __alloc_contig_try_harder(mm, original_size,
+                                                       original_min_size,
+                                                       flags, &allocated);
+                       if (err)
+                               return err;
+
+                       list_splice_tail(&allocated, blocks);
+                       return 0;
+               }
 
                return -EINVAL;
        }
@@ -2064,9 +2070,11 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
                                                                original_size,
                                                                
original_min_size,
                                                                flags,
-                                                               blocks);
-                               if (!err)
+                                                               &allocated);
+                               if (!err) {
+                                       list_splice_tail(&allocated, blocks);
                                        return 0;
+                               }
                                if (err != -ENOSPC)
                                        return err;
                                goto err_free;
diff --git a/drivers/gpu/tests/gpu_buddy_test.c 
b/drivers/gpu/tests/gpu_buddy_test.c
index b75d32ca6ca0..2c6f1da1a6d1 100644
--- a/drivers/gpu/tests/gpu_buddy_test.c
+++ b/drivers/gpu/tests/gpu_buddy_test.c
@@ -1235,6 +1235,15 @@ static void gpu_test_buddy_alloc_contiguous(struct kunit 
*test)
                                                            
GPU_BUDDY_CONTIGUOUS_ALLOCATION),
                               "buddy_alloc hit an error size=%lu\n", 2 * ps);
 
+       /*
+        * A failed contiguous allocation with a non-empty blocks list
+        * must NOT corrupt or free pre-existing blocks.
+        */
+       KUNIT_EXPECT_TRUE(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
+                                                      3 * ps, ps, &allocated,
+                                                      
GPU_BUDDY_CONTIGUOUS_ALLOCATION));
+       KUNIT_EXPECT_FALSE(test, list_empty(&allocated));
+
        gpu_buddy_free_list(&mm, &left, 0);
        KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size,
                                                            3 * ps, ps, 
&allocated,
-- 
2.55.0

Reply via email to