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