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
dbm_to_guc(struct xe_guc_db_mgr * dbm)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
dbm_to_gt(struct xe_guc_db_mgr * dbm)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
dbm_to_xe(struct xe_guc_db_mgr * dbm)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
__fini_dbm(struct drm_device * drm,void * arg)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 */
xe_guc_db_mgr_init(struct xe_guc_db_mgr * dbm,unsigned int count)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",
110 dbm->count, str_plural(dbm->count));
111 return 0;
112 }
113
dbm_reserve_chunk_locked(struct xe_guc_db_mgr * dbm,unsigned int count,unsigned int spare)114 static int dbm_reserve_chunk_locked(struct xe_guc_db_mgr *dbm,
115 unsigned int count, unsigned int spare)
116 {
117 unsigned int used;
118 int index;
119
120 dbm_assert(dbm, count);
121 dbm_assert(dbm, count <= GUC_NUM_DOORBELLS);
122 dbm_assert(dbm, dbm->count <= GUC_NUM_DOORBELLS);
123 lockdep_assert_held(dbm_mutex(dbm));
124
125 if (!dbm->count)
126 return -ENODATA;
127
128 if (spare) {
129 used = bitmap_weight(dbm->bitmap, dbm->count);
130 if (used + count + spare > dbm->count)
131 return -EDQUOT;
132 }
133
134 index = bitmap_find_next_zero_area(dbm->bitmap, dbm->count, 0, count, 0);
135 if (index >= dbm->count)
136 return -ENOSPC;
137
138 bitmap_set(dbm->bitmap, index, count);
139
140 return index;
141 }
142
dbm_release_chunk_locked(struct xe_guc_db_mgr * dbm,unsigned int start,unsigned int count)143 static void dbm_release_chunk_locked(struct xe_guc_db_mgr *dbm,
144 unsigned int start, unsigned int count)
145 {
146 dbm_assert(dbm, count);
147 dbm_assert(dbm, count <= GUC_NUM_DOORBELLS);
148 dbm_assert(dbm, dbm->count);
149 dbm_assert(dbm, dbm->count <= GUC_NUM_DOORBELLS);
150 lockdep_assert_held(dbm_mutex(dbm));
151
152 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
153 unsigned int n;
154
155 for (n = 0; n < count; n++)
156 dbm_assert(dbm, test_bit(start + n, dbm->bitmap));
157 }
158 bitmap_clear(dbm->bitmap, start, count);
159 }
160
161 /**
162 * xe_guc_db_mgr_reserve_id_locked() - Reserve a single GuC Doorbell ID.
163 * @dbm: the &xe_guc_db_mgr
164 *
165 * This function expects that submission lock is already taken.
166 *
167 * Return: ID of the allocated GuC doorbell or a negative error code on failure.
168 */
xe_guc_db_mgr_reserve_id_locked(struct xe_guc_db_mgr * dbm)169 int xe_guc_db_mgr_reserve_id_locked(struct xe_guc_db_mgr *dbm)
170 {
171 return dbm_reserve_chunk_locked(dbm, 1, 0);
172 }
173
174 /**
175 * xe_guc_db_mgr_release_id_locked() - Release a single GuC Doorbell ID.
176 * @dbm: the &xe_guc_db_mgr
177 * @id: the GuC Doorbell ID to release
178 *
179 * This function expects that submission lock is already taken.
180 */
xe_guc_db_mgr_release_id_locked(struct xe_guc_db_mgr * dbm,unsigned int id)181 void xe_guc_db_mgr_release_id_locked(struct xe_guc_db_mgr *dbm, unsigned int id)
182 {
183 return dbm_release_chunk_locked(dbm, id, 1);
184 }
185
186 /**
187 * xe_guc_db_mgr_reserve_range() - Reserve a range of GuC Doorbell IDs.
188 * @dbm: the &xe_guc_db_mgr
189 * @count: number of GuC doorbell IDs to reserve
190 * @spare: number of GuC doorbell IDs to keep available
191 *
192 * This function is dedicated for the for use by the PF which expects that
193 * allocated range for the VF will be contiguous and that there will be at
194 * least &spare IDs still available for the PF use after this reservation.
195 *
196 * Return: starting ID of the allocated GuC doorbell ID range or
197 * a negative error code on failure.
198 */
xe_guc_db_mgr_reserve_range(struct xe_guc_db_mgr * dbm,unsigned int count,unsigned int spare)199 int xe_guc_db_mgr_reserve_range(struct xe_guc_db_mgr *dbm,
200 unsigned int count, unsigned int spare)
201 {
202 int ret;
203
204 mutex_lock(dbm_mutex(dbm));
205 ret = dbm_reserve_chunk_locked(dbm, count, spare);
206 mutex_unlock(dbm_mutex(dbm));
207
208 return ret;
209 }
210
211 /**
212 * xe_guc_db_mgr_release_range() - Release a range of Doorbell IDs.
213 * @dbm: the &xe_guc_db_mgr
214 * @start: the starting ID of GuC doorbell ID range to release
215 * @count: number of GuC doorbell IDs to release
216 */
xe_guc_db_mgr_release_range(struct xe_guc_db_mgr * dbm,unsigned int start,unsigned int count)217 void xe_guc_db_mgr_release_range(struct xe_guc_db_mgr *dbm,
218 unsigned int start, unsigned int count)
219 {
220 mutex_lock(dbm_mutex(dbm));
221 dbm_release_chunk_locked(dbm, start, count);
222 mutex_unlock(dbm_mutex(dbm));
223 }
224
dbm_print_locked(struct xe_guc_db_mgr * dbm,struct drm_printer * p,int indent)225 static void dbm_print_locked(struct xe_guc_db_mgr *dbm, struct drm_printer *p, int indent)
226 {
227 unsigned int rs, re;
228 unsigned int total;
229
230 drm_printf_indent(p, indent, "count: %u\n", dbm->count);
231 if (!dbm->bitmap)
232 return;
233
234 total = 0;
235 for_each_clear_bitrange(rs, re, dbm->bitmap, dbm->count) {
236 drm_printf_indent(p, indent, "available range: %u..%u (%u)\n",
237 rs, re - 1, re - rs);
238 total += re - rs;
239 }
240 drm_printf_indent(p, indent, "available total: %u\n", total);
241
242 total = 0;
243 for_each_set_bitrange(rs, re, dbm->bitmap, dbm->count) {
244 drm_printf_indent(p, indent, "reserved range: %u..%u (%u)\n",
245 rs, re - 1, re - rs);
246 total += re - rs;
247 }
248 drm_printf_indent(p, indent, "reserved total: %u\n", total);
249 }
250
251 /**
252 * xe_guc_db_mgr_print() - Print status of GuC Doorbells Manager.
253 * @dbm: the &xe_guc_db_mgr to print
254 * @p: the &drm_printer to print to
255 * @indent: tab indentation level
256 */
xe_guc_db_mgr_print(struct xe_guc_db_mgr * dbm,struct drm_printer * p,int indent)257 void xe_guc_db_mgr_print(struct xe_guc_db_mgr *dbm,
258 struct drm_printer *p, int indent)
259 {
260 mutex_lock(dbm_mutex(dbm));
261 dbm_print_locked(dbm, p, indent);
262 mutex_unlock(dbm_mutex(dbm));
263 }
264
265 #if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
266 #include "tests/xe_guc_db_mgr_test.c"
267 #endif
268