Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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
 31static struct xe_guc *dbm_to_guc(struct xe_guc_db_mgr *dbm)
 32{
 33	return container_of(dbm, struct xe_guc, dbm);
 34}
 35
 36static struct xe_gt *dbm_to_gt(struct xe_guc_db_mgr *dbm)
 37{
 38	return guc_to_gt(dbm_to_guc(dbm));
 39}
 40
 41static 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
 49static void dbm_print_locked(struct xe_guc_db_mgr *dbm, struct drm_printer *p, int indent);
 50
 51static 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 */
 87int 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;
108done:
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
114static 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
143static 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 */
169int 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 */
181void 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 */
199int 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 */
217void 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
225static 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 */
257void 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