| 1 | // SPDX-License-Identifier: MIT |
| 2 | /* |
| 3 | * Copyright © 2019 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #include <linux/list.h> |
| 7 | #include <linux/list_sort.h> |
| 8 | #include <linux/llist.h> |
| 9 | |
| 10 | #include <drm/drm_print.h> |
| 11 | |
| 12 | #include "i915_drv.h" |
| 13 | #include "intel_engine.h" |
| 14 | #include "intel_engine_user.h" |
| 15 | #include "intel_gt.h" |
| 16 | #include "uc/intel_guc_submission.h" |
| 17 | |
| 18 | struct intel_engine_cs * |
| 19 | intel_engine_lookup_user(struct drm_i915_private *i915, u8 class, u8 instance) |
| 20 | { |
| 21 | struct rb_node *p = i915->uabi_engines.rb_node; |
| 22 | |
| 23 | while (p) { |
| 24 | struct intel_engine_cs *it = |
| 25 | rb_entry(p, typeof(*it), uabi_node); |
| 26 | |
| 27 | if (class < it->uabi_class) |
| 28 | p = p->rb_left; |
| 29 | else if (class > it->uabi_class || |
| 30 | instance > it->uabi_instance) |
| 31 | p = p->rb_right; |
| 32 | else if (instance < it->uabi_instance) |
| 33 | p = p->rb_left; |
| 34 | else |
| 35 | return it; |
| 36 | } |
| 37 | |
| 38 | return NULL; |
| 39 | } |
| 40 | |
| 41 | void intel_engine_add_user(struct intel_engine_cs *engine) |
| 42 | { |
| 43 | llist_add(new: &engine->uabi_llist, head: &engine->i915->uabi_engines_llist); |
| 44 | } |
| 45 | |
| 46 | #define I915_NO_UABI_CLASS ((u16)(-1)) |
| 47 | |
| 48 | static const u16 uabi_classes[] = { |
| 49 | [RENDER_CLASS] = I915_ENGINE_CLASS_RENDER, |
| 50 | [COPY_ENGINE_CLASS] = I915_ENGINE_CLASS_COPY, |
| 51 | [VIDEO_DECODE_CLASS] = I915_ENGINE_CLASS_VIDEO, |
| 52 | [VIDEO_ENHANCEMENT_CLASS] = I915_ENGINE_CLASS_VIDEO_ENHANCE, |
| 53 | [COMPUTE_CLASS] = I915_ENGINE_CLASS_COMPUTE, |
| 54 | [OTHER_CLASS] = I915_NO_UABI_CLASS, /* Not exposed to users, no uabi class. */ |
| 55 | }; |
| 56 | |
| 57 | static int engine_cmp(void *priv, const struct list_head *A, |
| 58 | const struct list_head *B) |
| 59 | { |
| 60 | const struct intel_engine_cs *a = |
| 61 | container_of(A, typeof(*a), uabi_list); |
| 62 | const struct intel_engine_cs *b = |
| 63 | container_of(B, typeof(*b), uabi_list); |
| 64 | |
| 65 | if (uabi_classes[a->class] < uabi_classes[b->class]) |
| 66 | return -1; |
| 67 | if (uabi_classes[a->class] > uabi_classes[b->class]) |
| 68 | return 1; |
| 69 | |
| 70 | if (a->instance < b->instance) |
| 71 | return -1; |
| 72 | if (a->instance > b->instance) |
| 73 | return 1; |
| 74 | |
| 75 | return 0; |
| 76 | } |
| 77 | |
| 78 | static struct llist_node *get_engines(struct drm_i915_private *i915) |
| 79 | { |
| 80 | return llist_del_all(head: &i915->uabi_engines_llist); |
| 81 | } |
| 82 | |
| 83 | static void sort_engines(struct drm_i915_private *i915, |
| 84 | struct list_head *engines) |
| 85 | { |
| 86 | struct llist_node *pos, *next; |
| 87 | |
| 88 | llist_for_each_safe(pos, next, get_engines(i915)) { |
| 89 | struct intel_engine_cs *engine = |
| 90 | container_of(pos, typeof(*engine), uabi_llist); |
| 91 | list_add(new: &engine->uabi_list, head: engines); |
| 92 | } |
| 93 | list_sort(NULL, head: engines, cmp: engine_cmp); |
| 94 | } |
| 95 | |
| 96 | static void set_scheduler_caps(struct drm_i915_private *i915) |
| 97 | { |
| 98 | static const struct { |
| 99 | u8 engine; |
| 100 | u8 sched; |
| 101 | } map[] = { |
| 102 | #define MAP(x, y) { ilog2(I915_ENGINE_##x), ilog2(I915_SCHEDULER_CAP_##y) } |
| 103 | MAP(HAS_PREEMPTION, PREEMPTION), |
| 104 | MAP(HAS_SEMAPHORES, SEMAPHORES), |
| 105 | MAP(SUPPORTS_STATS, ENGINE_BUSY_STATS), |
| 106 | #undef MAP |
| 107 | }; |
| 108 | struct intel_engine_cs *engine; |
| 109 | u32 enabled, disabled; |
| 110 | |
| 111 | enabled = 0; |
| 112 | disabled = 0; |
| 113 | for_each_uabi_engine(engine, i915) { /* all engines must agree! */ |
| 114 | int i; |
| 115 | |
| 116 | if (engine->sched_engine->schedule) |
| 117 | enabled |= (I915_SCHEDULER_CAP_ENABLED | |
| 118 | I915_SCHEDULER_CAP_PRIORITY); |
| 119 | else |
| 120 | disabled |= (I915_SCHEDULER_CAP_ENABLED | |
| 121 | I915_SCHEDULER_CAP_PRIORITY); |
| 122 | |
| 123 | if (intel_uc_uses_guc_submission(uc: &engine->gt->uc)) |
| 124 | enabled |= I915_SCHEDULER_CAP_STATIC_PRIORITY_MAP; |
| 125 | |
| 126 | for (i = 0; i < ARRAY_SIZE(map); i++) { |
| 127 | if (engine->flags & BIT(map[i].engine)) |
| 128 | enabled |= BIT(map[i].sched); |
| 129 | else |
| 130 | disabled |= BIT(map[i].sched); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | i915->caps.scheduler = enabled & ~disabled; |
| 135 | if (!(i915->caps.scheduler & I915_SCHEDULER_CAP_ENABLED)) |
| 136 | i915->caps.scheduler = 0; |
| 137 | } |
| 138 | |
| 139 | const char *intel_engine_class_repr(u8 class) |
| 140 | { |
| 141 | static const char * const uabi_names[] = { |
| 142 | [RENDER_CLASS] = "rcs" , |
| 143 | [COPY_ENGINE_CLASS] = "bcs" , |
| 144 | [VIDEO_DECODE_CLASS] = "vcs" , |
| 145 | [VIDEO_ENHANCEMENT_CLASS] = "vecs" , |
| 146 | [OTHER_CLASS] = "other" , |
| 147 | [COMPUTE_CLASS] = "ccs" , |
| 148 | }; |
| 149 | |
| 150 | if (class >= ARRAY_SIZE(uabi_names) || !uabi_names[class]) |
| 151 | return "xxx" ; |
| 152 | |
| 153 | return uabi_names[class]; |
| 154 | } |
| 155 | |
| 156 | struct legacy_ring { |
| 157 | struct intel_gt *gt; |
| 158 | u8 class; |
| 159 | u8 instance; |
| 160 | }; |
| 161 | |
| 162 | static int legacy_ring_idx(const struct legacy_ring *ring) |
| 163 | { |
| 164 | static const struct { |
| 165 | u8 base, max; |
| 166 | } map[] = { |
| 167 | [RENDER_CLASS] = { RCS0, 1 }, |
| 168 | [COPY_ENGINE_CLASS] = { BCS0, 1 }, |
| 169 | [VIDEO_DECODE_CLASS] = { VCS0, I915_MAX_VCS }, |
| 170 | [VIDEO_ENHANCEMENT_CLASS] = { VECS0, I915_MAX_VECS }, |
| 171 | [COMPUTE_CLASS] = { CCS0, I915_MAX_CCS }, |
| 172 | }; |
| 173 | |
| 174 | if (GEM_DEBUG_WARN_ON(ring->class >= ARRAY_SIZE(map))) |
| 175 | return INVALID_ENGINE; |
| 176 | |
| 177 | if (GEM_DEBUG_WARN_ON(ring->instance >= map[ring->class].max)) |
| 178 | return INVALID_ENGINE; |
| 179 | |
| 180 | return map[ring->class].base + ring->instance; |
| 181 | } |
| 182 | |
| 183 | static void add_legacy_ring(struct legacy_ring *ring, |
| 184 | struct intel_engine_cs *engine) |
| 185 | { |
| 186 | if (engine->gt != ring->gt || engine->class != ring->class) { |
| 187 | ring->gt = engine->gt; |
| 188 | ring->class = engine->class; |
| 189 | ring->instance = 0; |
| 190 | } |
| 191 | |
| 192 | engine->legacy_idx = legacy_ring_idx(ring); |
| 193 | if (engine->legacy_idx != INVALID_ENGINE) |
| 194 | ring->instance++; |
| 195 | } |
| 196 | |
| 197 | static void engine_rename(struct intel_engine_cs *engine, const char *name, u16 instance) |
| 198 | { |
| 199 | char old[sizeof(engine->name)]; |
| 200 | |
| 201 | memcpy(old, engine->name, sizeof(engine->name)); |
| 202 | scnprintf(buf: engine->name, size: sizeof(engine->name), fmt: "%s%u" , name, instance); |
| 203 | drm_dbg(&engine->i915->drm, "renamed %s to %s\n" , old, engine->name); |
| 204 | } |
| 205 | |
| 206 | void intel_engines_driver_register(struct drm_i915_private *i915) |
| 207 | { |
| 208 | u16 name_instance, other_instance = 0; |
| 209 | struct legacy_ring ring = {}; |
| 210 | struct list_head *it, *next; |
| 211 | struct rb_node **p, *prev; |
| 212 | LIST_HEAD(engines); |
| 213 | |
| 214 | sort_engines(i915, engines: &engines); |
| 215 | |
| 216 | prev = NULL; |
| 217 | p = &i915->uabi_engines.rb_node; |
| 218 | list_for_each_safe(it, next, &engines) { |
| 219 | struct intel_engine_cs *engine = |
| 220 | container_of(it, typeof(*engine), uabi_list); |
| 221 | |
| 222 | if (intel_gt_has_unrecoverable_error(gt: engine->gt)) |
| 223 | continue; /* ignore incomplete engines */ |
| 224 | |
| 225 | GEM_BUG_ON(engine->class >= ARRAY_SIZE(uabi_classes)); |
| 226 | engine->uabi_class = uabi_classes[engine->class]; |
| 227 | if (engine->uabi_class == I915_NO_UABI_CLASS) { |
| 228 | name_instance = other_instance++; |
| 229 | } else { |
| 230 | GEM_BUG_ON(engine->uabi_class >= |
| 231 | ARRAY_SIZE(i915->engine_uabi_class_count)); |
| 232 | name_instance = |
| 233 | i915->engine_uabi_class_count[engine->uabi_class]++; |
| 234 | } |
| 235 | engine->uabi_instance = name_instance; |
| 236 | |
| 237 | /* |
| 238 | * Replace the internal name with the final user and log facing |
| 239 | * name. |
| 240 | */ |
| 241 | engine_rename(engine, |
| 242 | name: intel_engine_class_repr(class: engine->class), |
| 243 | instance: name_instance); |
| 244 | |
| 245 | if (engine->uabi_class == I915_NO_UABI_CLASS) |
| 246 | continue; |
| 247 | |
| 248 | rb_link_node(node: &engine->uabi_node, parent: prev, rb_link: p); |
| 249 | rb_insert_color(&engine->uabi_node, &i915->uabi_engines); |
| 250 | |
| 251 | GEM_BUG_ON(intel_engine_lookup_user(i915, |
| 252 | engine->uabi_class, |
| 253 | engine->uabi_instance) != engine); |
| 254 | |
| 255 | /* Fix up the mapping to match default execbuf::user_map[] */ |
| 256 | add_legacy_ring(ring: &ring, engine); |
| 257 | |
| 258 | prev = &engine->uabi_node; |
| 259 | p = &prev->rb_right; |
| 260 | } |
| 261 | |
| 262 | if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) && |
| 263 | IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) { |
| 264 | struct intel_engine_cs *engine; |
| 265 | unsigned int isolation; |
| 266 | int class, inst; |
| 267 | int errors = 0; |
| 268 | |
| 269 | for (class = 0; class < ARRAY_SIZE(i915->engine_uabi_class_count); class++) { |
| 270 | for (inst = 0; inst < i915->engine_uabi_class_count[class]; inst++) { |
| 271 | engine = intel_engine_lookup_user(i915, |
| 272 | class, instance: inst); |
| 273 | if (!engine) { |
| 274 | pr_err("UABI engine not found for { class:%d, instance:%d }\n" , |
| 275 | class, inst); |
| 276 | errors++; |
| 277 | continue; |
| 278 | } |
| 279 | |
| 280 | if (engine->uabi_class != class || |
| 281 | engine->uabi_instance != inst) { |
| 282 | pr_err("Wrong UABI engine:%s { class:%d, instance:%d } found for { class:%d, instance:%d }\n" , |
| 283 | engine->name, |
| 284 | engine->uabi_class, |
| 285 | engine->uabi_instance, |
| 286 | class, inst); |
| 287 | errors++; |
| 288 | continue; |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | * Make sure that classes with multiple engine instances all |
| 295 | * share the same basic configuration. |
| 296 | */ |
| 297 | isolation = intel_engines_has_context_isolation(i915); |
| 298 | for_each_uabi_engine(engine, i915) { |
| 299 | unsigned int bit = BIT(engine->uabi_class); |
| 300 | unsigned int expected = engine->default_state ? bit : 0; |
| 301 | |
| 302 | if ((isolation & bit) != expected) { |
| 303 | pr_err("mismatching default context state for class %d on engine %s\n" , |
| 304 | engine->uabi_class, engine->name); |
| 305 | errors++; |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | if (drm_WARN(&i915->drm, errors, |
| 310 | "Invalid UABI engine mapping found" )) |
| 311 | i915->uabi_engines = RB_ROOT; |
| 312 | } |
| 313 | |
| 314 | set_scheduler_caps(i915); |
| 315 | } |
| 316 | |
| 317 | unsigned int intel_engines_has_context_isolation(struct drm_i915_private *i915) |
| 318 | { |
| 319 | struct intel_engine_cs *engine; |
| 320 | unsigned int which; |
| 321 | |
| 322 | which = 0; |
| 323 | for_each_uabi_engine(engine, i915) |
| 324 | if (engine->default_state) |
| 325 | which |= BIT(engine->uabi_class); |
| 326 | |
| 327 | return which; |
| 328 | } |
| 329 | |