Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0 OR MIT */
2 : /**************************************************************************
3 : *
4 : * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA
5 : * All Rights Reserved.
6 : *
7 : * Permission is hereby granted, free of charge, to any person obtaining a
8 : * copy of this software and associated documentation files (the
9 : * "Software"), to deal in the Software without restriction, including
10 : * without limitation the rights to use, copy, modify, merge, publish,
11 : * distribute, sub license, and/or sell copies of the Software, and to
12 : * permit persons to whom the Software is furnished to do so, subject to
13 : * the following conditions:
14 : *
15 : * The above copyright notice and this permission notice (including the
16 : * next paragraph) shall be included in all copies or substantial portions
17 : * of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 : * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 : * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 : * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 : * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 : *
27 : **************************************************************************/
28 : /*
29 : * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
30 : */
31 :
32 : #include <drm/ttm/ttm_device.h>
33 : #include <drm/ttm/ttm_placement.h>
34 : #include <drm/ttm/ttm_range_manager.h>
35 : #include <drm/ttm/ttm_bo_api.h>
36 : #include <drm/drm_mm.h>
37 : #include <linux/slab.h>
38 : #include <linux/spinlock.h>
39 :
40 : /*
41 : * Currently we use a spinlock for the lock, but a mutex *may* be
42 : * more appropriate to reduce scheduling latency if the range manager
43 : * ends up with very fragmented allocation patterns.
44 : */
45 :
46 : struct ttm_range_manager {
47 : struct ttm_resource_manager manager;
48 : struct drm_mm mm;
49 : spinlock_t lock;
50 : };
51 :
52 : static inline struct ttm_range_manager *
53 : to_range_manager(struct ttm_resource_manager *man)
54 : {
55 0 : return container_of(man, struct ttm_range_manager, manager);
56 : }
57 :
58 0 : static int ttm_range_man_alloc(struct ttm_resource_manager *man,
59 : struct ttm_buffer_object *bo,
60 : const struct ttm_place *place,
61 : struct ttm_resource **res)
62 : {
63 0 : struct ttm_range_manager *rman = to_range_manager(man);
64 : struct ttm_range_mgr_node *node;
65 0 : struct drm_mm *mm = &rman->mm;
66 : enum drm_mm_insert_mode mode;
67 : unsigned long lpfn;
68 : int ret;
69 :
70 0 : lpfn = place->lpfn;
71 0 : if (!lpfn)
72 0 : lpfn = man->size;
73 :
74 0 : node = kzalloc(struct_size(node, mm_nodes, 1), GFP_KERNEL);
75 0 : if (!node)
76 : return -ENOMEM;
77 :
78 0 : mode = DRM_MM_INSERT_BEST;
79 0 : if (place->flags & TTM_PL_FLAG_TOPDOWN)
80 0 : mode = DRM_MM_INSERT_HIGH;
81 :
82 0 : ttm_resource_init(bo, place, &node->base);
83 :
84 0 : spin_lock(&rman->lock);
85 0 : ret = drm_mm_insert_node_in_range(mm, &node->mm_nodes[0],
86 0 : node->base.num_pages,
87 0 : bo->page_alignment, 0,
88 0 : place->fpfn, lpfn, mode);
89 0 : spin_unlock(&rman->lock);
90 :
91 0 : if (unlikely(ret)) {
92 0 : ttm_resource_fini(man, &node->base);
93 0 : kfree(node);
94 0 : return ret;
95 : }
96 :
97 0 : node->base.start = node->mm_nodes[0].start;
98 0 : *res = &node->base;
99 0 : return 0;
100 : }
101 :
102 0 : static void ttm_range_man_free(struct ttm_resource_manager *man,
103 : struct ttm_resource *res)
104 : {
105 0 : struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);
106 0 : struct ttm_range_manager *rman = to_range_manager(man);
107 :
108 0 : spin_lock(&rman->lock);
109 0 : drm_mm_remove_node(&node->mm_nodes[0]);
110 0 : spin_unlock(&rman->lock);
111 :
112 0 : ttm_resource_fini(man, res);
113 0 : kfree(node);
114 0 : }
115 :
116 0 : static void ttm_range_man_debug(struct ttm_resource_manager *man,
117 : struct drm_printer *printer)
118 : {
119 0 : struct ttm_range_manager *rman = to_range_manager(man);
120 :
121 0 : spin_lock(&rman->lock);
122 0 : drm_mm_print(&rman->mm, printer);
123 0 : spin_unlock(&rman->lock);
124 0 : }
125 :
126 : static const struct ttm_resource_manager_func ttm_range_manager_func = {
127 : .alloc = ttm_range_man_alloc,
128 : .free = ttm_range_man_free,
129 : .debug = ttm_range_man_debug
130 : };
131 :
132 : /**
133 : * ttm_range_man_init_nocheck - Initialise a generic range manager for the
134 : * selected memory type.
135 : *
136 : * @bdev: ttm device
137 : * @type: memory manager type
138 : * @use_tt: if the memory manager uses tt
139 : * @p_size: size of area to be managed in pages.
140 : *
141 : * The range manager is installed for this device in the type slot.
142 : *
143 : * Return: %0 on success or a negative error code on failure
144 : */
145 0 : int ttm_range_man_init_nocheck(struct ttm_device *bdev,
146 : unsigned type, bool use_tt,
147 : unsigned long p_size)
148 : {
149 : struct ttm_resource_manager *man;
150 : struct ttm_range_manager *rman;
151 :
152 0 : rman = kzalloc(sizeof(*rman), GFP_KERNEL);
153 0 : if (!rman)
154 : return -ENOMEM;
155 :
156 0 : man = &rman->manager;
157 0 : man->use_tt = use_tt;
158 :
159 0 : man->func = &ttm_range_manager_func;
160 :
161 0 : ttm_resource_manager_init(man, bdev, p_size);
162 :
163 0 : drm_mm_init(&rman->mm, 0, p_size);
164 0 : spin_lock_init(&rman->lock);
165 :
166 0 : ttm_set_driver_manager(bdev, type, &rman->manager);
167 0 : ttm_resource_manager_set_used(man, true);
168 0 : return 0;
169 : }
170 : EXPORT_SYMBOL(ttm_range_man_init_nocheck);
171 :
172 : /**
173 : * ttm_range_man_fini_nocheck - Remove the generic range manager from a slot
174 : * and tear it down.
175 : *
176 : * @bdev: ttm device
177 : * @type: memory manager type
178 : *
179 : * Return: %0 on success or a negative error code on failure
180 : */
181 0 : int ttm_range_man_fini_nocheck(struct ttm_device *bdev,
182 : unsigned type)
183 : {
184 0 : struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
185 0 : struct ttm_range_manager *rman = to_range_manager(man);
186 0 : struct drm_mm *mm = &rman->mm;
187 : int ret;
188 :
189 0 : if (!man)
190 : return 0;
191 :
192 0 : ttm_resource_manager_set_used(man, false);
193 :
194 0 : ret = ttm_resource_manager_evict_all(bdev, man);
195 0 : if (ret)
196 : return ret;
197 :
198 0 : spin_lock(&rman->lock);
199 0 : drm_mm_clean(mm);
200 0 : drm_mm_takedown(mm);
201 0 : spin_unlock(&rman->lock);
202 :
203 : ttm_resource_manager_cleanup(man);
204 0 : ttm_set_driver_manager(bdev, type, NULL);
205 0 : kfree(rman);
206 0 : return 0;
207 : }
208 : EXPORT_SYMBOL(ttm_range_man_fini_nocheck);
|