xref: /linux/include/drm/drm_buddy.h (revision 15833fea97c1fdb3b34fceefa4b51177dd57e18f)
16387a3c4SArunpravin /* SPDX-License-Identifier: MIT */
26387a3c4SArunpravin /*
36387a3c4SArunpravin  * Copyright © 2021 Intel Corporation
46387a3c4SArunpravin  */
56387a3c4SArunpravin 
66387a3c4SArunpravin #ifndef __DRM_BUDDY_H__
76387a3c4SArunpravin #define __DRM_BUDDY_H__
86387a3c4SArunpravin 
96387a3c4SArunpravin #include <linux/bitops.h>
106387a3c4SArunpravin #include <linux/list.h>
116387a3c4SArunpravin #include <linux/slab.h>
126387a3c4SArunpravin #include <linux/sched.h>
136387a3c4SArunpravin 
146387a3c4SArunpravin #include <drm/drm_print.h>
156387a3c4SArunpravin 
166387a3c4SArunpravin #define range_overflows(start, size, max) ({ \
176387a3c4SArunpravin 	typeof(start) start__ = (start); \
186387a3c4SArunpravin 	typeof(size) size__ = (size); \
196387a3c4SArunpravin 	typeof(max) max__ = (max); \
206387a3c4SArunpravin 	(void)(&start__ == &size__); \
216387a3c4SArunpravin 	(void)(&start__ == &max__); \
226387a3c4SArunpravin 	start__ >= max__ || size__ > max__ - start__; \
236387a3c4SArunpravin })
246387a3c4SArunpravin 
250a1844bfSArunpravin Paneer Selvam #define DRM_BUDDY_RANGE_ALLOCATION		BIT(0)
260a1844bfSArunpravin Paneer Selvam #define DRM_BUDDY_TOPDOWN_ALLOCATION		BIT(1)
270a1844bfSArunpravin Paneer Selvam #define DRM_BUDDY_CONTIGUOUS_ALLOCATION		BIT(2)
2896950929SArunpravin Paneer Selvam #define DRM_BUDDY_CLEAR_ALLOCATION		BIT(3)
2996950929SArunpravin Paneer Selvam #define DRM_BUDDY_CLEARED			BIT(4)
30*d507ae0dSArunpravin Paneer Selvam #define DRM_BUDDY_TRIM_DISABLE			BIT(5)
31afea229fSArunpravin 
326387a3c4SArunpravin struct drm_buddy_block {
336387a3c4SArunpravin #define DRM_BUDDY_HEADER_OFFSET GENMASK_ULL(63, 12)
346387a3c4SArunpravin #define DRM_BUDDY_HEADER_STATE  GENMASK_ULL(11, 10)
356387a3c4SArunpravin #define   DRM_BUDDY_ALLOCATED	   (1 << 10)
366387a3c4SArunpravin #define   DRM_BUDDY_FREE	   (2 << 10)
376387a3c4SArunpravin #define   DRM_BUDDY_SPLIT	   (3 << 10)
3896950929SArunpravin Paneer Selvam #define DRM_BUDDY_HEADER_CLEAR  GENMASK_ULL(9, 9)
396387a3c4SArunpravin /* Free to be used, if needed in the future */
4096950929SArunpravin Paneer Selvam #define DRM_BUDDY_HEADER_UNUSED GENMASK_ULL(8, 6)
416387a3c4SArunpravin #define DRM_BUDDY_HEADER_ORDER  GENMASK_ULL(5, 0)
426387a3c4SArunpravin 	u64 header;
436387a3c4SArunpravin 
446387a3c4SArunpravin 	struct drm_buddy_block *left;
456387a3c4SArunpravin 	struct drm_buddy_block *right;
466387a3c4SArunpravin 	struct drm_buddy_block *parent;
476387a3c4SArunpravin 
486387a3c4SArunpravin 	void *private; /* owned by creator */
496387a3c4SArunpravin 
506387a3c4SArunpravin 	/*
516387a3c4SArunpravin 	 * While the block is allocated by the user through drm_buddy_alloc*,
526387a3c4SArunpravin 	 * the user has ownership of the link, for example to maintain within
536387a3c4SArunpravin 	 * a list, if so desired. As soon as the block is freed with
546387a3c4SArunpravin 	 * drm_buddy_free* ownership is given back to the mm.
556387a3c4SArunpravin 	 */
566387a3c4SArunpravin 	struct list_head link;
576387a3c4SArunpravin 	struct list_head tmp_link;
586387a3c4SArunpravin };
596387a3c4SArunpravin 
60117bbc0eSMatthew Auld /* Order-zero must be at least SZ_4K */
61117bbc0eSMatthew Auld #define DRM_BUDDY_MAX_ORDER (63 - 12)
626387a3c4SArunpravin 
636387a3c4SArunpravin /*
646387a3c4SArunpravin  * Binary Buddy System.
656387a3c4SArunpravin  *
666387a3c4SArunpravin  * Locking should be handled by the user, a simple mutex around
676387a3c4SArunpravin  * drm_buddy_alloc* and drm_buddy_free* should suffice.
686387a3c4SArunpravin  */
696387a3c4SArunpravin struct drm_buddy {
706387a3c4SArunpravin 	/* Maintain a free list for each order. */
716387a3c4SArunpravin 	struct list_head *free_list;
726387a3c4SArunpravin 
736387a3c4SArunpravin 	/*
746387a3c4SArunpravin 	 * Maintain explicit binary tree(s) to track the allocation of the
756387a3c4SArunpravin 	 * address space. This gives us a simple way of finding a buddy block
766387a3c4SArunpravin 	 * and performing the potentially recursive merge step when freeing a
776387a3c4SArunpravin 	 * block.  Nodes are either allocated or free, in which case they will
786387a3c4SArunpravin 	 * also exist on the respective free list.
796387a3c4SArunpravin 	 */
806387a3c4SArunpravin 	struct drm_buddy_block **roots;
816387a3c4SArunpravin 
826387a3c4SArunpravin 	/*
836387a3c4SArunpravin 	 * Anything from here is public, and remains static for the lifetime of
846387a3c4SArunpravin 	 * the mm. Everything above is considered do-not-touch.
856387a3c4SArunpravin 	 */
866387a3c4SArunpravin 	unsigned int n_roots;
876387a3c4SArunpravin 	unsigned int max_order;
886387a3c4SArunpravin 
89117bbc0eSMatthew Auld 	/* Must be at least SZ_4K */
906387a3c4SArunpravin 	u64 chunk_size;
916387a3c4SArunpravin 	u64 size;
926387a3c4SArunpravin 	u64 avail;
9396950929SArunpravin Paneer Selvam 	u64 clear_avail;
946387a3c4SArunpravin };
956387a3c4SArunpravin 
966387a3c4SArunpravin static inline u64
drm_buddy_block_offset(struct drm_buddy_block * block)976387a3c4SArunpravin drm_buddy_block_offset(struct drm_buddy_block *block)
986387a3c4SArunpravin {
996387a3c4SArunpravin 	return block->header & DRM_BUDDY_HEADER_OFFSET;
1006387a3c4SArunpravin }
1016387a3c4SArunpravin 
1026387a3c4SArunpravin static inline unsigned int
drm_buddy_block_order(struct drm_buddy_block * block)1036387a3c4SArunpravin drm_buddy_block_order(struct drm_buddy_block *block)
1046387a3c4SArunpravin {
1056387a3c4SArunpravin 	return block->header & DRM_BUDDY_HEADER_ORDER;
1066387a3c4SArunpravin }
1076387a3c4SArunpravin 
1086387a3c4SArunpravin static inline unsigned int
drm_buddy_block_state(struct drm_buddy_block * block)1096387a3c4SArunpravin drm_buddy_block_state(struct drm_buddy_block *block)
1106387a3c4SArunpravin {
1116387a3c4SArunpravin 	return block->header & DRM_BUDDY_HEADER_STATE;
1126387a3c4SArunpravin }
1136387a3c4SArunpravin 
1146387a3c4SArunpravin static inline bool
drm_buddy_block_is_allocated(struct drm_buddy_block * block)1156387a3c4SArunpravin drm_buddy_block_is_allocated(struct drm_buddy_block *block)
1166387a3c4SArunpravin {
1176387a3c4SArunpravin 	return drm_buddy_block_state(block) == DRM_BUDDY_ALLOCATED;
1186387a3c4SArunpravin }
1196387a3c4SArunpravin 
1206387a3c4SArunpravin static inline bool
drm_buddy_block_is_clear(struct drm_buddy_block * block)12196950929SArunpravin Paneer Selvam drm_buddy_block_is_clear(struct drm_buddy_block *block)
12296950929SArunpravin Paneer Selvam {
12396950929SArunpravin Paneer Selvam 	return block->header & DRM_BUDDY_HEADER_CLEAR;
12496950929SArunpravin Paneer Selvam }
12596950929SArunpravin Paneer Selvam 
12696950929SArunpravin Paneer Selvam static inline bool
drm_buddy_block_is_free(struct drm_buddy_block * block)1276387a3c4SArunpravin drm_buddy_block_is_free(struct drm_buddy_block *block)
1286387a3c4SArunpravin {
1296387a3c4SArunpravin 	return drm_buddy_block_state(block) == DRM_BUDDY_FREE;
1306387a3c4SArunpravin }
1316387a3c4SArunpravin 
1326387a3c4SArunpravin static inline bool
drm_buddy_block_is_split(struct drm_buddy_block * block)1336387a3c4SArunpravin drm_buddy_block_is_split(struct drm_buddy_block *block)
1346387a3c4SArunpravin {
1356387a3c4SArunpravin 	return drm_buddy_block_state(block) == DRM_BUDDY_SPLIT;
1366387a3c4SArunpravin }
1376387a3c4SArunpravin 
1386387a3c4SArunpravin static inline u64
drm_buddy_block_size(struct drm_buddy * mm,struct drm_buddy_block * block)1396387a3c4SArunpravin drm_buddy_block_size(struct drm_buddy *mm,
1406387a3c4SArunpravin 		     struct drm_buddy_block *block)
1416387a3c4SArunpravin {
1426387a3c4SArunpravin 	return mm->chunk_size << drm_buddy_block_order(block);
1436387a3c4SArunpravin }
1446387a3c4SArunpravin 
1456387a3c4SArunpravin int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size);
1466387a3c4SArunpravin 
1476387a3c4SArunpravin void drm_buddy_fini(struct drm_buddy *mm);
1486387a3c4SArunpravin 
14992937f17SArunpravin struct drm_buddy_block *
15092937f17SArunpravin drm_get_buddy(struct drm_buddy_block *block);
15192937f17SArunpravin 
152afea229fSArunpravin int drm_buddy_alloc_blocks(struct drm_buddy *mm,
153afea229fSArunpravin 			   u64 start, u64 end, u64 size,
154afea229fSArunpravin 			   u64 min_page_size,
1556387a3c4SArunpravin 			   struct list_head *blocks,
156afea229fSArunpravin 			   unsigned long flags);
1576387a3c4SArunpravin 
15895ee2a8bSArunpravin int drm_buddy_block_trim(struct drm_buddy *mm,
159*d507ae0dSArunpravin Paneer Selvam 			 u64 *start,
16095ee2a8bSArunpravin 			 u64 new_size,
16195ee2a8bSArunpravin 			 struct list_head *blocks);
16295ee2a8bSArunpravin 
1636387a3c4SArunpravin void drm_buddy_free_block(struct drm_buddy *mm, struct drm_buddy_block *block);
1646387a3c4SArunpravin 
16596950929SArunpravin Paneer Selvam void drm_buddy_free_list(struct drm_buddy *mm,
16696950929SArunpravin Paneer Selvam 			 struct list_head *objects,
16796950929SArunpravin Paneer Selvam 			 unsigned int flags);
1686387a3c4SArunpravin 
1696387a3c4SArunpravin void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p);
1706387a3c4SArunpravin void drm_buddy_block_print(struct drm_buddy *mm,
1716387a3c4SArunpravin 			   struct drm_buddy_block *block,
1726387a3c4SArunpravin 			   struct drm_printer *p);
1736387a3c4SArunpravin #endif
174