162306a36Sopenharmony_ci/* SPDX-License-Identifier: MIT */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2021 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#ifndef __DRM_BUDDY_H__
762306a36Sopenharmony_ci#define __DRM_BUDDY_H__
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/list.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <drm/drm_print.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define range_overflows(start, size, max) ({ \
1762306a36Sopenharmony_ci	typeof(start) start__ = (start); \
1862306a36Sopenharmony_ci	typeof(size) size__ = (size); \
1962306a36Sopenharmony_ci	typeof(max) max__ = (max); \
2062306a36Sopenharmony_ci	(void)(&start__ == &size__); \
2162306a36Sopenharmony_ci	(void)(&start__ == &max__); \
2262306a36Sopenharmony_ci	start__ >= max__ || size__ > max__ - start__; \
2362306a36Sopenharmony_ci})
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DRM_BUDDY_RANGE_ALLOCATION (1 << 0)
2662306a36Sopenharmony_ci#define DRM_BUDDY_TOPDOWN_ALLOCATION (1 << 1)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct drm_buddy_block {
2962306a36Sopenharmony_ci#define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
3062306a36Sopenharmony_ci#define DRM_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
3162306a36Sopenharmony_ci#define   DRM_BUDDY_ALLOCATED	   (1 << 10)
3262306a36Sopenharmony_ci#define   DRM_BUDDY_FREE	   (2 << 10)
3362306a36Sopenharmony_ci#define   DRM_BUDDY_SPLIT	   (3 << 10)
3462306a36Sopenharmony_ci/* Free to be used, if needed in the future */
3562306a36Sopenharmony_ci#define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(9, 6)
3662306a36Sopenharmony_ci#define DRM_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
3762306a36Sopenharmony_ci	u64 header;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	struct drm_buddy_block *left;
4062306a36Sopenharmony_ci	struct drm_buddy_block *right;
4162306a36Sopenharmony_ci	struct drm_buddy_block *parent;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	void *private; /* owned by creator */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * While the block is allocated by the user through drm_buddy_alloc*,
4762306a36Sopenharmony_ci	 * the user has ownership of the link, for example to maintain within
4862306a36Sopenharmony_ci	 * a list, if so desired. As soon as the block is freed with
4962306a36Sopenharmony_ci	 * drm_buddy_free* ownership is given back to the mm.
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	struct list_head link;
5262306a36Sopenharmony_ci	struct list_head tmp_link;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Order-zero must be at least PAGE_SIZE */
5662306a36Sopenharmony_ci#define DRM_BUDDY_MAX_ORDER (63 - PAGE_SHIFT)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Binary Buddy System.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * Locking should be handled by the user, a simple mutex around
6262306a36Sopenharmony_ci * drm_buddy_alloc* and drm_buddy_free* should suffice.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistruct drm_buddy {
6562306a36Sopenharmony_ci	/* Maintain a free list for each order. */
6662306a36Sopenharmony_ci	struct list_head *free_list;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/*
6962306a36Sopenharmony_ci	 * Maintain explicit binary tree(s) to track the allocation of the
7062306a36Sopenharmony_ci	 * address space. This gives us a simple way of finding a buddy block
7162306a36Sopenharmony_ci	 * and performing the potentially recursive merge step when freeing a
7262306a36Sopenharmony_ci	 * block.  Nodes are either allocated or free, in which case they will
7362306a36Sopenharmony_ci	 * also exist on the respective free list.
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	struct drm_buddy_block **roots;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * Anything from here is public, and remains static for the lifetime of
7962306a36Sopenharmony_ci	 * the mm. Everything above is considered do-not-touch.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	unsigned int n_roots;
8262306a36Sopenharmony_ci	unsigned int max_order;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Must be at least PAGE_SIZE */
8562306a36Sopenharmony_ci	u64 chunk_size;
8662306a36Sopenharmony_ci	u64 size;
8762306a36Sopenharmony_ci	u64 avail;
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic inline u64
9162306a36Sopenharmony_cidrm_buddy_block_offset(struct drm_buddy_block *block)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	return block->header & DRM_BUDDY_HEADER_OFFSET;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline unsigned int
9762306a36Sopenharmony_cidrm_buddy_block_order(struct drm_buddy_block *block)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return block->header & DRM_BUDDY_HEADER_ORDER;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic inline unsigned int
10362306a36Sopenharmony_cidrm_buddy_block_state(struct drm_buddy_block *block)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	return block->header & DRM_BUDDY_HEADER_STATE;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic inline bool
10962306a36Sopenharmony_cidrm_buddy_block_is_allocated(struct drm_buddy_block *block)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline bool
11562306a36Sopenharmony_cidrm_buddy_block_is_free(struct drm_buddy_block *block)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return drm_buddy_block_state(block) == DRM_BUDDY_FREE;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline bool
12162306a36Sopenharmony_cidrm_buddy_block_is_split(struct drm_buddy_block *block)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic inline u64
12762306a36Sopenharmony_cidrm_buddy_block_size(struct drm_buddy *mm,
12862306a36Sopenharmony_ci		     struct drm_buddy_block *block)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	return mm->chunk_size << drm_buddy_block_order(block);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_civoid drm_buddy_fini(struct drm_buddy *mm);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct drm_buddy_block *
13862306a36Sopenharmony_cidrm_get_buddy(struct drm_buddy_block *block);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciint drm_buddy_alloc_blocks(struct drm_buddy *mm,
14162306a36Sopenharmony_ci			   u64 start, u64 end, u64 size,
14262306a36Sopenharmony_ci			   u64 min_page_size,
14362306a36Sopenharmony_ci			   struct list_head *blocks,
14462306a36Sopenharmony_ci			   unsigned long flags);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint drm_buddy_block_trim(struct drm_buddy *mm,
14762306a36Sopenharmony_ci			 u64 new_size,
14862306a36Sopenharmony_ci			 struct list_head *blocks);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_civoid drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_civoid drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_civoid drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
15562306a36Sopenharmony_civoid drm_buddy_block_print(struct drm_buddy *mm,
15662306a36Sopenharmony_ci			   struct drm_buddy_block *block,
15762306a36Sopenharmony_ci			   struct drm_printer *p);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#endif
160