xref: /linux/drivers/gpu/drm/xe/xe_guc_db_mgr.c (revision e6a901a00822659181c93c86d8bbc2a17779fddc)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023 Intel Corporation
4  */
5 
6 #include <linux/bitmap.h>
7 #include <linux/mutex.h>
8 
9 #include <drm/drm_managed.h>
10 
11 #include "regs/xe_guc_regs.h"
12 
13 #include "xe_assert.h"
14 #include "xe_gt_printk.h"
15 #include "xe_guc.h"
16 #include "xe_guc_db_mgr.h"
17 #include "xe_guc_types.h"
18 
19 /**
20  * DOC: GuC Doorbells
21  *
22  * The GFX doorbell solution provides a mechanism for submission of workload
23  * to the graphics hardware by a ring3 application without the penalty of
24  * ring transition for each workload submission.
25  *
26  * In SR-IOV mode, the doorbells are treated as shared resource and PF must
27  * be able to provision exclusive range of IDs across VFs, which may want to
28  * use this feature.
29  */
30 
31 static struct xe_guc *dbm_to_guc(struct xe_guc_db_mgr *dbm)
32 {
33 	return container_of(dbm, struct xe_guc, dbm);
34 }
35 
36 static struct xe_gt *dbm_to_gt(struct xe_guc_db_mgr *dbm)
37 {
38 	return guc_to_gt(dbm_to_guc(dbm));
39 }
40 
41 static struct xe_device *dbm_to_xe(struct xe_guc_db_mgr *dbm)
42 {
43 	return gt_to_xe(dbm_to_gt(dbm));
44 }
45 
46 #define dbm_assert(_dbm, _cond)		xe_gt_assert(dbm_to_gt(_dbm), _cond)
47 #define dbm_mutex(_dbm)			(&dbm_to_guc(_dbm)->submission_state.lock)
48 
49 static void dbm_print_locked(struct xe_guc_db_mgr *dbm, struct drm_printer *p, int indent);
50 
51 static void __fini_dbm(struct drm_device *drm, void *arg)
52 {
53 	struct xe_guc_db_mgr *dbm = arg;
54 	unsigned int weight;
55 
56 	mutex_lock(dbm_mutex(dbm));
57 
58 	weight = bitmap_weight(dbm->bitmap, dbm->count);
59 	if (weight) {
60 		struct drm_printer p = xe_gt_info_printer(dbm_to_gt(dbm));
61 
62 		xe_gt_err(dbm_to_gt(dbm), "GuC doorbells manager unclean (%u/%u)\n",
63 			  weight, dbm->count);
64 		dbm_print_locked(dbm, &p, 1);
65 	}
66 
67 	bitmap_free(dbm->bitmap);
68 	dbm->bitmap = NULL;
69 	dbm->count = 0;
70 
71 	mutex_unlock(dbm_mutex(dbm));
72 }
73 
74 /**
75  * xe_guc_db_mgr_init() - Initialize GuC Doorbells Manager.
76  * @dbm: the &xe_guc_db_mgr to initialize
77  * @count: number of doorbells to manage
78  *
79  * The bare-metal or PF driver can pass ~0 as &count to indicate that all
80  * doorbells supported by the hardware are available for use.
81  *
82  * Only VF's drivers will have to provide explicit number of doorbells IDs
83  * that they can use.
84  *
85  * Return: 0 on success or a negative error code on failure.
86  */
87 int xe_guc_db_mgr_init(struct xe_guc_db_mgr *dbm, unsigned int count)
88 {
89 	int ret;
90 
91 	if (count == ~0)
92 		count = GUC_NUM_DOORBELLS;
93 
94 	dbm_assert(dbm, !dbm->bitmap);
95 	dbm_assert(dbm, count <= GUC_NUM_DOORBELLS);
96 
97 	if (!count)
98 		goto done;
99 
100 	dbm->bitmap = bitmap_zalloc(count, GFP_KERNEL);
101 	if (!dbm->bitmap)
102 		return -ENOMEM;
103 	dbm->count = count;
104 
105 	ret = drmm_add_action_or_reset(&dbm_to_xe(dbm)->drm, __fini_dbm, dbm);
106 	if (ret)
107 		return ret;
108 done:
109 	xe_gt_dbg(dbm_to_gt(dbm), "using %u doorbell(s)\n", dbm->count);
110 	return 0;
111 }
112 
113 static int dbm_reserve_chunk_locked(struct xe_guc_db_mgr *dbm,
114 				    unsigned int count, unsigned int spare)
115 {
116 	unsigned int used;
117 	int index;
118 
119 	dbm_assert(dbm, count);
120 	dbm_assert(dbm, count <= GUC_NUM_DOORBELLS);
121 	dbm_assert(dbm, dbm->count <= GUC_NUM_DOORBELLS);
122 	lockdep_assert_held(dbm_mutex(dbm));
123 
124 	if (!dbm->count)
125 		return -ENODATA;
126 
127 	if (spare) {
128 		used = bitmap_weight(dbm->bitmap, dbm->count);
129 		if (used + count + spare > dbm->count)
130 			return -EDQUOT;
131 	}
132 
133 	index = bitmap_find_next_zero_area(dbm->bitmap, dbm->count, 0, count, 0);
134 	if (index >= dbm->count)
135 		return -ENOSPC;
136 
137 	bitmap_set(dbm->bitmap, index, count);
138 
139 	return index;
140 }
141 
142 static void dbm_release_chunk_locked(struct xe_guc_db_mgr *dbm,
143 				     unsigned int start, unsigned int count)
144 {
145 	dbm_assert(dbm, count);
146 	dbm_assert(dbm, count <= GUC_NUM_DOORBELLS);
147 	dbm_assert(dbm, dbm->count);
148 	dbm_assert(dbm, dbm->count <= GUC_NUM_DOORBELLS);
149 	lockdep_assert_held(dbm_mutex(dbm));
150 
151 	if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
152 		unsigned int n;
153 
154 		for (n = 0; n < count; n++)
155 			dbm_assert(dbm, test_bit(start + n, dbm->bitmap));
156 	}
157 	bitmap_clear(dbm->bitmap, start, count);
158 }
159 
160 /**
161  * xe_guc_db_mgr_reserve_id_locked() - Reserve a single GuC Doorbell ID.
162  * @dbm: the &xe_guc_db_mgr
163  *
164  * This function expects that submission lock is already taken.
165  *
166  * Return: ID of the allocated GuC doorbell or a negative error code on failure.
167  */
168 int xe_guc_db_mgr_reserve_id_locked(struct xe_guc_db_mgr *dbm)
169 {
170 	return dbm_reserve_chunk_locked(dbm, 1, 0);
171 }
172 
173 /**
174  * xe_guc_db_mgr_release_id_locked() - Release a single GuC Doorbell ID.
175  * @dbm: the &xe_guc_db_mgr
176  * @id: the GuC Doorbell ID to release
177  *
178  * This function expects that submission lock is already taken.
179  */
180 void xe_guc_db_mgr_release_id_locked(struct xe_guc_db_mgr *dbm, unsigned int id)
181 {
182 	return dbm_release_chunk_locked(dbm, id, 1);
183 }
184 
185 /**
186  * xe_guc_db_mgr_reserve_range() - Reserve a range of GuC Doorbell IDs.
187  * @dbm: the &xe_guc_db_mgr
188  * @count: number of GuC doorbell IDs to reserve
189  * @spare: number of GuC doorbell IDs to keep available
190  *
191  * This function is dedicated for the for use by the PF which expects that
192  * allocated range for the VF will be contiguous and that there will be at
193  * least &spare IDs still available for the PF use after this reservation.
194  *
195  * Return: starting ID of the allocated GuC doorbell ID range or
196  *         a negative error code on failure.
197  */
198 int xe_guc_db_mgr_reserve_range(struct xe_guc_db_mgr *dbm,
199 				unsigned int count, unsigned int spare)
200 {
201 	int ret;
202 
203 	mutex_lock(dbm_mutex(dbm));
204 	ret = dbm_reserve_chunk_locked(dbm, count, spare);
205 	mutex_unlock(dbm_mutex(dbm));
206 
207 	return ret;
208 }
209 
210 /**
211  * xe_guc_db_mgr_release_range() - Release a range of Doorbell IDs.
212  * @dbm: the &xe_guc_db_mgr
213  * @start: the starting ID of GuC doorbell ID range to release
214  * @count: number of GuC doorbell IDs to release
215  */
216 void xe_guc_db_mgr_release_range(struct xe_guc_db_mgr *dbm,
217 				 unsigned int start, unsigned int count)
218 {
219 	mutex_lock(dbm_mutex(dbm));
220 	dbm_release_chunk_locked(dbm, start, count);
221 	mutex_unlock(dbm_mutex(dbm));
222 }
223 
224 static void dbm_print_locked(struct xe_guc_db_mgr *dbm, struct drm_printer *p, int indent)
225 {
226 	unsigned int rs, re;
227 	unsigned int total;
228 
229 	drm_printf_indent(p, indent, "count: %u\n", dbm->count);
230 	if (!dbm->bitmap)
231 		return;
232 
233 	total = 0;
234 	for_each_clear_bitrange(rs, re, dbm->bitmap, dbm->count) {
235 		drm_printf_indent(p, indent, "available range: %u..%u (%u)\n",
236 				  rs, re - 1, re - rs);
237 		total += re - rs;
238 	}
239 	drm_printf_indent(p, indent, "available total: %u\n", total);
240 
241 	total = 0;
242 	for_each_set_bitrange(rs, re, dbm->bitmap, dbm->count) {
243 		drm_printf_indent(p, indent, "reserved range: %u..%u (%u)\n",
244 				  rs, re - 1, re - rs);
245 		total += re - rs;
246 	}
247 	drm_printf_indent(p, indent, "reserved total: %u\n", total);
248 }
249 
250 /**
251  * xe_guc_db_mgr_print() - Print status of GuC Doorbells Manager.
252  * @dbm: the &xe_guc_db_mgr to print
253  * @p: the &drm_printer to print to
254  * @indent: tab indentation level
255  */
256 void xe_guc_db_mgr_print(struct xe_guc_db_mgr *dbm,
257 			 struct drm_printer *p, int indent)
258 {
259 	mutex_lock(dbm_mutex(dbm));
260 	dbm_print_locked(dbm, p, indent);
261 	mutex_unlock(dbm_mutex(dbm));
262 }
263 
264 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
265 #include "tests/xe_guc_db_mgr_test.c"
266 #endif
267