mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-12 00:38:55 +00:00
476e406302
Implemented a function which walk through the order list, compares the offset and returns the maximum offset block, this method is unpredictable in obtaining the high range address blocks which depends on allocation and deallocation. for instance, if driver requests address at a low specific range, allocator traverses from the root block and splits the larger blocks until it reaches the specific block and in the process of splitting, lower orders in the freelist are occupied with low range address blocks and for the subsequent TOPDOWN memory request we may return the low range blocks.To overcome this issue, we may go with the below approach. The other approach, sorting each order list entries in ascending order and compares the last entry of each order list in the freelist and return the max block. This creates sorting overhead on every drm_buddy_free() request and split up of larger blocks for a single page request. v2: - Fix alignment issues(Matthew Auld) - Remove unnecessary list_empty check(Matthew Auld) - merged the below patch to see the feature in action - add top-down alloc support to i915 driver Signed-off-by: Arunpravin <Arunpravin.PaneerSelvam@amd.com> Reviewed-by: Matthew Auld <matthew.auld@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20220221164552.2434-2-Arunpravin.PaneerSelvam@amd.com Signed-off-by: Christian König <christian.koenig@amd.com>
153 lines
4.0 KiB
C
153 lines
4.0 KiB
C
/* SPDX-License-Identifier: MIT */
|
|
/*
|
|
* Copyright © 2021 Intel Corporation
|
|
*/
|
|
|
|
#ifndef __DRM_BUDDY_H__
|
|
#define __DRM_BUDDY_H__
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/list.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include <drm/drm_print.h>
|
|
|
|
#define range_overflows(start, size, max) ({ \
|
|
typeof(start) start__ = (start); \
|
|
typeof(size) size__ = (size); \
|
|
typeof(max) max__ = (max); \
|
|
(void)(&start__ == &size__); \
|
|
(void)(&start__ == &max__); \
|
|
start__ >= max__ || size__ > max__ - start__; \
|
|
})
|
|
|
|
#define DRM_BUDDY_RANGE_ALLOCATION (1 << 0)
|
|
#define DRM_BUDDY_TOPDOWN_ALLOCATION (1 << 1)
|
|
|
|
struct drm_buddy_block {
|
|
#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
|
|
#define DRM_BUDDY_HEADER_STATE GENMASK_ULL(11, 10)
|
|
#define DRM_BUDDY_ALLOCATED (1 << 10)
|
|
#define DRM_BUDDY_FREE (2 << 10)
|
|
#define DRM_BUDDY_SPLIT (3 << 10)
|
|
/* Free to be used, if needed in the future */
|
|
#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
|
|
#define DRM_BUDDY_HEADER_ORDER GENMASK_ULL(5, 0)
|
|
u64 header;
|
|
|
|
struct drm_buddy_block *left;
|
|
struct drm_buddy_block *right;
|
|
struct drm_buddy_block *parent;
|
|
|
|
void *private; /* owned by creator */
|
|
|
|
/*
|
|
* While the block is allocated by the user through drm_buddy_alloc*,
|
|
* the user has ownership of the link, for example to maintain within
|
|
* a list, if so desired. As soon as the block is freed with
|
|
* drm_buddy_free* ownership is given back to the mm.
|
|
*/
|
|
struct list_head link;
|
|
struct list_head tmp_link;
|
|
};
|
|
|
|
/* Order-zero must be at least PAGE_SIZE */
|
|
#define DRM_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
|
|
|
|
/*
|
|
* Binary Buddy System.
|
|
*
|
|
* Locking should be handled by the user, a simple mutex around
|
|
* drm_buddy_alloc* and drm_buddy_free* should suffice.
|
|
*/
|
|
struct drm_buddy {
|
|
/* Maintain a free list for each order. */
|
|
struct list_head *free_list;
|
|
|
|
/*
|
|
* Maintain explicit binary tree(s) to track the allocation of the
|
|
* address space. This gives us a simple way of finding a buddy block
|
|
* and performing the potentially recursive merge step when freeing a
|
|
* block. Nodes are either allocated or free, in which case they will
|
|
* also exist on the respective free list.
|
|
*/
|
|
struct drm_buddy_block **roots;
|
|
|
|
/*
|
|
* Anything from here is public, and remains static for the lifetime of
|
|
* the mm. Everything above is considered do-not-touch.
|
|
*/
|
|
unsigned int n_roots;
|
|
unsigned int max_order;
|
|
|
|
/* Must be at least PAGE_SIZE */
|
|
u64 chunk_size;
|
|
u64 size;
|
|
u64 avail;
|
|
};
|
|
|
|
static inline u64
|
|
drm_buddy_block_offset(struct drm_buddy_block *block)
|
|
{
|
|
return block->header & DRM_BUDDY_HEADER_OFFSET;
|
|
}
|
|
|
|
static inline unsigned int
|
|
drm_buddy_block_order(struct drm_buddy_block *block)
|
|
{
|
|
return block->header & DRM_BUDDY_HEADER_ORDER;
|
|
}
|
|
|
|
static inline unsigned int
|
|
drm_buddy_block_state(struct drm_buddy_block *block)
|
|
{
|
|
return block->header & DRM_BUDDY_HEADER_STATE;
|
|
}
|
|
|
|
static inline bool
|
|
drm_buddy_block_is_allocated(struct drm_buddy_block *block)
|
|
{
|
|
return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
|
|
}
|
|
|
|
static inline bool
|
|
drm_buddy_block_is_free(struct drm_buddy_block *block)
|
|
{
|
|
return drm_buddy_block_state(block) == DRM_BUDDY_FREE;
|
|
}
|
|
|
|
static inline bool
|
|
drm_buddy_block_is_split(struct drm_buddy_block *block)
|
|
{
|
|
return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT;
|
|
}
|
|
|
|
static inline u64
|
|
drm_buddy_block_size(struct drm_buddy *mm,
|
|
struct drm_buddy_block *block)
|
|
{
|
|
return mm->chunk_size << drm_buddy_block_order(block);
|
|
}
|
|
|
|
int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size);
|
|
|
|
void drm_buddy_fini(struct drm_buddy *mm);
|
|
|
|
int drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
|
u64 start, u64 end, u64 size,
|
|
u64 min_page_size,
|
|
struct list_head *blocks,
|
|
unsigned long flags);
|
|
|
|
void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block);
|
|
|
|
void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects);
|
|
|
|
void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
|
|
void drm_buddy_block_print(struct drm_buddy *mm,
|
|
struct drm_buddy_block *block,
|
|
struct drm_printer *p);
|
|
|
|
#endif
|