1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2024 Intel Corporation
4 */
5
6 #include <linux/bitmap.h>
7 #include <linux/mutex.h>
8
9 #include <drm/drm_managed.h>
10
11 #include "xe_assert.h"
12 #include "xe_gt_printk.h"
13 #include "xe_guc.h"
14 #include "xe_guc_id_mgr.h"
15 #include "xe_guc_types.h"
16
idm_to_guc(struct xe_guc_id_mgr * idm)17 static struct xe_guc *idm_to_guc(struct xe_guc_id_mgr *idm)
18 {
19 return container_of(idm, struct xe_guc, submission_state.idm);
20 }
21
idm_to_gt(struct xe_guc_id_mgr * idm)22 static struct xe_gt *idm_to_gt(struct xe_guc_id_mgr *idm)
23 {
24 return guc_to_gt(idm_to_guc(idm));
25 }
26
idm_to_xe(struct xe_guc_id_mgr * idm)27 static struct xe_device *idm_to_xe(struct xe_guc_id_mgr *idm)
28 {
29 return gt_to_xe(idm_to_gt(idm));
30 }
31
32 #define idm_assert(idm, cond) xe_gt_assert(idm_to_gt(idm), cond)
33 #define idm_mutex(idm) (&idm_to_guc(idm)->submission_state.lock)
34
35 static void idm_print_locked(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent);
36
__fini_idm(struct drm_device * drm,void * arg)37 static void __fini_idm(struct drm_device *drm, void *arg)
38 {
39 struct xe_guc_id_mgr *idm = arg;
40
41 mutex_lock(idm_mutex(idm));
42
43 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
44 unsigned int weight = bitmap_weight(idm->bitmap, idm->total);
45
46 if (weight) {
47 struct drm_printer p = xe_gt_info_printer(idm_to_gt(idm));
48
49 xe_gt_err(idm_to_gt(idm), "GUC ID manager unclean (%u/%u)\n",
50 weight, idm->total);
51 idm_print_locked(idm, &p, 1);
52 }
53 }
54
55 bitmap_free(idm->bitmap);
56 idm->bitmap = NULL;
57 idm->total = 0;
58 idm->used = 0;
59
60 mutex_unlock(idm_mutex(idm));
61 }
62
63 /**
64 * xe_guc_id_mgr_init() - Initialize GuC context ID Manager.
65 * @idm: the &xe_guc_id_mgr to initialize
66 * @limit: number of IDs to manage
67 *
68 * The bare-metal or PF driver can pass ~0 as &limit to indicate that all
69 * context IDs supported by the GuC firmware are available for use.
70 *
71 * Only VF drivers will have to provide explicit number of context IDs
72 * that they can use.
73 *
74 * Return: 0 on success or a negative error code on failure.
75 */
xe_guc_id_mgr_init(struct xe_guc_id_mgr * idm,unsigned int limit)76 int xe_guc_id_mgr_init(struct xe_guc_id_mgr *idm, unsigned int limit)
77 {
78 int ret;
79
80 idm_assert(idm, !idm->bitmap);
81 idm_assert(idm, !idm->total);
82 idm_assert(idm, !idm->used);
83
84 if (limit == ~0)
85 limit = GUC_ID_MAX;
86 else if (limit > GUC_ID_MAX)
87 return -ERANGE;
88 else if (!limit)
89 return -EINVAL;
90
91 idm->bitmap = bitmap_zalloc(limit, GFP_KERNEL);
92 if (!idm->bitmap)
93 return -ENOMEM;
94 idm->total = limit;
95
96 ret = drmm_add_action_or_reset(&idm_to_xe(idm)->drm, __fini_idm, idm);
97 if (ret)
98 return ret;
99
100 xe_gt_dbg(idm_to_gt(idm), "using %u GuC ID%s\n",
101 idm->total, str_plural(idm->total));
102 return 0;
103 }
104
find_last_zero_area(unsigned long * bitmap,unsigned int total,unsigned int count)105 static unsigned int find_last_zero_area(unsigned long *bitmap,
106 unsigned int total,
107 unsigned int count)
108 {
109 unsigned int found = total;
110 unsigned int rs, re, range;
111
112 for_each_clear_bitrange(rs, re, bitmap, total) {
113 range = re - rs;
114 if (range < count)
115 continue;
116 found = rs + (range - count);
117 }
118 return found;
119 }
120
idm_reserve_chunk_locked(struct xe_guc_id_mgr * idm,unsigned int count,unsigned int retain)121 static int idm_reserve_chunk_locked(struct xe_guc_id_mgr *idm,
122 unsigned int count, unsigned int retain)
123 {
124 int id;
125
126 idm_assert(idm, count);
127 lockdep_assert_held(idm_mutex(idm));
128
129 if (!idm->total)
130 return -ENODATA;
131
132 if (retain) {
133 /*
134 * For IDs reservations (used on PF for VFs) we want to make
135 * sure there will be at least 'retain' available for the PF
136 */
137 if (idm->used + count + retain > idm->total)
138 return -EDQUOT;
139 /*
140 * ... and we want to reserve highest IDs close to the end.
141 */
142 id = find_last_zero_area(idm->bitmap, idm->total, count);
143 } else {
144 /*
145 * For regular IDs reservations (used by submission code)
146 * we start searching from the lower range of IDs.
147 */
148 id = bitmap_find_next_zero_area(idm->bitmap, idm->total, 0, count, 0);
149 }
150 if (id >= idm->total)
151 return -ENOSPC;
152
153 bitmap_set(idm->bitmap, id, count);
154 idm->used += count;
155
156 return id;
157 }
158
idm_release_chunk_locked(struct xe_guc_id_mgr * idm,unsigned int start,unsigned int count)159 static void idm_release_chunk_locked(struct xe_guc_id_mgr *idm,
160 unsigned int start, unsigned int count)
161 {
162 idm_assert(idm, count);
163 idm_assert(idm, count <= idm->used);
164 idm_assert(idm, start < idm->total);
165 idm_assert(idm, start + count - 1 < idm->total);
166 lockdep_assert_held(idm_mutex(idm));
167
168 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
169 unsigned int n;
170
171 for (n = 0; n < count; n++)
172 idm_assert(idm, test_bit(start + n, idm->bitmap));
173 }
174 bitmap_clear(idm->bitmap, start, count);
175 idm->used -= count;
176 }
177
178 /**
179 * xe_guc_id_mgr_reserve_locked() - Reserve one or more GuC context IDs.
180 * @idm: the &xe_guc_id_mgr
181 * @count: number of IDs to allocate (can't be 0)
182 *
183 * This function is dedicated for the use by the GuC submission code,
184 * where submission lock is already taken.
185 *
186 * Return: ID of allocated GuC context or a negative error code on failure.
187 */
xe_guc_id_mgr_reserve_locked(struct xe_guc_id_mgr * idm,unsigned int count)188 int xe_guc_id_mgr_reserve_locked(struct xe_guc_id_mgr *idm, unsigned int count)
189 {
190 return idm_reserve_chunk_locked(idm, count, 0);
191 }
192
193 /**
194 * xe_guc_id_mgr_release_locked() - Release one or more GuC context IDs.
195 * @idm: the &xe_guc_id_mgr
196 * @id: the GuC context ID to release
197 * @count: number of IDs to release (can't be 0)
198 *
199 * This function is dedicated for the use by the GuC submission code,
200 * where submission lock is already taken.
201 */
xe_guc_id_mgr_release_locked(struct xe_guc_id_mgr * idm,unsigned int id,unsigned int count)202 void xe_guc_id_mgr_release_locked(struct xe_guc_id_mgr *idm, unsigned int id,
203 unsigned int count)
204 {
205 return idm_release_chunk_locked(idm, id, count);
206 }
207
208 /**
209 * xe_guc_id_mgr_reserve() - Reserve a range of GuC context IDs.
210 * @idm: the &xe_guc_id_mgr
211 * @count: number of GuC context IDs to reserve (can't be 0)
212 * @retain: number of GuC context IDs to keep available (can't be 0)
213 *
214 * This function is dedicated for the use by the PF driver which expects that
215 * reserved range of IDs will be contiguous and that there will be at least
216 * &retain IDs still available for the PF after this reservation.
217 *
218 * Return: starting ID of the allocated GuC context ID range or
219 * a negative error code on failure.
220 */
xe_guc_id_mgr_reserve(struct xe_guc_id_mgr * idm,unsigned int count,unsigned int retain)221 int xe_guc_id_mgr_reserve(struct xe_guc_id_mgr *idm,
222 unsigned int count, unsigned int retain)
223 {
224 int ret;
225
226 idm_assert(idm, count);
227 idm_assert(idm, retain);
228
229 mutex_lock(idm_mutex(idm));
230 ret = idm_reserve_chunk_locked(idm, count, retain);
231 mutex_unlock(idm_mutex(idm));
232
233 return ret;
234 }
235
236 /**
237 * xe_guc_id_mgr_release() - Release a range of GuC context IDs.
238 * @idm: the &xe_guc_id_mgr
239 * @start: the starting ID of GuC context range to release
240 * @count: number of GuC context IDs to release
241 */
xe_guc_id_mgr_release(struct xe_guc_id_mgr * idm,unsigned int start,unsigned int count)242 void xe_guc_id_mgr_release(struct xe_guc_id_mgr *idm,
243 unsigned int start, unsigned int count)
244 {
245 mutex_lock(idm_mutex(idm));
246 idm_release_chunk_locked(idm, start, count);
247 mutex_unlock(idm_mutex(idm));
248 }
249
idm_print_locked(struct xe_guc_id_mgr * idm,struct drm_printer * p,int indent)250 static void idm_print_locked(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent)
251 {
252 unsigned int rs, re;
253
254 lockdep_assert_held(idm_mutex(idm));
255
256 drm_printf_indent(p, indent, "total %u\n", idm->total);
257 if (!idm->bitmap)
258 return;
259
260 drm_printf_indent(p, indent, "used %u\n", idm->used);
261 for_each_set_bitrange(rs, re, idm->bitmap, idm->total)
262 drm_printf_indent(p, indent, "range %u..%u (%u)\n", rs, re - 1, re - rs);
263 }
264
265 /**
266 * xe_guc_id_mgr_print() - Print status of GuC ID Manager.
267 * @idm: the &xe_guc_id_mgr to print
268 * @p: the &drm_printer to print to
269 * @indent: tab indentation level
270 */
xe_guc_id_mgr_print(struct xe_guc_id_mgr * idm,struct drm_printer * p,int indent)271 void xe_guc_id_mgr_print(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent)
272 {
273 mutex_lock(idm_mutex(idm));
274 idm_print_locked(idm, p, indent);
275 mutex_unlock(idm_mutex(idm));
276 }
277
278 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
279 #include "tests/xe_guc_id_mgr_test.c"
280 #endif
281