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
17static 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
22static struct xe_gt *idm_to_gt(struct xe_guc_id_mgr *idm)
23{
24	return guc_to_gt(idm_to_guc(idm));
25}
26
27static 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
35static void idm_print_locked(struct xe_guc_id_mgr *idm, struct drm_printer *p, int indent);
36
37static 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 */
76int 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
104static 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
120static 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
158static 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 */
187int 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 */
201void 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 */
220int 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 */
241void 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
249static 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 */
270void 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