xref: /linux/drivers/gpu/drm/xe/xe_guc_id_mgr.c (revision 40ccd6aa3e2e05be93394e3cd560c718dedfcc77)
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 
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 
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 
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 
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  */
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_info(idm_to_gt(idm), "using %u GUC ID(s)\n", idm->total);
101 	return 0;
102 }
103 
104 static unsigned int find_last_zero_area(unsigned long *bitmap,
105 					unsigned int total,
106 					unsigned int count)
107 {
108 	unsigned int found = total;
109 	unsigned int rs, re, range;
110 
111 	for_each_clear_bitrange(rs, re, bitmap, total) {
112 		range = re - rs;
113 		if (range < count)
114 			continue;
115 		found = rs + (range - count);
116 	}
117 	return found;
118 }
119 
120 static int idm_reserve_chunk_locked(struct xe_guc_id_mgr *idm,
121 				    unsigned int count, unsigned int retain)
122 {
123 	int id;
124 
125 	idm_assert(idm, count);
126 	lockdep_assert_held(idm_mutex(idm));
127 
128 	if (!idm->total)
129 		return -ENODATA;
130 
131 	if (retain) {
132 		/*
133 		 * For IDs reservations (used on PF for VFs) we want to make
134 		 * sure there will be at least 'retain' available for the PF
135 		 */
136 		if (idm->used + count + retain > idm->total)
137 			return -EDQUOT;
138 		/*
139 		 * ... and we want to reserve highest IDs close to the end.
140 		 */
141 		id = find_last_zero_area(idm->bitmap, idm->total, count);
142 	} else {
143 		/*
144 		 * For regular IDs reservations (used by submission code)
145 		 * we start searching from the lower range of IDs.
146 		 */
147 		id = bitmap_find_next_zero_area(idm->bitmap, idm->total, 0, count, 0);
148 	}
149 	if (id >= idm->total)
150 		return -ENOSPC;
151 
152 	bitmap_set(idm->bitmap, id, count);
153 	idm->used += count;
154 
155 	return id;
156 }
157 
158 static void idm_release_chunk_locked(struct xe_guc_id_mgr *idm,
159 				     unsigned int start, unsigned int count)
160 {
161 	idm_assert(idm, count);
162 	idm_assert(idm, count <= idm->used);
163 	idm_assert(idm, start < idm->total);
164 	idm_assert(idm, start + count - 1 < idm->total);
165 	lockdep_assert_held(idm_mutex(idm));
166 
167 	if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
168 		unsigned int n;
169 
170 		for (n = 0; n < count; n++)
171 			idm_assert(idm, test_bit(start + n, idm->bitmap));
172 	}
173 	bitmap_clear(idm->bitmap, start, count);
174 	idm->used -= count;
175 }
176 
177 /**
178  * xe_guc_id_mgr_reserve_locked() - Reserve one or more GuC context IDs.
179  * @idm: the &xe_guc_id_mgr
180  * @count: number of IDs to allocate (can't be 0)
181  *
182  * This function is dedicated for the use by the GuC submission code,
183  * where submission lock is already taken.
184  *
185  * Return: ID of allocated GuC context or a negative error code on failure.
186  */
187 int xe_guc_id_mgr_reserve_locked(struct xe_guc_id_mgr *idm, unsigned int count)
188 {
189 	return idm_reserve_chunk_locked(idm, count, 0);
190 }
191 
192 /**
193  * xe_guc_id_mgr_release_locked() - Release one or more GuC context IDs.
194  * @idm: the &xe_guc_id_mgr
195  * @id: the GuC context ID to release
196  * @count: number of IDs to release (can't be 0)
197  *
198  * This function is dedicated for the use by the GuC submission code,
199  * where submission lock is already taken.
200  */
201 void xe_guc_id_mgr_release_locked(struct xe_guc_id_mgr *idm, unsigned int id,
202 				  unsigned int count)
203 {
204 	return idm_release_chunk_locked(idm, id, count);
205 }
206 
207 /**
208  * xe_guc_id_mgr_reserve() - Reserve a range of GuC context IDs.
209  * @idm: the &xe_guc_id_mgr
210  * @count: number of GuC context IDs to reserve (can't be 0)
211  * @retain: number of GuC context IDs to keep available (can't be 0)
212  *
213  * This function is dedicated for the use by the PF driver which expects that
214  * reserved range of IDs will be contiguous and that there will be at least
215  * &retain IDs still available for the PF after this reservation.
216  *
217  * Return: starting ID of the allocated GuC context ID range or
218  *         a negative error code on failure.
219  */
220 int xe_guc_id_mgr_reserve(struct xe_guc_id_mgr *idm,
221 			  unsigned int count, unsigned int retain)
222 {
223 	int ret;
224 
225 	idm_assert(idm, count);
226 	idm_assert(idm, retain);
227 
228 	mutex_lock(idm_mutex(idm));
229 	ret = idm_reserve_chunk_locked(idm, count, retain);
230 	mutex_unlock(idm_mutex(idm));
231 
232 	return ret;
233 }
234 
235 /**
236  * xe_guc_id_mgr_release() - Release a range of GuC context IDs.
237  * @idm: the &xe_guc_id_mgr
238  * @start: the starting ID of GuC context range to release
239  * @count: number of GuC context IDs to release
240  */
241 void xe_guc_id_mgr_release(struct xe_guc_id_mgr *idm,
242 			   unsigned int start, unsigned int count)
243 {
244 	mutex_lock(idm_mutex(idm));
245 	idm_release_chunk_locked(idm, start, count);
246 	mutex_unlock(idm_mutex(idm));
247 }
248 
249 static void idm_print_locked(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent)
250 {
251 	unsigned int rs, re;
252 
253 	lockdep_assert_held(idm_mutex(idm));
254 
255 	drm_printf_indent(p, indent, "total %u\n", idm->total);
256 	if (!idm->bitmap)
257 		return;
258 
259 	drm_printf_indent(p, indent, "used %u\n", idm->used);
260 	for_each_set_bitrange(rs, re, idm->bitmap, idm->total)
261 		drm_printf_indent(p, indent, "range %u..%u (%u)\n", rs, re - 1, re - rs);
262 }
263 
264 /**
265  * xe_guc_id_mgr_print() - Print status of GuC ID Manager.
266  * @idm: the &xe_guc_id_mgr to print
267  * @p: the &drm_printer to print to
268  * @indent: tab indentation level
269  */
270 void xe_guc_id_mgr_print(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent)
271 {
272 	mutex_lock(idm_mutex(idm));
273 	idm_print_locked(idm, p, indent);
274 	mutex_unlock(idm_mutex(idm));
275 }
276 
277 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
278 #include "tests/xe_guc_id_mgr_test.c"
279 #endif
280