fbdev: Garbage collect fbdev scrolling acceleration, part 1 (from TODO list)
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / i915_ttm_buddy_manager.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2021 Intel Corporation
4  */
5
6 #include <linux/slab.h>
7
8 #include <drm/ttm/ttm_bo_driver.h>
9 #include <drm/ttm/ttm_placement.h>
10
11 #include "i915_ttm_buddy_manager.h"
12
13 #include "i915_buddy.h"
14 #include "i915_gem.h"
15
16 struct i915_ttm_buddy_manager {
17         struct ttm_resource_manager manager;
18         struct i915_buddy_mm mm;
19         struct list_head reserved;
20         struct mutex lock;
21         u64 default_page_size;
22 };
23
24 static struct i915_ttm_buddy_manager *
25 to_buddy_manager(struct ttm_resource_manager *man)
26 {
27         return container_of(man, struct i915_ttm_buddy_manager, manager);
28 }
29
30 static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
31                                     struct ttm_buffer_object *bo,
32                                     const struct ttm_place *place,
33                                     struct ttm_resource **res)
34 {
35         struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
36         struct i915_ttm_buddy_resource *bman_res;
37         struct i915_buddy_mm *mm = &bman->mm;
38         unsigned long n_pages;
39         unsigned int min_order;
40         u64 min_page_size;
41         u64 size;
42         int err;
43
44         GEM_BUG_ON(place->fpfn || place->lpfn);
45
46         bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL);
47         if (!bman_res)
48                 return -ENOMEM;
49
50         ttm_resource_init(bo, place, &bman_res->base);
51         INIT_LIST_HEAD(&bman_res->blocks);
52         bman_res->mm = mm;
53
54         GEM_BUG_ON(!bman_res->base.num_pages);
55         size = bman_res->base.num_pages << PAGE_SHIFT;
56
57         min_page_size = bman->default_page_size;
58         if (bo->page_alignment)
59                 min_page_size = bo->page_alignment << PAGE_SHIFT;
60
61         GEM_BUG_ON(min_page_size < mm->chunk_size);
62         min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
63         if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
64                 size = roundup_pow_of_two(size);
65                 min_order = ilog2(size) - ilog2(mm->chunk_size);
66         }
67
68         if (size > mm->size) {
69                 err = -E2BIG;
70                 goto err_free_res;
71         }
72
73         n_pages = size >> ilog2(mm->chunk_size);
74
75         do {
76                 struct i915_buddy_block *block;
77                 unsigned int order;
78
79                 order = fls(n_pages) - 1;
80                 GEM_BUG_ON(order > mm->max_order);
81                 GEM_BUG_ON(order < min_order);
82
83                 do {
84                         mutex_lock(&bman->lock);
85                         block = i915_buddy_alloc(mm, order);
86                         mutex_unlock(&bman->lock);
87                         if (!IS_ERR(block))
88                                 break;
89
90                         if (order-- == min_order) {
91                                 err = -ENOSPC;
92                                 goto err_free_blocks;
93                         }
94                 } while (1);
95
96                 n_pages -= BIT(order);
97
98                 list_add_tail(&block->link, &bman_res->blocks);
99
100                 if (!n_pages)
101                         break;
102         } while (1);
103
104         *res = &bman_res->base;
105         return 0;
106
107 err_free_blocks:
108         mutex_lock(&bman->lock);
109         i915_buddy_free_list(mm, &bman_res->blocks);
110         mutex_unlock(&bman->lock);
111 err_free_res:
112         kfree(bman_res);
113         return err;
114 }
115
116 static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
117                                     struct ttm_resource *res)
118 {
119         struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
120         struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
121
122         mutex_lock(&bman->lock);
123         i915_buddy_free_list(&bman->mm, &bman_res->blocks);
124         mutex_unlock(&bman->lock);
125
126         kfree(bman_res);
127 }
128
129 static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
130         .alloc = i915_ttm_buddy_man_alloc,
131         .free = i915_ttm_buddy_man_free,
132 };
133
134
135 /**
136  * i915_ttm_buddy_man_init - Setup buddy allocator based ttm manager
137  * @bdev: The ttm device
138  * @type: Memory type we want to manage
139  * @use_tt: Set use_tt for the manager
140  * @size: The size in bytes to manage
141  * @default_page_size: The default minimum page size in bytes for allocations,
142  * this must be at least as large as @chunk_size, and can be overridden by
143  * setting the BO page_alignment, to be larger or smaller as needed.
144  * @chunk_size: The minimum page size in bytes for our allocations i.e
145  * order-zero
146  *
147  * Note that the starting address is assumed to be zero here, since this
148  * simplifies keeping the property where allocated blocks having natural
149  * power-of-two alignment. So long as the real starting address is some large
150  * power-of-two, or naturally start from zero, then this should be fine.  Also
151  * the &i915_ttm_buddy_man_reserve interface can be used to preserve alignment
152  * if say there is some unusable range from the start of the region. We can
153  * revisit this in the future and make the interface accept an actual starting
154  * offset and let it take care of the rest.
155  *
156  * Note that if the @size is not aligned to the @chunk_size then we perform the
157  * required rounding to get the usable size. The final size in pages can be
158  * taken from &ttm_resource_manager.size.
159  *
160  * Return: 0 on success, negative error code on failure.
161  */
162 int i915_ttm_buddy_man_init(struct ttm_device *bdev,
163                             unsigned int type, bool use_tt,
164                             u64 size, u64 default_page_size,
165                             u64 chunk_size)
166 {
167         struct ttm_resource_manager *man;
168         struct i915_ttm_buddy_manager *bman;
169         int err;
170
171         bman = kzalloc(sizeof(*bman), GFP_KERNEL);
172         if (!bman)
173                 return -ENOMEM;
174
175         err = i915_buddy_init(&bman->mm, size, chunk_size);
176         if (err)
177                 goto err_free_bman;
178
179         mutex_init(&bman->lock);
180         INIT_LIST_HEAD(&bman->reserved);
181         GEM_BUG_ON(default_page_size < chunk_size);
182         bman->default_page_size = default_page_size;
183
184         man = &bman->manager;
185         man->use_tt = use_tt;
186         man->func = &i915_ttm_buddy_manager_func;
187         ttm_resource_manager_init(man, bman->mm.size >> PAGE_SHIFT);
188
189         ttm_resource_manager_set_used(man, true);
190         ttm_set_driver_manager(bdev, type, man);
191
192         return 0;
193
194 err_free_bman:
195         kfree(bman);
196         return err;
197 }
198
199 /**
200  * i915_ttm_buddy_man_fini - Destroy the buddy allocator ttm manager
201  * @bdev: The ttm device
202  * @type: Memory type we want to manage
203  *
204  * Note that if we reserved anything with &i915_ttm_buddy_man_reserve, this will
205  * also be freed for us here.
206  *
207  * Return: 0 on success, negative error code on failure.
208  */
209 int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
210 {
211         struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
212         struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
213         struct i915_buddy_mm *mm = &bman->mm;
214         int ret;
215
216         ttm_resource_manager_set_used(man, false);
217
218         ret = ttm_resource_manager_evict_all(bdev, man);
219         if (ret)
220                 return ret;
221
222         ttm_set_driver_manager(bdev, type, NULL);
223
224         mutex_lock(&bman->lock);
225         i915_buddy_free_list(mm, &bman->reserved);
226         i915_buddy_fini(mm);
227         mutex_unlock(&bman->lock);
228
229         ttm_resource_manager_cleanup(man);
230         kfree(bman);
231
232         return 0;
233 }
234
235 /**
236  * i915_ttm_buddy_man_reserve - Reserve address range
237  * @man: The buddy allocator ttm manager
238  * @start: The offset in bytes, where the region start is assumed to be zero
239  * @size: The size in bytes
240  *
241  * Note that the starting address for the region is always assumed to be zero.
242  *
243  * Return: 0 on success, negative error code on failure.
244  */
245 int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
246                                u64 start, u64 size)
247 {
248         struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
249         struct i915_buddy_mm *mm = &bman->mm;
250         int ret;
251
252         mutex_lock(&bman->lock);
253         ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
254         mutex_unlock(&bman->lock);
255
256         return ret;
257 }
258