Re: [PATCH v11 1/5] drm: improve drm_buddy_alloc function
On 10/02/22 9:40 pm, Matthew Auld wrote: > On 27/01/2022 14:11, Arunpravin wrote: >> - Make drm_buddy_alloc a single function to handle >>range allocation and non-range allocation demands >> >> - Implemented a new function alloc_range() which allocates >>the requested power-of-two block comply with range limitations >> >> - Moved order computation and memory alignment logic from >>i915 driver to drm buddy >> >> v2: >>merged below changes to keep the build unbroken >> - drm_buddy_alloc_range() becomes obsolete and may be removed >> - enable ttm range allocation (fpfn / lpfn) support in i915 driver >> - apply enhanced drm_buddy_alloc() function to i915 driver >> >> v3(Matthew Auld): >>- Fix alignment issues and remove unnecessary list_empty check >>- add more validation checks for input arguments >>- make alloc_range() block allocations as bottom-up >>- optimize order computation logic >>- replace uint64_t with u64, which is preferred in the kernel >> >> v4(Matthew Auld): >>- keep drm_buddy_alloc_range() function implementation for generic >> actual range allocations >>- keep alloc_range() implementation for end bias allocations >> >> v5(Matthew Auld): >>- modify drm_buddy_alloc() passing argument place->lpfn to lpfn >> as place->lpfn will currently always be zero for i915 >> >> v6(Matthew Auld): >>- fixup potential uaf - If we are unlucky and can't allocate >> enough memory when splitting blocks, where we temporarily >> end up with the given block and its buddy on the respective >> free list, then we need to ensure we delete both blocks, >> and no just the buddy, before potentially freeing them >> >>- fix warnings reported by kernel test robot >> >> v7(Matthew Auld): >>- revert fixup potential uaf >>- keep __alloc_range() add node to the list logic same as >> drm_buddy_alloc_blocks() by having a temporary list variable >>- at drm_buddy_alloc_blocks() keep i915 range_overflows macro >> and add a new check for end variable >> >> v8: >>- fix warnings reported by kernel test robot >> >> Signed-off-by: Arunpravin >> --- >> drivers/gpu/drm/drm_buddy.c | 315 +- >> drivers/gpu/drm/i915/i915_ttm_buddy_manager.c | 67 ++-- >> drivers/gpu/drm/i915/i915_ttm_buddy_manager.h | 2 + >> include/drm/drm_buddy.h | 13 +- >> 4 files changed, 280 insertions(+), 117 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c >> index d60878bc9c20..cfc160a1ef1a 100644 >> --- a/drivers/gpu/drm/drm_buddy.c >> +++ b/drivers/gpu/drm/drm_buddy.c >> @@ -282,23 +282,97 @@ void drm_buddy_free_list(struct drm_buddy *mm, struct >> list_head *objects) >> } >> EXPORT_SYMBOL(drm_buddy_free_list); >> >> -/** >> - * drm_buddy_alloc_blocks - allocate power-of-two blocks >> - * >> - * @mm: DRM buddy manager to allocate from >> - * @order: size of the allocation >> - * >> - * The order value here translates to: >> - * >> - * 0 = 2^0 * mm->chunk_size >> - * 1 = 2^1 * mm->chunk_size >> - * 2 = 2^2 * mm->chunk_size >> - * >> - * Returns: >> - * allocated ptr to the _buddy_block on success >> - */ >> -struct drm_buddy_block * >> -drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order) >> +static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) >> +{ >> +return s1 <= e2 && e1 >= s2; >> +} >> + >> +static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) >> +{ >> +return s1 <= s2 && e1 >= e2; >> +} >> + >> +static struct drm_buddy_block * >> +alloc_range_bias(struct drm_buddy *mm, >> + u64 start, u64 end, >> + unsigned int order) >> +{ >> +struct drm_buddy_block *block; >> +struct drm_buddy_block *buddy; >> +LIST_HEAD(dfs); >> +int err; >> +int i; >> + >> +end = end - 1; >> + >> +for (i = 0; i < mm->n_roots; ++i) >> +list_add_tail(>roots[i]->tmp_link, ); >> + >> +do { >> +u64 block_start; >> +u64 block_end; >> + >> +block = list_first_entry_or_null(, >> + struct drm_buddy_block, >> + tmp_link); >> +if (!block) >> +break; >> + >> +list_del(>tmp_link); >> + >> +if (drm_buddy_block_order(block) < order) >> +continue; >> + >> +block_start = drm_buddy_block_offset(block); >> +block_end = block_start + drm_buddy_block_size(mm, block) - 1; >> + >> +if (!overlaps(start, end, block_start, block_end)) >> +continue; >> + >> +if (drm_buddy_block_is_allocated(block)) >> +continue; >> + >> +if (contains(start, end, block_start, block_end) && >> +order == drm_buddy_block_order(block)) { >> +/* >> +
Re: [PATCH v11 1/5] drm: improve drm_buddy_alloc function
On 27/01/2022 14:11, Arunpravin wrote: - Make drm_buddy_alloc a single function to handle range allocation and non-range allocation demands - Implemented a new function alloc_range() which allocates the requested power-of-two block comply with range limitations - Moved order computation and memory alignment logic from i915 driver to drm buddy v2: merged below changes to keep the build unbroken - drm_buddy_alloc_range() becomes obsolete and may be removed - enable ttm range allocation (fpfn / lpfn) support in i915 driver - apply enhanced drm_buddy_alloc() function to i915 driver v3(Matthew Auld): - Fix alignment issues and remove unnecessary list_empty check - add more validation checks for input arguments - make alloc_range() block allocations as bottom-up - optimize order computation logic - replace uint64_t with u64, which is preferred in the kernel v4(Matthew Auld): - keep drm_buddy_alloc_range() function implementation for generic actual range allocations - keep alloc_range() implementation for end bias allocations v5(Matthew Auld): - modify drm_buddy_alloc() passing argument place->lpfn to lpfn as place->lpfn will currently always be zero for i915 v6(Matthew Auld): - fixup potential uaf - If we are unlucky and can't allocate enough memory when splitting blocks, where we temporarily end up with the given block and its buddy on the respective free list, then we need to ensure we delete both blocks, and no just the buddy, before potentially freeing them - fix warnings reported by kernel test robot v7(Matthew Auld): - revert fixup potential uaf - keep __alloc_range() add node to the list logic same as drm_buddy_alloc_blocks() by having a temporary list variable - at drm_buddy_alloc_blocks() keep i915 range_overflows macro and add a new check for end variable v8: - fix warnings reported by kernel test robot Signed-off-by: Arunpravin --- drivers/gpu/drm/drm_buddy.c | 315 +- drivers/gpu/drm/i915/i915_ttm_buddy_manager.c | 67 ++-- drivers/gpu/drm/i915/i915_ttm_buddy_manager.h | 2 + include/drm/drm_buddy.h | 13 +- 4 files changed, 280 insertions(+), 117 deletions(-) diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index d60878bc9c20..cfc160a1ef1a 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -282,23 +282,97 @@ void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects) } EXPORT_SYMBOL(drm_buddy_free_list); -/** - * drm_buddy_alloc_blocks - allocate power-of-two blocks - * - * @mm: DRM buddy manager to allocate from - * @order: size of the allocation - * - * The order value here translates to: - * - * 0 = 2^0 * mm->chunk_size - * 1 = 2^1 * mm->chunk_size - * 2 = 2^2 * mm->chunk_size - * - * Returns: - * allocated ptr to the _buddy_block on success - */ -struct drm_buddy_block * -drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order) +static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= e2 && e1 >= s2; +} + +static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= s2 && e1 >= e2; +} + +static struct drm_buddy_block * +alloc_range_bias(struct drm_buddy *mm, +u64 start, u64 end, +unsigned int order) +{ + struct drm_buddy_block *block; + struct drm_buddy_block *buddy; + LIST_HEAD(dfs); + int err; + int i; + + end = end - 1; + + for (i = 0; i < mm->n_roots; ++i) + list_add_tail(>roots[i]->tmp_link, ); + + do { + u64 block_start; + u64 block_end; + + block = list_first_entry_or_null(, +struct drm_buddy_block, +tmp_link); + if (!block) + break; + + list_del(>tmp_link); + + if (drm_buddy_block_order(block) < order) + continue; + + block_start = drm_buddy_block_offset(block); + block_end = block_start + drm_buddy_block_size(mm, block) - 1; + + if (!overlaps(start, end, block_start, block_end)) + continue; + + if (drm_buddy_block_is_allocated(block)) + continue; + + if (contains(start, end, block_start, block_end) && + order == drm_buddy_block_order(block)) { + /* +* Find the free block within the range. +*/ + if (drm_buddy_block_is_free(block)) + return block; + + continue; + } + + if (!drm_buddy_block_is_split(block)) { + err = split_block(mm, block); +
[PATCH v11 1/5] drm: improve drm_buddy_alloc function
- Make drm_buddy_alloc a single function to handle range allocation and non-range allocation demands - Implemented a new function alloc_range() which allocates the requested power-of-two block comply with range limitations - Moved order computation and memory alignment logic from i915 driver to drm buddy v2: merged below changes to keep the build unbroken - drm_buddy_alloc_range() becomes obsolete and may be removed - enable ttm range allocation (fpfn / lpfn) support in i915 driver - apply enhanced drm_buddy_alloc() function to i915 driver v3(Matthew Auld): - Fix alignment issues and remove unnecessary list_empty check - add more validation checks for input arguments - make alloc_range() block allocations as bottom-up - optimize order computation logic - replace uint64_t with u64, which is preferred in the kernel v4(Matthew Auld): - keep drm_buddy_alloc_range() function implementation for generic actual range allocations - keep alloc_range() implementation for end bias allocations v5(Matthew Auld): - modify drm_buddy_alloc() passing argument place->lpfn to lpfn as place->lpfn will currently always be zero for i915 v6(Matthew Auld): - fixup potential uaf - If we are unlucky and can't allocate enough memory when splitting blocks, where we temporarily end up with the given block and its buddy on the respective free list, then we need to ensure we delete both blocks, and no just the buddy, before potentially freeing them - fix warnings reported by kernel test robot v7(Matthew Auld): - revert fixup potential uaf - keep __alloc_range() add node to the list logic same as drm_buddy_alloc_blocks() by having a temporary list variable - at drm_buddy_alloc_blocks() keep i915 range_overflows macro and add a new check for end variable v8: - fix warnings reported by kernel test robot Signed-off-by: Arunpravin --- drivers/gpu/drm/drm_buddy.c | 315 +- drivers/gpu/drm/i915/i915_ttm_buddy_manager.c | 67 ++-- drivers/gpu/drm/i915/i915_ttm_buddy_manager.h | 2 + include/drm/drm_buddy.h | 13 +- 4 files changed, 280 insertions(+), 117 deletions(-) diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index d60878bc9c20..cfc160a1ef1a 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -282,23 +282,97 @@ void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects) } EXPORT_SYMBOL(drm_buddy_free_list); -/** - * drm_buddy_alloc_blocks - allocate power-of-two blocks - * - * @mm: DRM buddy manager to allocate from - * @order: size of the allocation - * - * The order value here translates to: - * - * 0 = 2^0 * mm->chunk_size - * 1 = 2^1 * mm->chunk_size - * 2 = 2^2 * mm->chunk_size - * - * Returns: - * allocated ptr to the _buddy_block on success - */ -struct drm_buddy_block * -drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order) +static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= e2 && e1 >= s2; +} + +static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) +{ + return s1 <= s2 && e1 >= e2; +} + +static struct drm_buddy_block * +alloc_range_bias(struct drm_buddy *mm, +u64 start, u64 end, +unsigned int order) +{ + struct drm_buddy_block *block; + struct drm_buddy_block *buddy; + LIST_HEAD(dfs); + int err; + int i; + + end = end - 1; + + for (i = 0; i < mm->n_roots; ++i) + list_add_tail(>roots[i]->tmp_link, ); + + do { + u64 block_start; + u64 block_end; + + block = list_first_entry_or_null(, +struct drm_buddy_block, +tmp_link); + if (!block) + break; + + list_del(>tmp_link); + + if (drm_buddy_block_order(block) < order) + continue; + + block_start = drm_buddy_block_offset(block); + block_end = block_start + drm_buddy_block_size(mm, block) - 1; + + if (!overlaps(start, end, block_start, block_end)) + continue; + + if (drm_buddy_block_is_allocated(block)) + continue; + + if (contains(start, end, block_start, block_end) && + order == drm_buddy_block_order(block)) { + /* +* Find the free block within the range. +*/ + if (drm_buddy_block_is_free(block)) + return block; + + continue; + } + + if (!drm_buddy_block_is_split(block)) { + err = split_block(mm, block); + if (unlikely(err)) + goto