Commit 6387a3c4 authored by Arunpravin's avatar Arunpravin Committed by Christian König
Browse files

drm: move the buddy allocator from i915 into common drm



Move the base i915 buddy allocator code into drm
- Move i915_buddy.h to include/drm
- Move i915_buddy.c to drm root folder
- Rename "i915" string with "drm" string wherever applicable
- Rename "I915" string with "DRM" string wherever applicable
- Fix header file dependencies
- Fix alignment issues
- add Makefile support for drm buddy
- export functions and write kerneldoc description
- Remove i915 selftest config check condition as buddy selftest
  will be moved to drm selftest folder

cleanup i915 buddy references in i915 driver module
and replace with drm buddy

v2:
  - include header file in alphabetical order(Thomas)
  - merged changes listed in the body section into a single patch
    to keep the build intact(Christian, Jani)

v3:
  - make drm buddy a separate module(Thomas, Christian)

v4:
  - Fix build error reported by kernel test robot <lkp@intel.com>
  - removed i915 buddy selftest from i915_mock_selftests.h to
    avoid build error
  - removed selftests/i915_buddy.c file as we create a new set of
    buddy test cases in drm/selftests folder

v5:
  - Fix merge conflict issue

v6:
  - replace drm_buddy_mm structure name as drm_buddy(Thomas, Christian)
  - replace drm_buddy_alloc() function name as drm_buddy_alloc_blocks()
    (Thomas)
  - replace drm_buddy_free() function name as drm_buddy_free_block()
    (Thomas)
  - export drm_buddy_free_block() function
  - fix multiple instances of KMEM_CACHE() entry

v7:
  - fix warnings reported by kernel test robot <lkp@intel.com>
  - modify the license(Christian)

v8:
  - fix warnings reported by kernel test robot <lkp@intel.com>

Signed-off-by: default avatarArunpravin <Arunpravin.PaneerSelvam@amd.com>
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220118104504.2349-1-Arunpravin.PaneerSelvam@amd.com


Signed-off-by: default avatarChristian König <christian.koenig@amd.com>
parent 6b79f96f
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -204,6 +204,12 @@ config DRM_TTM
	  GPU memory types. Will be enabled automatically if a device driver
	  uses it.

config DRM_BUDDY
	tristate
	depends on DRM
	help
	  A page based buddy allocator

config DRM_VRAM_HELPER
	tristate
	depends on DRM
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ obj-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_cma_helper.o
drm_shmem_helper-y := drm_gem_shmem_helper.o
obj-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_shmem_helper.o

obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o

drm_vram_helper-y := drm_gem_vram_helper.o
obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o

+535 −0
Original line number Diff line number Diff line
@@ -4,23 +4,21 @@
 */

#include <linux/kmemleak.h>
#include <linux/module.h>
#include <linux/sizes.h>

#include "i915_buddy.h"

#include "i915_gem.h"
#include "i915_utils.h"
#include <drm/drm_buddy.h>

static struct kmem_cache *slab_blocks;

static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_mm *mm,
						 struct i915_buddy_block *parent,
static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
					       struct drm_buddy_block *parent,
					       unsigned int order,
					       u64 offset)
{
	struct i915_buddy_block *block;
	struct drm_buddy_block *block;

	GEM_BUG_ON(order > I915_BUDDY_MAX_ORDER);
	BUG_ON(order > DRM_BUDDY_MAX_ORDER);

	block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
	if (!block)
@@ -30,43 +28,55 @@ static struct i915_buddy_block *i915_block_alloc(struct i915_buddy_mm *mm,
	block->header |= order;
	block->parent = parent;

	GEM_BUG_ON(block->header & I915_BUDDY_HEADER_UNUSED);
	BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
	return block;
}

static void i915_block_free(struct i915_buddy_mm *mm,
			    struct i915_buddy_block *block)
static void drm_block_free(struct drm_buddy *mm,
			   struct drm_buddy_block *block)
{
	kmem_cache_free(slab_blocks, block);
}

static void mark_allocated(struct i915_buddy_block *block)
static void mark_allocated(struct drm_buddy_block *block)
{
	block->header &= ~I915_BUDDY_HEADER_STATE;
	block->header |= I915_BUDDY_ALLOCATED;
	block->header &= ~DRM_BUDDY_HEADER_STATE;
	block->header |= DRM_BUDDY_ALLOCATED;

	list_del(&block->link);
}

static void mark_free(struct i915_buddy_mm *mm,
		      struct i915_buddy_block *block)
static void mark_free(struct drm_buddy *mm,
		      struct drm_buddy_block *block)
{
	block->header &= ~I915_BUDDY_HEADER_STATE;
	block->header |= I915_BUDDY_FREE;
	block->header &= ~DRM_BUDDY_HEADER_STATE;
	block->header |= DRM_BUDDY_FREE;

	list_add(&block->link,
		 &mm->free_list[i915_buddy_block_order(block)]);
		 &mm->free_list[drm_buddy_block_order(block)]);
}

static void mark_split(struct i915_buddy_block *block)
static void mark_split(struct drm_buddy_block *block)
{
	block->header &= ~I915_BUDDY_HEADER_STATE;
	block->header |= I915_BUDDY_SPLIT;
	block->header &= ~DRM_BUDDY_HEADER_STATE;
	block->header |= DRM_BUDDY_SPLIT;

	list_del(&block->link);
}

int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
/**
 * drm_buddy_init - init memory manager
 *
 * @mm: DRM buddy manager to initialize
 * @size: size in bytes to manage
 * @chunk_size: minimum page size in bytes for our allocations
 *
 * Initializes the memory manager and its resources.
 *
 * Returns:
 * 0 on success, error code on failure.
 */
int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
{
	unsigned int i;
	u64 offset;
@@ -87,7 +97,7 @@ int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
	mm->chunk_size = chunk_size;
	mm->max_order = ilog2(size) - ilog2(chunk_size);

	GEM_BUG_ON(mm->max_order > I915_BUDDY_MAX_ORDER);
	BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);

	mm->free_list = kmalloc_array(mm->max_order + 1,
				      sizeof(struct list_head),
@@ -101,7 +111,7 @@ int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
	mm->n_roots = hweight64(size);

	mm->roots = kmalloc_array(mm->n_roots,
				  sizeof(struct i915_buddy_block *),
				  sizeof(struct drm_buddy_block *),
				  GFP_KERNEL);
	if (!mm->roots)
		goto out_free_list;
@@ -114,21 +124,21 @@ int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)
	 * not itself a power-of-two.
	 */
	do {
		struct i915_buddy_block *root;
		struct drm_buddy_block *root;
		unsigned int order;
		u64 root_size;

		root_size = rounddown_pow_of_two(size);
		order = ilog2(root_size) - ilog2(chunk_size);

		root = i915_block_alloc(mm, NULL, order, offset);
		root = drm_block_alloc(mm, NULL, order, offset);
		if (!root)
			goto out_free_roots;

		mark_free(mm, root);

		GEM_BUG_ON(i > mm->max_order);
		GEM_BUG_ON(i915_buddy_block_size(mm, root) < chunk_size);
		BUG_ON(i > mm->max_order);
		BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);

		mm->roots[i] = root;

@@ -141,45 +151,54 @@ int i915_buddy_init(struct i915_buddy_mm *mm, u64 size, u64 chunk_size)

out_free_roots:
	while (i--)
		i915_block_free(mm, mm->roots[i]);
		drm_block_free(mm, mm->roots[i]);
	kfree(mm->roots);
out_free_list:
	kfree(mm->free_list);
	return -ENOMEM;
}
EXPORT_SYMBOL(drm_buddy_init);

void i915_buddy_fini(struct i915_buddy_mm *mm)
/**
 * drm_buddy_fini - tear down the memory manager
 *
 * @mm: DRM buddy manager to free
 *
 * Cleanup memory manager resources and the freelist
 */
void drm_buddy_fini(struct drm_buddy *mm)
{
	int i;

	for (i = 0; i < mm->n_roots; ++i) {
		GEM_WARN_ON(!i915_buddy_block_is_free(mm->roots[i]));
		i915_block_free(mm, mm->roots[i]);
		WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
		drm_block_free(mm, mm->roots[i]);
	}

	GEM_WARN_ON(mm->avail != mm->size);
	WARN_ON(mm->avail != mm->size);

	kfree(mm->roots);
	kfree(mm->free_list);
}
EXPORT_SYMBOL(drm_buddy_fini);

static int split_block(struct i915_buddy_mm *mm,
		       struct i915_buddy_block *block)
static int split_block(struct drm_buddy *mm,
		       struct drm_buddy_block *block)
{
	unsigned int block_order = i915_buddy_block_order(block) - 1;
	u64 offset = i915_buddy_block_offset(block);
	unsigned int block_order = drm_buddy_block_order(block) - 1;
	u64 offset = drm_buddy_block_offset(block);

	GEM_BUG_ON(!i915_buddy_block_is_free(block));
	GEM_BUG_ON(!i915_buddy_block_order(block));
	BUG_ON(!drm_buddy_block_is_free(block));
	BUG_ON(!drm_buddy_block_order(block));

	block->left = i915_block_alloc(mm, block, block_order, offset);
	block->left = drm_block_alloc(mm, block, block_order, offset);
	if (!block->left)
		return -ENOMEM;

	block->right = i915_block_alloc(mm, block, block_order,
	block->right = drm_block_alloc(mm, block, block_order,
				       offset + (mm->chunk_size << block_order));
	if (!block->right) {
		i915_block_free(mm, block->left);
		drm_block_free(mm, block->left);
		return -ENOMEM;
	}

@@ -191,10 +210,10 @@ static int split_block(struct i915_buddy_mm *mm,
	return 0;
}

static struct i915_buddy_block *
get_buddy(struct i915_buddy_block *block)
static struct drm_buddy_block *
get_buddy(struct drm_buddy_block *block)
{
	struct i915_buddy_block *parent;
	struct drm_buddy_block *parent;

	parent = block->parent;
	if (!parent)
@@ -206,23 +225,23 @@ get_buddy(struct i915_buddy_block *block)
	return parent->left;
}

static void __i915_buddy_free(struct i915_buddy_mm *mm,
			      struct i915_buddy_block *block)
static void __drm_buddy_free(struct drm_buddy *mm,
			     struct drm_buddy_block *block)
{
	struct i915_buddy_block *parent;
	struct drm_buddy_block *parent;

	while ((parent = block->parent)) {
		struct i915_buddy_block *buddy;
		struct drm_buddy_block *buddy;

		buddy = get_buddy(block);

		if (!i915_buddy_block_is_free(buddy))
		if (!drm_buddy_block_is_free(buddy))
			break;

		list_del(&buddy->link);

		i915_block_free(mm, block);
		i915_block_free(mm, buddy);
		drm_block_free(mm, block);
		drm_block_free(mm, buddy);

		block = parent;
	}
@@ -230,43 +249,64 @@ static void __i915_buddy_free(struct i915_buddy_mm *mm,
	mark_free(mm, block);
}

void i915_buddy_free(struct i915_buddy_mm *mm,
		     struct i915_buddy_block *block)
/**
 * drm_buddy_free_block - free a block
 *
 * @mm: DRM buddy manager
 * @block: block to be freed
 */
void drm_buddy_free_block(struct drm_buddy *mm,
			  struct drm_buddy_block *block)
{
	GEM_BUG_ON(!i915_buddy_block_is_allocated(block));
	mm->avail += i915_buddy_block_size(mm, block);
	__i915_buddy_free(mm, block);
	BUG_ON(!drm_buddy_block_is_allocated(block));
	mm->avail += drm_buddy_block_size(mm, block);
	__drm_buddy_free(mm, block);
}
EXPORT_SYMBOL(drm_buddy_free_block);

void i915_buddy_free_list(struct i915_buddy_mm *mm, struct list_head *objects)
/**
 * drm_buddy_free_list - free blocks
 *
 * @mm: DRM buddy manager
 * @objects: input list head to free blocks
 */
void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects)
{
	struct i915_buddy_block *block, *on;
	struct drm_buddy_block *block, *on;

	list_for_each_entry_safe(block, on, objects, link) {
		i915_buddy_free(mm, block);
		drm_buddy_free_block(mm, block);
		cond_resched();
	}
	INIT_LIST_HEAD(objects);
}
EXPORT_SYMBOL(drm_buddy_free_list);

/*
 * Allocate power-of-two block. The order value here translates to:
/**
 * 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 &drm_buddy_block on success
 */
struct i915_buddy_block *
i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
struct drm_buddy_block *
drm_buddy_alloc_blocks(struct drm_buddy *mm, unsigned int order)
{
	struct i915_buddy_block *block = NULL;
	struct drm_buddy_block *block = NULL;
	unsigned int i;
	int err;

	for (i = order; i <= mm->max_order; ++i) {
		block = list_first_entry_or_null(&mm->free_list[i],
						 struct i915_buddy_block,
						 struct drm_buddy_block,
						 link);
		if (block)
			break;
@@ -275,7 +315,7 @@ i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
	if (!block)
		return ERR_PTR(-ENOSPC);

	GEM_BUG_ON(!i915_buddy_block_is_free(block));
	BUG_ON(!drm_buddy_block_is_free(block));

	while (i != order) {
		err = split_block(mm, block);
@@ -288,15 +328,16 @@ i915_buddy_alloc(struct i915_buddy_mm *mm, unsigned int order)
	}

	mark_allocated(block);
	mm->avail -= i915_buddy_block_size(mm, block);
	mm->avail -= drm_buddy_block_size(mm, block);
	kmemleak_update_trace(block);
	return block;

out_free:
	if (i != order)
		__i915_buddy_free(mm, block);
		__drm_buddy_free(mm, block);
	return ERR_PTR(err);
}
EXPORT_SYMBOL(drm_buddy_alloc_blocks);

static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
{
@@ -308,22 +349,32 @@ static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
	return s1 <= s2 && e1 >= e2;
}

/*
 * Allocate range. Note that it's safe to chain together multiple alloc_ranges
 * with the same blocks list.
/**
 * drm_buddy_alloc_range - allocate range
 *
 * @mm: DRM buddy manager to allocate from
 * @blocks: output list head to add allocated blocks
 * @start: start of the allowed range for this block
 * @size: size of the allocation
 *
 * Intended for pre-allocating portions of the address space, for example to
 * reserve a block for the initial framebuffer or similar, hence the expectation
 * here is that i915_buddy_alloc() is still the main vehicle for
 * here is that drm_buddy_alloc_blocks() is still the main vehicle for
 * allocations, so if that's not the case then the drm_mm range allocator is
 * probably a much better fit, and so you should probably go use that instead.
 *
 * Note that it's safe to chain together multiple alloc_ranges
 * with the same blocks list
 *
 * Returns:
 * 0 on success, error code on failure.
 */
int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
int drm_buddy_alloc_range(struct drm_buddy *mm,
			  struct list_head *blocks,
			  u64 start, u64 size)
{
	struct i915_buddy_block *block;
	struct i915_buddy_block *buddy;
	struct drm_buddy_block *block;
	struct drm_buddy_block *buddy;
	LIST_HEAD(allocated);
	LIST_HEAD(dfs);
	u64 end;
@@ -349,37 +400,37 @@ int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
		u64 block_end;

		block = list_first_entry_or_null(&dfs,
						 struct i915_buddy_block,
						 struct drm_buddy_block,
						 tmp_link);
		if (!block)
			break;

		list_del(&block->tmp_link);

		block_start = i915_buddy_block_offset(block);
		block_end = block_start + i915_buddy_block_size(mm, block) - 1;
		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 (i915_buddy_block_is_allocated(block)) {
		if (drm_buddy_block_is_allocated(block)) {
			err = -ENOSPC;
			goto err_free;
		}

		if (contains(start, end, block_start, block_end)) {
			if (!i915_buddy_block_is_free(block)) {
			if (!drm_buddy_block_is_free(block)) {
				err = -ENOSPC;
				goto err_free;
			}

			mark_allocated(block);
			mm->avail -= i915_buddy_block_size(mm, block);
			mm->avail -= drm_buddy_block_size(mm, block);
			list_add_tail(&block->link, &allocated);
			continue;
		}

		if (!i915_buddy_block_is_split(block)) {
		if (!drm_buddy_block_is_split(block)) {
			err = split_block(mm, block);
			if (unlikely(err))
				goto err_undo;
@@ -400,26 +451,41 @@ int i915_buddy_alloc_range(struct i915_buddy_mm *mm,
	 */
	buddy = get_buddy(block);
	if (buddy &&
	    (i915_buddy_block_is_free(block) &&
	     i915_buddy_block_is_free(buddy)))
		__i915_buddy_free(mm, block);
	    (drm_buddy_block_is_free(block) &&
	     drm_buddy_block_is_free(buddy)))
		__drm_buddy_free(mm, block);

err_free:
	i915_buddy_free_list(mm, &allocated);
	drm_buddy_free_list(mm, &allocated);
	return err;
}
EXPORT_SYMBOL(drm_buddy_alloc_range);

void i915_buddy_block_print(struct i915_buddy_mm *mm,
			    struct i915_buddy_block *block,
/**
 * drm_buddy_block_print - print block information
 *
 * @mm: DRM buddy manager
 * @block: DRM buddy block
 * @p: DRM printer to use
 */
void drm_buddy_block_print(struct drm_buddy *mm,
			   struct drm_buddy_block *block,
			   struct drm_printer *p)
{
	u64 start = i915_buddy_block_offset(block);
	u64 size = i915_buddy_block_size(mm, block);
	u64 start = drm_buddy_block_offset(block);
	u64 size = drm_buddy_block_size(mm, block);

	drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
}
EXPORT_SYMBOL(drm_buddy_block_print);

void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p)
/**
 * drm_buddy_print - print allocator state
 *
 * @mm: DRM buddy manager
 * @p: DRM printer to use
 */
void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
{
	int order;

@@ -427,11 +493,11 @@ void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p)
		   mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);

	for (order = mm->max_order; order >= 0; order--) {
		struct i915_buddy_block *block;
		struct drm_buddy_block *block;
		u64 count = 0, free;

		list_for_each_entry(block, &mm->free_list[order], link) {
			GEM_BUG_ON(!i915_buddy_block_is_free(block));
			BUG_ON(!drm_buddy_block_is_free(block));
			count++;
		}

@@ -446,21 +512,24 @@ void i915_buddy_print(struct i915_buddy_mm *mm, struct drm_printer *p)
		drm_printf(p, ", pages: %llu\n", count);
	}
}
EXPORT_SYMBOL(drm_buddy_print);

#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/i915_buddy.c"
#endif

void i915_buddy_module_exit(void)
static void drm_buddy_module_exit(void)
{
	kmem_cache_destroy(slab_blocks);
}

int __init i915_buddy_module_init(void)
static int __init drm_buddy_module_init(void)
{
	slab_blocks = KMEM_CACHE(i915_buddy_block, 0);
	slab_blocks = KMEM_CACHE(drm_buddy_block, 0);
	if (!slab_blocks)
		return -ENOMEM;

	return 0;
}

module_init(drm_buddy_module_init);
module_exit(drm_buddy_module_exit);

MODULE_DESCRIPTION("DRM Buddy Allocator");
MODULE_LICENSE("Dual MIT/GPL");
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ config DRM_I915
	select CEC_CORE if CEC_NOTIFIER
	select VMAP_PFN
	select DRM_TTM
	select DRM_BUDDY
	help
	  Choose this option if you have a system that has "Intel Graphics
	  Media Accelerator" or "HD Graphics" integrated graphics,
+0 −1
Original line number Diff line number Diff line
@@ -161,7 +161,6 @@ gem-y += \
i915-y += \
	  $(gem-y) \
	  i915_active.o \
	  i915_buddy.o \
	  i915_cmd_parser.o \
	  i915_gem_evict.o \
	  i915_gem_gtt.o \
Loading