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