xref: /openbmc/linux/drivers/gpu/drm/drm_buddy.c (revision 816ffd28002651a469e86d1118a225862e392ecd)
16387a3c4SArunpravin // SPDX-License-Identifier: MIT
26387a3c4SArunpravin /*
36387a3c4SArunpravin  * Copyright © 2021 Intel Corporation
46387a3c4SArunpravin  */
56387a3c4SArunpravin 
66387a3c4SArunpravin #include <linux/kmemleak.h>
76387a3c4SArunpravin #include <linux/module.h>
86387a3c4SArunpravin #include <linux/sizes.h>
96387a3c4SArunpravin 
106387a3c4SArunpravin #include <drm/drm_buddy.h>
116387a3c4SArunpravin 
126387a3c4SArunpravin static struct kmem_cache *slab_blocks;
136387a3c4SArunpravin 
drm_block_alloc(struct drm_buddy * mm,struct drm_buddy_block * parent,unsigned int order,u64 offset)146387a3c4SArunpravin static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
156387a3c4SArunpravin 					       struct drm_buddy_block *parent,
166387a3c4SArunpravin 					       unsigned int order,
176387a3c4SArunpravin 					       u64 offset)
186387a3c4SArunpravin {
196387a3c4SArunpravin 	struct drm_buddy_block *block;
206387a3c4SArunpravin 
216387a3c4SArunpravin 	BUG_ON(order > DRM_BUDDY_MAX_ORDER);
226387a3c4SArunpravin 
236387a3c4SArunpravin 	block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL);
246387a3c4SArunpravin 	if (!block)
256387a3c4SArunpravin 		return NULL;
266387a3c4SArunpravin 
276387a3c4SArunpravin 	block->header = offset;
286387a3c4SArunpravin 	block->header |= order;
296387a3c4SArunpravin 	block->parent = parent;
306387a3c4SArunpravin 
316387a3c4SArunpravin 	BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
326387a3c4SArunpravin 	return block;
336387a3c4SArunpravin }
346387a3c4SArunpravin 
drm_block_free(struct drm_buddy * mm,struct drm_buddy_block * block)356387a3c4SArunpravin static void drm_block_free(struct drm_buddy *mm,
366387a3c4SArunpravin 			   struct drm_buddy_block *block)
376387a3c4SArunpravin {
386387a3c4SArunpravin 	kmem_cache_free(slab_blocks, block);
396387a3c4SArunpravin }
406387a3c4SArunpravin 
list_insert_sorted(struct drm_buddy * mm,struct drm_buddy_block * block)415640e816SArunpravin Paneer Selvam static void list_insert_sorted(struct drm_buddy *mm,
425640e816SArunpravin Paneer Selvam 			       struct drm_buddy_block *block)
435640e816SArunpravin Paneer Selvam {
445640e816SArunpravin Paneer Selvam 	struct drm_buddy_block *node;
455640e816SArunpravin Paneer Selvam 	struct list_head *head;
465640e816SArunpravin Paneer Selvam 
475640e816SArunpravin Paneer Selvam 	head = &mm->free_list[drm_buddy_block_order(block)];
485640e816SArunpravin Paneer Selvam 	if (list_empty(head)) {
495640e816SArunpravin Paneer Selvam 		list_add(&block->link, head);
505640e816SArunpravin Paneer Selvam 		return;
515640e816SArunpravin Paneer Selvam 	}
525640e816SArunpravin Paneer Selvam 
535640e816SArunpravin Paneer Selvam 	list_for_each_entry(node, head, link)
545640e816SArunpravin Paneer Selvam 		if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node))
555640e816SArunpravin Paneer Selvam 			break;
565640e816SArunpravin Paneer Selvam 
575640e816SArunpravin Paneer Selvam 	__list_add(&block->link, node->link.prev, &node->link);
585640e816SArunpravin Paneer Selvam }
595640e816SArunpravin Paneer Selvam 
mark_allocated(struct drm_buddy_block * block)606387a3c4SArunpravin static void mark_allocated(struct drm_buddy_block *block)
616387a3c4SArunpravin {
626387a3c4SArunpravin 	block->header &= ~DRM_BUDDY_HEADER_STATE;
636387a3c4SArunpravin 	block->header |= DRM_BUDDY_ALLOCATED;
646387a3c4SArunpravin 
656387a3c4SArunpravin 	list_del(&block->link);
666387a3c4SArunpravin }
676387a3c4SArunpravin 
mark_free(struct drm_buddy * mm,struct drm_buddy_block * block)686387a3c4SArunpravin static void mark_free(struct drm_buddy *mm,
696387a3c4SArunpravin 		      struct drm_buddy_block *block)
706387a3c4SArunpravin {
716387a3c4SArunpravin 	block->header &= ~DRM_BUDDY_HEADER_STATE;
726387a3c4SArunpravin 	block->header |= DRM_BUDDY_FREE;
736387a3c4SArunpravin 
745640e816SArunpravin Paneer Selvam 	list_insert_sorted(mm, block);
756387a3c4SArunpravin }
766387a3c4SArunpravin 
mark_split(struct drm_buddy_block * block)776387a3c4SArunpravin static void mark_split(struct drm_buddy_block *block)
786387a3c4SArunpravin {
796387a3c4SArunpravin 	block->header &= ~DRM_BUDDY_HEADER_STATE;
806387a3c4SArunpravin 	block->header |= DRM_BUDDY_SPLIT;
816387a3c4SArunpravin 
826387a3c4SArunpravin 	list_del(&block->link);
836387a3c4SArunpravin }
846387a3c4SArunpravin 
856387a3c4SArunpravin /**
866387a3c4SArunpravin  * drm_buddy_init - init memory manager
876387a3c4SArunpravin  *
886387a3c4SArunpravin  * @mm: DRM buddy manager to initialize
896387a3c4SArunpravin  * @size: size in bytes to manage
906387a3c4SArunpravin  * @chunk_size: minimum page size in bytes for our allocations
916387a3c4SArunpravin  *
926387a3c4SArunpravin  * Initializes the memory manager and its resources.
936387a3c4SArunpravin  *
946387a3c4SArunpravin  * Returns:
956387a3c4SArunpravin  * 0 on success, error code on failure.
966387a3c4SArunpravin  */
drm_buddy_init(struct drm_buddy * mm,u64 size,u64 chunk_size)976387a3c4SArunpravin int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
986387a3c4SArunpravin {
996387a3c4SArunpravin 	unsigned int i;
1006387a3c4SArunpravin 	u64 offset;
1016387a3c4SArunpravin 
1026387a3c4SArunpravin 	if (size < chunk_size)
1036387a3c4SArunpravin 		return -EINVAL;
1046387a3c4SArunpravin 
1056387a3c4SArunpravin 	if (chunk_size < PAGE_SIZE)
1066387a3c4SArunpravin 		return -EINVAL;
1076387a3c4SArunpravin 
1086387a3c4SArunpravin 	if (!is_power_of_2(chunk_size))
1096387a3c4SArunpravin 		return -EINVAL;
1106387a3c4SArunpravin 
1116387a3c4SArunpravin 	size = round_down(size, chunk_size);
1126387a3c4SArunpravin 
1136387a3c4SArunpravin 	mm->size = size;
1146387a3c4SArunpravin 	mm->avail = size;
1156387a3c4SArunpravin 	mm->chunk_size = chunk_size;
1166387a3c4SArunpravin 	mm->max_order = ilog2(size) - ilog2(chunk_size);
1176387a3c4SArunpravin 
1186387a3c4SArunpravin 	BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);
1196387a3c4SArunpravin 
1206387a3c4SArunpravin 	mm->free_list = kmalloc_array(mm->max_order + 1,
1216387a3c4SArunpravin 				      sizeof(struct list_head),
1226387a3c4SArunpravin 				      GFP_KERNEL);
1236387a3c4SArunpravin 	if (!mm->free_list)
1246387a3c4SArunpravin 		return -ENOMEM;
1256387a3c4SArunpravin 
1266387a3c4SArunpravin 	for (i = 0; i <= mm->max_order; ++i)
1276387a3c4SArunpravin 		INIT_LIST_HEAD(&mm->free_list[i]);
1286387a3c4SArunpravin 
1296387a3c4SArunpravin 	mm->n_roots = hweight64(size);
1306387a3c4SArunpravin 
1316387a3c4SArunpravin 	mm->roots = kmalloc_array(mm->n_roots,
1326387a3c4SArunpravin 				  sizeof(struct drm_buddy_block *),
1336387a3c4SArunpravin 				  GFP_KERNEL);
1346387a3c4SArunpravin 	if (!mm->roots)
1356387a3c4SArunpravin 		goto out_free_list;
1366387a3c4SArunpravin 
1376387a3c4SArunpravin 	offset = 0;
1386387a3c4SArunpravin 	i = 0;
1396387a3c4SArunpravin 
1406387a3c4SArunpravin 	/*
1416387a3c4SArunpravin 	 * Split into power-of-two blocks, in case we are given a size that is
1426387a3c4SArunpravin 	 * not itself a power-of-two.
1436387a3c4SArunpravin 	 */
1446387a3c4SArunpravin 	do {
1456387a3c4SArunpravin 		struct drm_buddy_block *root;
1466387a3c4SArunpravin 		unsigned int order;
1476387a3c4SArunpravin 		u64 root_size;
1486387a3c4SArunpravin 
1494453545bSDavid Gow 		order = ilog2(size) - ilog2(chunk_size);
1504453545bSDavid Gow 		root_size = chunk_size << order;
1516387a3c4SArunpravin 
1526387a3c4SArunpravin 		root = drm_block_alloc(mm, NULL, order, offset);
1536387a3c4SArunpravin 		if (!root)
1546387a3c4SArunpravin 			goto out_free_roots;
1556387a3c4SArunpravin 
1566387a3c4SArunpravin 		mark_free(mm, root);
1576387a3c4SArunpravin 
1586387a3c4SArunpravin 		BUG_ON(i > mm->max_order);
1596387a3c4SArunpravin 		BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);
1606387a3c4SArunpravin 
1616387a3c4SArunpravin 		mm->roots[i] = root;
1626387a3c4SArunpravin 
1636387a3c4SArunpravin 		offset += root_size;
1646387a3c4SArunpravin 		size -= root_size;
1656387a3c4SArunpravin 		i++;
1666387a3c4SArunpravin 	} while (size);
1676387a3c4SArunpravin 
1686387a3c4SArunpravin 	return 0;
1696387a3c4SArunpravin 
1706387a3c4SArunpravin out_free_roots:
1716387a3c4SArunpravin 	while (i--)
1726387a3c4SArunpravin 		drm_block_free(mm, mm->roots[i]);
1736387a3c4SArunpravin 	kfree(mm->roots);
1746387a3c4SArunpravin out_free_list:
1756387a3c4SArunpravin 	kfree(mm->free_list);
1766387a3c4SArunpravin 	return -ENOMEM;
1776387a3c4SArunpravin }
1786387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_init);
1796387a3c4SArunpravin 
1806387a3c4SArunpravin /**
1816387a3c4SArunpravin  * drm_buddy_fini - tear down the memory manager
1826387a3c4SArunpravin  *
1836387a3c4SArunpravin  * @mm: DRM buddy manager to free
1846387a3c4SArunpravin  *
1856387a3c4SArunpravin  * Cleanup memory manager resources and the freelist
1866387a3c4SArunpravin  */
drm_buddy_fini(struct drm_buddy * mm)1876387a3c4SArunpravin void drm_buddy_fini(struct drm_buddy *mm)
1886387a3c4SArunpravin {
1896387a3c4SArunpravin 	int i;
1906387a3c4SArunpravin 
1916387a3c4SArunpravin 	for (i = 0; i < mm->n_roots; ++i) {
1926387a3c4SArunpravin 		WARN_ON(!drm_buddy_block_is_free(mm->roots[i]));
1936387a3c4SArunpravin 		drm_block_free(mm, mm->roots[i]);
1946387a3c4SArunpravin 	}
1956387a3c4SArunpravin 
1966387a3c4SArunpravin 	WARN_ON(mm->avail != mm->size);
1976387a3c4SArunpravin 
1986387a3c4SArunpravin 	kfree(mm->roots);
1996387a3c4SArunpravin 	kfree(mm->free_list);
2006387a3c4SArunpravin }
2016387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_fini);
2026387a3c4SArunpravin 
split_block(struct drm_buddy * mm,struct drm_buddy_block * block)2036387a3c4SArunpravin static int split_block(struct drm_buddy *mm,
2046387a3c4SArunpravin 		       struct drm_buddy_block *block)
2056387a3c4SArunpravin {
2066387a3c4SArunpravin 	unsigned int block_order = drm_buddy_block_order(block) - 1;
2076387a3c4SArunpravin 	u64 offset = drm_buddy_block_offset(block);
2086387a3c4SArunpravin 
2096387a3c4SArunpravin 	BUG_ON(!drm_buddy_block_is_free(block));
2106387a3c4SArunpravin 	BUG_ON(!drm_buddy_block_order(block));
2116387a3c4SArunpravin 
2126387a3c4SArunpravin 	block->left = drm_block_alloc(mm, block, block_order, offset);
2136387a3c4SArunpravin 	if (!block->left)
2146387a3c4SArunpravin 		return -ENOMEM;
2156387a3c4SArunpravin 
2166387a3c4SArunpravin 	block->right = drm_block_alloc(mm, block, block_order,
2176387a3c4SArunpravin 				       offset + (mm->chunk_size << block_order));
2186387a3c4SArunpravin 	if (!block->right) {
2196387a3c4SArunpravin 		drm_block_free(mm, block->left);
2206387a3c4SArunpravin 		return -ENOMEM;
2216387a3c4SArunpravin 	}
2226387a3c4SArunpravin 
2236387a3c4SArunpravin 	mark_free(mm, block->left);
2246387a3c4SArunpravin 	mark_free(mm, block->right);
2256387a3c4SArunpravin 
2266387a3c4SArunpravin 	mark_split(block);
2276387a3c4SArunpravin 
2286387a3c4SArunpravin 	return 0;
2296387a3c4SArunpravin }
2306387a3c4SArunpravin 
2316387a3c4SArunpravin static struct drm_buddy_block *
__get_buddy(struct drm_buddy_block * block)23292937f17SArunpravin __get_buddy(struct drm_buddy_block *block)
2336387a3c4SArunpravin {
2346387a3c4SArunpravin 	struct drm_buddy_block *parent;
2356387a3c4SArunpravin 
2366387a3c4SArunpravin 	parent = block->parent;
2376387a3c4SArunpravin 	if (!parent)
2386387a3c4SArunpravin 		return NULL;
2396387a3c4SArunpravin 
2406387a3c4SArunpravin 	if (parent->left == block)
2416387a3c4SArunpravin 		return parent->right;
2426387a3c4SArunpravin 
2436387a3c4SArunpravin 	return parent->left;
2446387a3c4SArunpravin }
2456387a3c4SArunpravin 
24692937f17SArunpravin /**
24792937f17SArunpravin  * drm_get_buddy - get buddy address
24892937f17SArunpravin  *
24992937f17SArunpravin  * @block: DRM buddy block
25092937f17SArunpravin  *
25192937f17SArunpravin  * Returns the corresponding buddy block for @block, or NULL
25292937f17SArunpravin  * if this is a root block and can't be merged further.
25392937f17SArunpravin  * Requires some kind of locking to protect against
25492937f17SArunpravin  * any concurrent allocate and free operations.
25592937f17SArunpravin  */
25692937f17SArunpravin struct drm_buddy_block *
drm_get_buddy(struct drm_buddy_block * block)25792937f17SArunpravin drm_get_buddy(struct drm_buddy_block *block)
25892937f17SArunpravin {
25992937f17SArunpravin 	return __get_buddy(block);
26092937f17SArunpravin }
26192937f17SArunpravin EXPORT_SYMBOL(drm_get_buddy);
26292937f17SArunpravin 
__drm_buddy_free(struct drm_buddy * mm,struct drm_buddy_block * block)2636387a3c4SArunpravin static void __drm_buddy_free(struct drm_buddy *mm,
2646387a3c4SArunpravin 			     struct drm_buddy_block *block)
2656387a3c4SArunpravin {
2666387a3c4SArunpravin 	struct drm_buddy_block *parent;
2676387a3c4SArunpravin 
2686387a3c4SArunpravin 	while ((parent = block->parent)) {
2696387a3c4SArunpravin 		struct drm_buddy_block *buddy;
2706387a3c4SArunpravin 
27192937f17SArunpravin 		buddy = __get_buddy(block);
2726387a3c4SArunpravin 
2736387a3c4SArunpravin 		if (!drm_buddy_block_is_free(buddy))
2746387a3c4SArunpravin 			break;
2756387a3c4SArunpravin 
2766387a3c4SArunpravin 		list_del(&buddy->link);
2776387a3c4SArunpravin 
2786387a3c4SArunpravin 		drm_block_free(mm, block);
2796387a3c4SArunpravin 		drm_block_free(mm, buddy);
2806387a3c4SArunpravin 
2816387a3c4SArunpravin 		block = parent;
2826387a3c4SArunpravin 	}
2836387a3c4SArunpravin 
2846387a3c4SArunpravin 	mark_free(mm, block);
2856387a3c4SArunpravin }
2866387a3c4SArunpravin 
2876387a3c4SArunpravin /**
2886387a3c4SArunpravin  * drm_buddy_free_block - free a block
2896387a3c4SArunpravin  *
2906387a3c4SArunpravin  * @mm: DRM buddy manager
2916387a3c4SArunpravin  * @block: block to be freed
2926387a3c4SArunpravin  */
drm_buddy_free_block(struct drm_buddy * mm,struct drm_buddy_block * block)2936387a3c4SArunpravin void drm_buddy_free_block(struct drm_buddy *mm,
2946387a3c4SArunpravin 			  struct drm_buddy_block *block)
2956387a3c4SArunpravin {
2966387a3c4SArunpravin 	BUG_ON(!drm_buddy_block_is_allocated(block));
2976387a3c4SArunpravin 	mm->avail += drm_buddy_block_size(mm, block);
2986387a3c4SArunpravin 	__drm_buddy_free(mm, block);
2996387a3c4SArunpravin }
3006387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_free_block);
3016387a3c4SArunpravin 
3026387a3c4SArunpravin /**
3036387a3c4SArunpravin  * drm_buddy_free_list - free blocks
3046387a3c4SArunpravin  *
3056387a3c4SArunpravin  * @mm: DRM buddy manager
3066387a3c4SArunpravin  * @objects: input list head to free blocks
3076387a3c4SArunpravin  */
drm_buddy_free_list(struct drm_buddy * mm,struct list_head * objects)3086387a3c4SArunpravin void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects)
3096387a3c4SArunpravin {
3106387a3c4SArunpravin 	struct drm_buddy_block *block, *on;
3116387a3c4SArunpravin 
3126387a3c4SArunpravin 	list_for_each_entry_safe(block, on, objects, link) {
3136387a3c4SArunpravin 		drm_buddy_free_block(mm, block);
3146387a3c4SArunpravin 		cond_resched();
3156387a3c4SArunpravin 	}
3166387a3c4SArunpravin 	INIT_LIST_HEAD(objects);
3176387a3c4SArunpravin }
3186387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_free_list);
3196387a3c4SArunpravin 
overlaps(u64 s1,u64 e1,u64 s2,u64 e2)320afea229fSArunpravin static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
321afea229fSArunpravin {
322afea229fSArunpravin 	return s1 <= e2 && e1 >= s2;
323afea229fSArunpravin }
324afea229fSArunpravin 
contains(u64 s1,u64 e1,u64 s2,u64 e2)325afea229fSArunpravin static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2)
326afea229fSArunpravin {
327afea229fSArunpravin 	return s1 <= s2 && e1 >= e2;
328afea229fSArunpravin }
329afea229fSArunpravin 
330afea229fSArunpravin static struct drm_buddy_block *
alloc_range_bias(struct drm_buddy * mm,u64 start,u64 end,unsigned int order)331afea229fSArunpravin alloc_range_bias(struct drm_buddy *mm,
332afea229fSArunpravin 		 u64 start, u64 end,
333afea229fSArunpravin 		 unsigned int order)
334afea229fSArunpravin {
335*5e476625SMatthew Auld 	u64 req_size = mm->chunk_size << order;
336afea229fSArunpravin 	struct drm_buddy_block *block;
337afea229fSArunpravin 	struct drm_buddy_block *buddy;
338afea229fSArunpravin 	LIST_HEAD(dfs);
339afea229fSArunpravin 	int err;
340afea229fSArunpravin 	int i;
341afea229fSArunpravin 
342afea229fSArunpravin 	end = end - 1;
343afea229fSArunpravin 
344afea229fSArunpravin 	for (i = 0; i < mm->n_roots; ++i)
345afea229fSArunpravin 		list_add_tail(&mm->roots[i]->tmp_link, &dfs);
346afea229fSArunpravin 
347afea229fSArunpravin 	do {
348afea229fSArunpravin 		u64 block_start;
349afea229fSArunpravin 		u64 block_end;
350afea229fSArunpravin 
351afea229fSArunpravin 		block = list_first_entry_or_null(&dfs,
352afea229fSArunpravin 						 struct drm_buddy_block,
353afea229fSArunpravin 						 tmp_link);
354afea229fSArunpravin 		if (!block)
355afea229fSArunpravin 			break;
356afea229fSArunpravin 
357afea229fSArunpravin 		list_del(&block->tmp_link);
358afea229fSArunpravin 
359afea229fSArunpravin 		if (drm_buddy_block_order(block) < order)
360afea229fSArunpravin 			continue;
361afea229fSArunpravin 
362afea229fSArunpravin 		block_start = drm_buddy_block_offset(block);
363afea229fSArunpravin 		block_end = block_start + drm_buddy_block_size(mm, block) - 1;
364afea229fSArunpravin 
365afea229fSArunpravin 		if (!overlaps(start, end, block_start, block_end))
366afea229fSArunpravin 			continue;
367afea229fSArunpravin 
368afea229fSArunpravin 		if (drm_buddy_block_is_allocated(block))
369afea229fSArunpravin 			continue;
370afea229fSArunpravin 
371*5e476625SMatthew Auld 		if (block_start < start || block_end > end) {
372*5e476625SMatthew Auld 			u64 adjusted_start = max(block_start, start);
373*5e476625SMatthew Auld 			u64 adjusted_end = min(block_end, end);
374*5e476625SMatthew Auld 
375*5e476625SMatthew Auld 			if (round_down(adjusted_end + 1, req_size) <=
376*5e476625SMatthew Auld 			    round_up(adjusted_start, req_size))
377*5e476625SMatthew Auld 				continue;
378*5e476625SMatthew Auld 		}
379*5e476625SMatthew Auld 
380afea229fSArunpravin 		if (contains(start, end, block_start, block_end) &&
381afea229fSArunpravin 		    order == drm_buddy_block_order(block)) {
382afea229fSArunpravin 			/*
383afea229fSArunpravin 			 * Find the free block within the range.
3846387a3c4SArunpravin 			 */
385afea229fSArunpravin 			if (drm_buddy_block_is_free(block))
386afea229fSArunpravin 				return block;
387afea229fSArunpravin 
388afea229fSArunpravin 			continue;
389afea229fSArunpravin 		}
390afea229fSArunpravin 
391afea229fSArunpravin 		if (!drm_buddy_block_is_split(block)) {
392afea229fSArunpravin 			err = split_block(mm, block);
393afea229fSArunpravin 			if (unlikely(err))
394afea229fSArunpravin 				goto err_undo;
395afea229fSArunpravin 		}
396afea229fSArunpravin 
397afea229fSArunpravin 		list_add(&block->right->tmp_link, &dfs);
398afea229fSArunpravin 		list_add(&block->left->tmp_link, &dfs);
399afea229fSArunpravin 	} while (1);
400afea229fSArunpravin 
401afea229fSArunpravin 	return ERR_PTR(-ENOSPC);
402afea229fSArunpravin 
403afea229fSArunpravin err_undo:
404afea229fSArunpravin 	/*
405afea229fSArunpravin 	 * We really don't want to leave around a bunch of split blocks, since
406afea229fSArunpravin 	 * bigger is better, so make sure we merge everything back before we
407afea229fSArunpravin 	 * free the allocated blocks.
408afea229fSArunpravin 	 */
40992937f17SArunpravin 	buddy = __get_buddy(block);
410afea229fSArunpravin 	if (buddy &&
411afea229fSArunpravin 	    (drm_buddy_block_is_free(block) &&
412afea229fSArunpravin 	     drm_buddy_block_is_free(buddy)))
413afea229fSArunpravin 		__drm_buddy_free(mm, block);
414afea229fSArunpravin 	return ERR_PTR(err);
415afea229fSArunpravin }
416afea229fSArunpravin 
417afea229fSArunpravin static struct drm_buddy_block *
get_maxblock(struct drm_buddy * mm,unsigned int order)4185640e816SArunpravin Paneer Selvam get_maxblock(struct drm_buddy *mm, unsigned int order)
419476e4063SArunpravin {
420476e4063SArunpravin 	struct drm_buddy_block *max_block = NULL, *node;
4215640e816SArunpravin Paneer Selvam 	unsigned int i;
422476e4063SArunpravin 
4235640e816SArunpravin Paneer Selvam 	for (i = order; i <= mm->max_order; ++i) {
4245640e816SArunpravin Paneer Selvam 		if (!list_empty(&mm->free_list[i])) {
4255640e816SArunpravin Paneer Selvam 			node = list_last_entry(&mm->free_list[i],
426476e4063SArunpravin 					       struct drm_buddy_block,
427476e4063SArunpravin 					       link);
4285640e816SArunpravin Paneer Selvam 			if (!max_block) {
429476e4063SArunpravin 				max_block = node;
4305640e816SArunpravin Paneer Selvam 				continue;
4315640e816SArunpravin Paneer Selvam 			}
4325640e816SArunpravin Paneer Selvam 
4335640e816SArunpravin Paneer Selvam 			if (drm_buddy_block_offset(node) >
4345640e816SArunpravin Paneer Selvam 			    drm_buddy_block_offset(max_block)) {
4355640e816SArunpravin Paneer Selvam 				max_block = node;
4365640e816SArunpravin Paneer Selvam 			}
4375640e816SArunpravin Paneer Selvam 		}
438476e4063SArunpravin 	}
439476e4063SArunpravin 
440476e4063SArunpravin 	return max_block;
441476e4063SArunpravin }
442476e4063SArunpravin 
443476e4063SArunpravin static struct drm_buddy_block *
alloc_from_freelist(struct drm_buddy * mm,unsigned int order,unsigned long flags)444afea229fSArunpravin alloc_from_freelist(struct drm_buddy *mm,
445afea229fSArunpravin 		    unsigned int order,
446afea229fSArunpravin 		    unsigned long flags)
4476387a3c4SArunpravin {
4486387a3c4SArunpravin 	struct drm_buddy_block *block = NULL;
4495640e816SArunpravin Paneer Selvam 	unsigned int tmp;
4506387a3c4SArunpravin 	int err;
4516387a3c4SArunpravin 
452476e4063SArunpravin 	if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
4535640e816SArunpravin Paneer Selvam 		block = get_maxblock(mm, order);
454476e4063SArunpravin 		if (block)
4555640e816SArunpravin Paneer Selvam 			/* Store the obtained block order */
4565640e816SArunpravin Paneer Selvam 			tmp = drm_buddy_block_order(block);
457476e4063SArunpravin 	} else {
4585640e816SArunpravin Paneer Selvam 		for (tmp = order; tmp <= mm->max_order; ++tmp) {
4595640e816SArunpravin Paneer Selvam 			if (!list_empty(&mm->free_list[tmp])) {
4605640e816SArunpravin Paneer Selvam 				block = list_last_entry(&mm->free_list[tmp],
4616387a3c4SArunpravin 							struct drm_buddy_block,
4626387a3c4SArunpravin 							link);
4636387a3c4SArunpravin 				if (block)
4646387a3c4SArunpravin 					break;
4656387a3c4SArunpravin 			}
466476e4063SArunpravin 		}
4675640e816SArunpravin Paneer Selvam 	}
4686387a3c4SArunpravin 
4696387a3c4SArunpravin 	if (!block)
4706387a3c4SArunpravin 		return ERR_PTR(-ENOSPC);
4716387a3c4SArunpravin 
4726387a3c4SArunpravin 	BUG_ON(!drm_buddy_block_is_free(block));
4736387a3c4SArunpravin 
4745640e816SArunpravin Paneer Selvam 	while (tmp != order) {
4756387a3c4SArunpravin 		err = split_block(mm, block);
4766387a3c4SArunpravin 		if (unlikely(err))
477afea229fSArunpravin 			goto err_undo;
4786387a3c4SArunpravin 
479afea229fSArunpravin 		block = block->right;
4805640e816SArunpravin Paneer Selvam 		tmp--;
4816387a3c4SArunpravin 	}
4826387a3c4SArunpravin 	return block;
4836387a3c4SArunpravin 
484afea229fSArunpravin err_undo:
4855640e816SArunpravin Paneer Selvam 	if (tmp != order)
4866387a3c4SArunpravin 		__drm_buddy_free(mm, block);
4876387a3c4SArunpravin 	return ERR_PTR(err);
4886387a3c4SArunpravin }
4896387a3c4SArunpravin 
__alloc_range(struct drm_buddy * mm,struct list_head * dfs,u64 start,u64 size,struct list_head * blocks)490afea229fSArunpravin static int __alloc_range(struct drm_buddy *mm,
491afea229fSArunpravin 			 struct list_head *dfs,
492afea229fSArunpravin 			 u64 start, u64 size,
493afea229fSArunpravin 			 struct list_head *blocks)
4946387a3c4SArunpravin {
4956387a3c4SArunpravin 	struct drm_buddy_block *block;
4966387a3c4SArunpravin 	struct drm_buddy_block *buddy;
4976387a3c4SArunpravin 	LIST_HEAD(allocated);
4986387a3c4SArunpravin 	u64 end;
4996387a3c4SArunpravin 	int err;
5006387a3c4SArunpravin 
5016387a3c4SArunpravin 	end = start + size - 1;
5026387a3c4SArunpravin 
5036387a3c4SArunpravin 	do {
5046387a3c4SArunpravin 		u64 block_start;
5056387a3c4SArunpravin 		u64 block_end;
5066387a3c4SArunpravin 
507afea229fSArunpravin 		block = list_first_entry_or_null(dfs,
5086387a3c4SArunpravin 						 struct drm_buddy_block,
5096387a3c4SArunpravin 						 tmp_link);
5106387a3c4SArunpravin 		if (!block)
5116387a3c4SArunpravin 			break;
5126387a3c4SArunpravin 
5136387a3c4SArunpravin 		list_del(&block->tmp_link);
5146387a3c4SArunpravin 
5156387a3c4SArunpravin 		block_start = drm_buddy_block_offset(block);
5166387a3c4SArunpravin 		block_end = block_start + drm_buddy_block_size(mm, block) - 1;
5176387a3c4SArunpravin 
5186387a3c4SArunpravin 		if (!overlaps(start, end, block_start, block_end))
5196387a3c4SArunpravin 			continue;
5206387a3c4SArunpravin 
5216387a3c4SArunpravin 		if (drm_buddy_block_is_allocated(block)) {
5226387a3c4SArunpravin 			err = -ENOSPC;
5236387a3c4SArunpravin 			goto err_free;
5246387a3c4SArunpravin 		}
5256387a3c4SArunpravin 
5266387a3c4SArunpravin 		if (contains(start, end, block_start, block_end)) {
5276387a3c4SArunpravin 			if (!drm_buddy_block_is_free(block)) {
5286387a3c4SArunpravin 				err = -ENOSPC;
5296387a3c4SArunpravin 				goto err_free;
5306387a3c4SArunpravin 			}
5316387a3c4SArunpravin 
5326387a3c4SArunpravin 			mark_allocated(block);
5336387a3c4SArunpravin 			mm->avail -= drm_buddy_block_size(mm, block);
5346387a3c4SArunpravin 			list_add_tail(&block->link, &allocated);
5356387a3c4SArunpravin 			continue;
5366387a3c4SArunpravin 		}
5376387a3c4SArunpravin 
5386387a3c4SArunpravin 		if (!drm_buddy_block_is_split(block)) {
5396387a3c4SArunpravin 			err = split_block(mm, block);
5406387a3c4SArunpravin 			if (unlikely(err))
5416387a3c4SArunpravin 				goto err_undo;
5426387a3c4SArunpravin 		}
5436387a3c4SArunpravin 
544afea229fSArunpravin 		list_add(&block->right->tmp_link, dfs);
545afea229fSArunpravin 		list_add(&block->left->tmp_link, dfs);
5466387a3c4SArunpravin 	} while (1);
5476387a3c4SArunpravin 
5486387a3c4SArunpravin 	list_splice_tail(&allocated, blocks);
5496387a3c4SArunpravin 	return 0;
5506387a3c4SArunpravin 
5516387a3c4SArunpravin err_undo:
5526387a3c4SArunpravin 	/*
5536387a3c4SArunpravin 	 * We really don't want to leave around a bunch of split blocks, since
5546387a3c4SArunpravin 	 * bigger is better, so make sure we merge everything back before we
5556387a3c4SArunpravin 	 * free the allocated blocks.
5566387a3c4SArunpravin 	 */
55792937f17SArunpravin 	buddy = __get_buddy(block);
5586387a3c4SArunpravin 	if (buddy &&
5596387a3c4SArunpravin 	    (drm_buddy_block_is_free(block) &&
5606387a3c4SArunpravin 	     drm_buddy_block_is_free(buddy)))
5616387a3c4SArunpravin 		__drm_buddy_free(mm, block);
5626387a3c4SArunpravin 
5636387a3c4SArunpravin err_free:
5646387a3c4SArunpravin 	drm_buddy_free_list(mm, &allocated);
5656387a3c4SArunpravin 	return err;
5666387a3c4SArunpravin }
567afea229fSArunpravin 
__drm_buddy_alloc_range(struct drm_buddy * mm,u64 start,u64 size,struct list_head * blocks)568afea229fSArunpravin static int __drm_buddy_alloc_range(struct drm_buddy *mm,
569afea229fSArunpravin 				   u64 start,
570afea229fSArunpravin 				   u64 size,
571afea229fSArunpravin 				   struct list_head *blocks)
572afea229fSArunpravin {
573afea229fSArunpravin 	LIST_HEAD(dfs);
574afea229fSArunpravin 	int i;
575afea229fSArunpravin 
576afea229fSArunpravin 	for (i = 0; i < mm->n_roots; ++i)
577afea229fSArunpravin 		list_add_tail(&mm->roots[i]->tmp_link, &dfs);
578afea229fSArunpravin 
579afea229fSArunpravin 	return __alloc_range(mm, &dfs, start, size, blocks);
580afea229fSArunpravin }
581afea229fSArunpravin 
582afea229fSArunpravin /**
58395ee2a8bSArunpravin  * drm_buddy_block_trim - free unused pages
58495ee2a8bSArunpravin  *
58595ee2a8bSArunpravin  * @mm: DRM buddy manager
58695ee2a8bSArunpravin  * @new_size: original size requested
58795ee2a8bSArunpravin  * @blocks: Input and output list of allocated blocks.
58895ee2a8bSArunpravin  * MUST contain single block as input to be trimmed.
58995ee2a8bSArunpravin  * On success will contain the newly allocated blocks
59095ee2a8bSArunpravin  * making up the @new_size. Blocks always appear in
59195ee2a8bSArunpravin  * ascending order
59295ee2a8bSArunpravin  *
59395ee2a8bSArunpravin  * For contiguous allocation, we round up the size to the nearest
59495ee2a8bSArunpravin  * power of two value, drivers consume *actual* size, so remaining
59595ee2a8bSArunpravin  * portions are unused and can be optionally freed with this function
59695ee2a8bSArunpravin  *
59795ee2a8bSArunpravin  * Returns:
59895ee2a8bSArunpravin  * 0 on success, error code on failure.
59995ee2a8bSArunpravin  */
drm_buddy_block_trim(struct drm_buddy * mm,u64 new_size,struct list_head * blocks)60095ee2a8bSArunpravin int drm_buddy_block_trim(struct drm_buddy *mm,
60195ee2a8bSArunpravin 			 u64 new_size,
60295ee2a8bSArunpravin 			 struct list_head *blocks)
60395ee2a8bSArunpravin {
60495ee2a8bSArunpravin 	struct drm_buddy_block *parent;
60595ee2a8bSArunpravin 	struct drm_buddy_block *block;
60695ee2a8bSArunpravin 	LIST_HEAD(dfs);
60795ee2a8bSArunpravin 	u64 new_start;
60895ee2a8bSArunpravin 	int err;
60995ee2a8bSArunpravin 
61095ee2a8bSArunpravin 	if (!list_is_singular(blocks))
61195ee2a8bSArunpravin 		return -EINVAL;
61295ee2a8bSArunpravin 
61395ee2a8bSArunpravin 	block = list_first_entry(blocks,
61495ee2a8bSArunpravin 				 struct drm_buddy_block,
61595ee2a8bSArunpravin 				 link);
61695ee2a8bSArunpravin 
61795ee2a8bSArunpravin 	if (WARN_ON(!drm_buddy_block_is_allocated(block)))
61895ee2a8bSArunpravin 		return -EINVAL;
61995ee2a8bSArunpravin 
62095ee2a8bSArunpravin 	if (new_size > drm_buddy_block_size(mm, block))
62195ee2a8bSArunpravin 		return -EINVAL;
62295ee2a8bSArunpravin 
62395ee2a8bSArunpravin 	if (!new_size || !IS_ALIGNED(new_size, mm->chunk_size))
62495ee2a8bSArunpravin 		return -EINVAL;
62595ee2a8bSArunpravin 
62695ee2a8bSArunpravin 	if (new_size == drm_buddy_block_size(mm, block))
62795ee2a8bSArunpravin 		return 0;
62895ee2a8bSArunpravin 
62995ee2a8bSArunpravin 	list_del(&block->link);
63095ee2a8bSArunpravin 	mark_free(mm, block);
63195ee2a8bSArunpravin 	mm->avail += drm_buddy_block_size(mm, block);
63295ee2a8bSArunpravin 
63395ee2a8bSArunpravin 	/* Prevent recursively freeing this node */
63495ee2a8bSArunpravin 	parent = block->parent;
63595ee2a8bSArunpravin 	block->parent = NULL;
63695ee2a8bSArunpravin 
63795ee2a8bSArunpravin 	new_start = drm_buddy_block_offset(block);
63895ee2a8bSArunpravin 	list_add(&block->tmp_link, &dfs);
63995ee2a8bSArunpravin 	err =  __alloc_range(mm, &dfs, new_start, new_size, blocks);
64095ee2a8bSArunpravin 	if (err) {
64195ee2a8bSArunpravin 		mark_allocated(block);
64295ee2a8bSArunpravin 		mm->avail -= drm_buddy_block_size(mm, block);
64395ee2a8bSArunpravin 		list_add(&block->link, blocks);
64495ee2a8bSArunpravin 	}
64595ee2a8bSArunpravin 
64695ee2a8bSArunpravin 	block->parent = parent;
64795ee2a8bSArunpravin 	return err;
64895ee2a8bSArunpravin }
64995ee2a8bSArunpravin EXPORT_SYMBOL(drm_buddy_block_trim);
65095ee2a8bSArunpravin 
65195ee2a8bSArunpravin /**
652afea229fSArunpravin  * drm_buddy_alloc_blocks - allocate power-of-two blocks
653afea229fSArunpravin  *
654afea229fSArunpravin  * @mm: DRM buddy manager to allocate from
655afea229fSArunpravin  * @start: start of the allowed range for this block
656afea229fSArunpravin  * @end: end of the allowed range for this block
657afea229fSArunpravin  * @size: size of the allocation
658afea229fSArunpravin  * @min_page_size: alignment of the allocation
659afea229fSArunpravin  * @blocks: output list head to add allocated blocks
660afea229fSArunpravin  * @flags: DRM_BUDDY_*_ALLOCATION flags
661afea229fSArunpravin  *
662afea229fSArunpravin  * alloc_range_bias() called on range limitations, which traverses
663afea229fSArunpravin  * the tree and returns the desired block.
664afea229fSArunpravin  *
665afea229fSArunpravin  * alloc_from_freelist() called when *no* range restrictions
666afea229fSArunpravin  * are enforced, which picks the block from the freelist.
667afea229fSArunpravin  *
668afea229fSArunpravin  * Returns:
669afea229fSArunpravin  * 0 on success, error code on failure.
670afea229fSArunpravin  */
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)671afea229fSArunpravin int drm_buddy_alloc_blocks(struct drm_buddy *mm,
672afea229fSArunpravin 			   u64 start, u64 end, u64 size,
673afea229fSArunpravin 			   u64 min_page_size,
674afea229fSArunpravin 			   struct list_head *blocks,
675afea229fSArunpravin 			   unsigned long flags)
676afea229fSArunpravin {
677afea229fSArunpravin 	struct drm_buddy_block *block = NULL;
678afea229fSArunpravin 	unsigned int min_order, order;
679afea229fSArunpravin 	unsigned long pages;
680afea229fSArunpravin 	LIST_HEAD(allocated);
681afea229fSArunpravin 	int err;
682afea229fSArunpravin 
683afea229fSArunpravin 	if (size < mm->chunk_size)
684afea229fSArunpravin 		return -EINVAL;
685afea229fSArunpravin 
686afea229fSArunpravin 	if (min_page_size < mm->chunk_size)
687afea229fSArunpravin 		return -EINVAL;
688afea229fSArunpravin 
689afea229fSArunpravin 	if (!is_power_of_2(min_page_size))
690afea229fSArunpravin 		return -EINVAL;
691afea229fSArunpravin 
692afea229fSArunpravin 	if (!IS_ALIGNED(start | end | size, mm->chunk_size))
693afea229fSArunpravin 		return -EINVAL;
694afea229fSArunpravin 
695afea229fSArunpravin 	if (end > mm->size)
696afea229fSArunpravin 		return -EINVAL;
697afea229fSArunpravin 
698afea229fSArunpravin 	if (range_overflows(start, size, mm->size))
699afea229fSArunpravin 		return -EINVAL;
700afea229fSArunpravin 
701afea229fSArunpravin 	/* Actual range allocation */
702afea229fSArunpravin 	if (start + size == end)
703afea229fSArunpravin 		return __drm_buddy_alloc_range(mm, start, size, blocks);
704afea229fSArunpravin 
7055f778760SArunpravin Paneer Selvam 	if (!IS_ALIGNED(size, min_page_size))
7065f778760SArunpravin Paneer Selvam 		return -EINVAL;
7075f778760SArunpravin Paneer Selvam 
708afea229fSArunpravin 	pages = size >> ilog2(mm->chunk_size);
709afea229fSArunpravin 	order = fls(pages) - 1;
710afea229fSArunpravin 	min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
711afea229fSArunpravin 
712afea229fSArunpravin 	do {
713afea229fSArunpravin 		order = min(order, (unsigned int)fls(pages) - 1);
714afea229fSArunpravin 		BUG_ON(order > mm->max_order);
715afea229fSArunpravin 		BUG_ON(order < min_order);
716afea229fSArunpravin 
717afea229fSArunpravin 		do {
718afea229fSArunpravin 			if (flags & DRM_BUDDY_RANGE_ALLOCATION)
719afea229fSArunpravin 				/* Allocate traversing within the range */
720afea229fSArunpravin 				block = alloc_range_bias(mm, start, end, order);
721afea229fSArunpravin 			else
722afea229fSArunpravin 				/* Allocate from freelist */
723afea229fSArunpravin 				block = alloc_from_freelist(mm, order, flags);
724afea229fSArunpravin 
725afea229fSArunpravin 			if (!IS_ERR(block))
726afea229fSArunpravin 				break;
727afea229fSArunpravin 
728afea229fSArunpravin 			if (order-- == min_order) {
729afea229fSArunpravin 				err = -ENOSPC;
730afea229fSArunpravin 				goto err_free;
731afea229fSArunpravin 			}
732afea229fSArunpravin 		} while (1);
733afea229fSArunpravin 
734afea229fSArunpravin 		mark_allocated(block);
735afea229fSArunpravin 		mm->avail -= drm_buddy_block_size(mm, block);
736afea229fSArunpravin 		kmemleak_update_trace(block);
737afea229fSArunpravin 		list_add_tail(&block->link, &allocated);
738afea229fSArunpravin 
739afea229fSArunpravin 		pages -= BIT(order);
740afea229fSArunpravin 
741afea229fSArunpravin 		if (!pages)
742afea229fSArunpravin 			break;
743afea229fSArunpravin 	} while (1);
744afea229fSArunpravin 
745afea229fSArunpravin 	list_splice_tail(&allocated, blocks);
746afea229fSArunpravin 	return 0;
747afea229fSArunpravin 
748afea229fSArunpravin err_free:
749afea229fSArunpravin 	drm_buddy_free_list(mm, &allocated);
750afea229fSArunpravin 	return err;
751afea229fSArunpravin }
752afea229fSArunpravin EXPORT_SYMBOL(drm_buddy_alloc_blocks);
7536387a3c4SArunpravin 
7546387a3c4SArunpravin /**
7556387a3c4SArunpravin  * drm_buddy_block_print - print block information
7566387a3c4SArunpravin  *
7576387a3c4SArunpravin  * @mm: DRM buddy manager
7586387a3c4SArunpravin  * @block: DRM buddy block
7596387a3c4SArunpravin  * @p: DRM printer to use
7606387a3c4SArunpravin  */
drm_buddy_block_print(struct drm_buddy * mm,struct drm_buddy_block * block,struct drm_printer * p)7616387a3c4SArunpravin void drm_buddy_block_print(struct drm_buddy *mm,
7626387a3c4SArunpravin 			   struct drm_buddy_block *block,
7636387a3c4SArunpravin 			   struct drm_printer *p)
7646387a3c4SArunpravin {
7656387a3c4SArunpravin 	u64 start = drm_buddy_block_offset(block);
7666387a3c4SArunpravin 	u64 size = drm_buddy_block_size(mm, block);
7676387a3c4SArunpravin 
7686387a3c4SArunpravin 	drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size);
7696387a3c4SArunpravin }
7706387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_block_print);
7716387a3c4SArunpravin 
7726387a3c4SArunpravin /**
7736387a3c4SArunpravin  * drm_buddy_print - print allocator state
7746387a3c4SArunpravin  *
7756387a3c4SArunpravin  * @mm: DRM buddy manager
7766387a3c4SArunpravin  * @p: DRM printer to use
7776387a3c4SArunpravin  */
drm_buddy_print(struct drm_buddy * mm,struct drm_printer * p)7786387a3c4SArunpravin void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
7796387a3c4SArunpravin {
7806387a3c4SArunpravin 	int order;
7816387a3c4SArunpravin 
7826387a3c4SArunpravin 	drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB\n",
7836387a3c4SArunpravin 		   mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20);
7846387a3c4SArunpravin 
7856387a3c4SArunpravin 	for (order = mm->max_order; order >= 0; order--) {
7866387a3c4SArunpravin 		struct drm_buddy_block *block;
7876387a3c4SArunpravin 		u64 count = 0, free;
7886387a3c4SArunpravin 
7896387a3c4SArunpravin 		list_for_each_entry(block, &mm->free_list[order], link) {
7906387a3c4SArunpravin 			BUG_ON(!drm_buddy_block_is_free(block));
7916387a3c4SArunpravin 			count++;
7926387a3c4SArunpravin 		}
7936387a3c4SArunpravin 
794cd11589bSMa Jun 		drm_printf(p, "order-%2d ", order);
7956387a3c4SArunpravin 
7966387a3c4SArunpravin 		free = count * (mm->chunk_size << order);
7976387a3c4SArunpravin 		if (free < SZ_1M)
798cd11589bSMa Jun 			drm_printf(p, "free: %8llu KiB", free >> 10);
7996387a3c4SArunpravin 		else
800cd11589bSMa Jun 			drm_printf(p, "free: %8llu MiB", free >> 20);
8016387a3c4SArunpravin 
802cd11589bSMa Jun 		drm_printf(p, ", blocks: %llu\n", count);
8036387a3c4SArunpravin 	}
8046387a3c4SArunpravin }
8056387a3c4SArunpravin EXPORT_SYMBOL(drm_buddy_print);
8066387a3c4SArunpravin 
drm_buddy_module_exit(void)8076387a3c4SArunpravin static void drm_buddy_module_exit(void)
8086387a3c4SArunpravin {
8096387a3c4SArunpravin 	kmem_cache_destroy(slab_blocks);
8106387a3c4SArunpravin }
8116387a3c4SArunpravin 
drm_buddy_module_init(void)8126387a3c4SArunpravin static int __init drm_buddy_module_init(void)
8136387a3c4SArunpravin {
8146387a3c4SArunpravin 	slab_blocks = KMEM_CACHE(drm_buddy_block, 0);
8156387a3c4SArunpravin 	if (!slab_blocks)
8166387a3c4SArunpravin 		return -ENOMEM;
8176387a3c4SArunpravin 
8186387a3c4SArunpravin 	return 0;
8196387a3c4SArunpravin }
8206387a3c4SArunpravin 
8216387a3c4SArunpravin module_init(drm_buddy_module_init);
8226387a3c4SArunpravin module_exit(drm_buddy_module_exit);
8236387a3c4SArunpravin 
8246387a3c4SArunpravin MODULE_DESCRIPTION("DRM Buddy Allocator");
8256387a3c4SArunpravin MODULE_LICENSE("Dual MIT/GPL");
826