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", 110 dbm->count, str_plural(dbm->count)); 111 return 0; 112 } 113 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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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