538 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright © 2019 Intel Corporation4 */5 6#include <linux/kobject.h>7#include <linux/sysfs.h>8 9#include "i915_drv.h"10#include "intel_engine.h"11#include "intel_engine_heartbeat.h"12#include "sysfs_engines.h"13 14struct kobj_engine {15 struct kobject base;16 struct intel_engine_cs *engine;17};18 19static struct intel_engine_cs *kobj_to_engine(struct kobject *kobj)20{21 return container_of(kobj, struct kobj_engine, base)->engine;22}23 24static ssize_t25name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)26{27 return sysfs_emit(buf, "%s\n", kobj_to_engine(kobj)->name);28}29 30static const struct kobj_attribute name_attr =31__ATTR(name, 0444, name_show, NULL);32 33static ssize_t34class_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)35{36 return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_class);37}38 39static const struct kobj_attribute class_attr =40__ATTR(class, 0444, class_show, NULL);41 42static ssize_t43inst_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)44{45 return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_instance);46}47 48static const struct kobj_attribute inst_attr =49__ATTR(instance, 0444, inst_show, NULL);50 51static ssize_t52mmio_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)53{54 return sysfs_emit(buf, "0x%x\n", kobj_to_engine(kobj)->mmio_base);55}56 57static const struct kobj_attribute mmio_attr =58__ATTR(mmio_base, 0444, mmio_show, NULL);59 60static const char * const vcs_caps[] = {61 [ilog2(I915_VIDEO_CLASS_CAPABILITY_HEVC)] = "hevc",62 [ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",63};64 65static const char * const vecs_caps[] = {66 [ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",67};68 69static ssize_t repr_trim(char *buf, ssize_t len)70{71 /* Trim off the trailing space and replace with a newline */72 if (len > PAGE_SIZE)73 len = PAGE_SIZE;74 if (len > 0)75 buf[len - 1] = '\n';76 77 return len;78}79 80static ssize_t81__caps_show(struct intel_engine_cs *engine,82 unsigned long caps, char *buf, bool show_unknown)83{84 const char * const *repr;85 int count, n;86 ssize_t len;87 88 switch (engine->class) {89 case VIDEO_DECODE_CLASS:90 repr = vcs_caps;91 count = ARRAY_SIZE(vcs_caps);92 break;93 94 case VIDEO_ENHANCEMENT_CLASS:95 repr = vecs_caps;96 count = ARRAY_SIZE(vecs_caps);97 break;98 99 default:100 repr = NULL;101 count = 0;102 break;103 }104 GEM_BUG_ON(count > BITS_PER_LONG);105 106 len = 0;107 for_each_set_bit(n, &caps, show_unknown ? BITS_PER_LONG : count) {108 if (n >= count || !repr[n]) {109 if (GEM_WARN_ON(show_unknown))110 len += sysfs_emit_at(buf, len, "[%x] ", n);111 } else {112 len += sysfs_emit_at(buf, len, "%s ", repr[n]);113 }114 if (GEM_WARN_ON(len >= PAGE_SIZE))115 break;116 }117 return repr_trim(buf, len);118}119 120static ssize_t121caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)122{123 struct intel_engine_cs *engine = kobj_to_engine(kobj);124 125 return __caps_show(engine, engine->uabi_capabilities, buf, true);126}127 128static const struct kobj_attribute caps_attr =129__ATTR(capabilities, 0444, caps_show, NULL);130 131static ssize_t132all_caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)133{134 return __caps_show(kobj_to_engine(kobj), -1, buf, false);135}136 137static const struct kobj_attribute all_caps_attr =138__ATTR(known_capabilities, 0444, all_caps_show, NULL);139 140static ssize_t141max_spin_store(struct kobject *kobj, struct kobj_attribute *attr,142 const char *buf, size_t count)143{144 struct intel_engine_cs *engine = kobj_to_engine(kobj);145 unsigned long long duration, clamped;146 int err;147 148 /*149 * When waiting for a request, if is it currently being executed150 * on the GPU, we busywait for a short while before sleeping. The151 * premise is that most requests are short, and if it is already152 * executing then there is a good chance that it will complete153 * before we can setup the interrupt handler and go to sleep.154 * We try to offset the cost of going to sleep, by first spinning155 * on the request -- if it completed in less time than it would take156 * to go sleep, process the interrupt and return back to the client,157 * then we have saved the client some latency, albeit at the cost158 * of spinning on an expensive CPU core.159 *160 * While we try to avoid waiting at all for a request that is unlikely161 * to complete, deciding how long it is worth spinning is for is an162 * arbitrary decision: trading off power vs latency.163 */164 165 err = kstrtoull(buf, 0, &duration);166 if (err)167 return err;168 169 clamped = intel_clamp_max_busywait_duration_ns(engine, duration);170 if (duration != clamped)171 return -EINVAL;172 173 WRITE_ONCE(engine->props.max_busywait_duration_ns, duration);174 175 return count;176}177 178static ssize_t179max_spin_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)180{181 struct intel_engine_cs *engine = kobj_to_engine(kobj);182 183 return sysfs_emit(buf, "%lu\n", engine->props.max_busywait_duration_ns);184}185 186static const struct kobj_attribute max_spin_attr =187__ATTR(max_busywait_duration_ns, 0644, max_spin_show, max_spin_store);188 189static ssize_t190max_spin_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)191{192 struct intel_engine_cs *engine = kobj_to_engine(kobj);193 194 return sysfs_emit(buf, "%lu\n", engine->defaults.max_busywait_duration_ns);195}196 197static const struct kobj_attribute max_spin_def =198__ATTR(max_busywait_duration_ns, 0444, max_spin_default, NULL);199 200static ssize_t201timeslice_store(struct kobject *kobj, struct kobj_attribute *attr,202 const char *buf, size_t count)203{204 struct intel_engine_cs *engine = kobj_to_engine(kobj);205 unsigned long long duration, clamped;206 int err;207 208 /*209 * Execlists uses a scheduling quantum (a timeslice) to alternate210 * execution between ready-to-run contexts of equal priority. This211 * ensures that all users (though only if they of equal importance)212 * have the opportunity to run and prevents livelocks where contexts213 * may have implicit ordering due to userspace semaphores.214 */215 216 err = kstrtoull(buf, 0, &duration);217 if (err)218 return err;219 220 clamped = intel_clamp_timeslice_duration_ms(engine, duration);221 if (duration != clamped)222 return -EINVAL;223 224 WRITE_ONCE(engine->props.timeslice_duration_ms, duration);225 226 if (execlists_active(&engine->execlists))227 set_timer_ms(&engine->execlists.timer, duration);228 229 return count;230}231 232static ssize_t233timeslice_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)234{235 struct intel_engine_cs *engine = kobj_to_engine(kobj);236 237 return sysfs_emit(buf, "%lu\n", engine->props.timeslice_duration_ms);238}239 240static const struct kobj_attribute timeslice_duration_attr =241__ATTR(timeslice_duration_ms, 0644, timeslice_show, timeslice_store);242 243static ssize_t244timeslice_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)245{246 struct intel_engine_cs *engine = kobj_to_engine(kobj);247 248 return sysfs_emit(buf, "%lu\n", engine->defaults.timeslice_duration_ms);249}250 251static const struct kobj_attribute timeslice_duration_def =252__ATTR(timeslice_duration_ms, 0444, timeslice_default, NULL);253 254static ssize_t255stop_store(struct kobject *kobj, struct kobj_attribute *attr,256 const char *buf, size_t count)257{258 struct intel_engine_cs *engine = kobj_to_engine(kobj);259 unsigned long long duration, clamped;260 int err;261 262 /*263 * When we allow ourselves to sleep before a GPU reset after disabling264 * submission, even for a few milliseconds, gives an innocent context265 * the opportunity to clear the GPU before the reset occurs. However,266 * how long to sleep depends on the typical non-preemptible duration267 * (a similar problem to determining the ideal preempt-reset timeout268 * or even the heartbeat interval).269 */270 271 err = kstrtoull(buf, 0, &duration);272 if (err)273 return err;274 275 clamped = intel_clamp_stop_timeout_ms(engine, duration);276 if (duration != clamped)277 return -EINVAL;278 279 WRITE_ONCE(engine->props.stop_timeout_ms, duration);280 return count;281}282 283static ssize_t284stop_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)285{286 struct intel_engine_cs *engine = kobj_to_engine(kobj);287 288 return sysfs_emit(buf, "%lu\n", engine->props.stop_timeout_ms);289}290 291static const struct kobj_attribute stop_timeout_attr =292__ATTR(stop_timeout_ms, 0644, stop_show, stop_store);293 294static ssize_t295stop_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)296{297 struct intel_engine_cs *engine = kobj_to_engine(kobj);298 299 return sysfs_emit(buf, "%lu\n", engine->defaults.stop_timeout_ms);300}301 302static const struct kobj_attribute stop_timeout_def =303__ATTR(stop_timeout_ms, 0444, stop_default, NULL);304 305static ssize_t306preempt_timeout_store(struct kobject *kobj, struct kobj_attribute *attr,307 const char *buf, size_t count)308{309 struct intel_engine_cs *engine = kobj_to_engine(kobj);310 unsigned long long timeout, clamped;311 int err;312 313 /*314 * After initialising a preemption request, we give the current315 * resident a small amount of time to vacate the GPU. The preemption316 * request is for a higher priority context and should be immediate to317 * maintain high quality of service (and avoid priority inversion).318 * However, the preemption granularity of the GPU can be quite coarse319 * and so we need a compromise.320 */321 322 err = kstrtoull(buf, 0, &timeout);323 if (err)324 return err;325 326 clamped = intel_clamp_preempt_timeout_ms(engine, timeout);327 if (timeout != clamped)328 return -EINVAL;329 330 WRITE_ONCE(engine->props.preempt_timeout_ms, timeout);331 332 if (READ_ONCE(engine->execlists.pending[0]))333 set_timer_ms(&engine->execlists.preempt, timeout);334 335 return count;336}337 338static ssize_t339preempt_timeout_show(struct kobject *kobj, struct kobj_attribute *attr,340 char *buf)341{342 struct intel_engine_cs *engine = kobj_to_engine(kobj);343 344 return sysfs_emit(buf, "%lu\n", engine->props.preempt_timeout_ms);345}346 347static const struct kobj_attribute preempt_timeout_attr =348__ATTR(preempt_timeout_ms, 0644, preempt_timeout_show, preempt_timeout_store);349 350static ssize_t351preempt_timeout_default(struct kobject *kobj, struct kobj_attribute *attr,352 char *buf)353{354 struct intel_engine_cs *engine = kobj_to_engine(kobj);355 356 return sysfs_emit(buf, "%lu\n", engine->defaults.preempt_timeout_ms);357}358 359static const struct kobj_attribute preempt_timeout_def =360__ATTR(preempt_timeout_ms, 0444, preempt_timeout_default, NULL);361 362static ssize_t363heartbeat_store(struct kobject *kobj, struct kobj_attribute *attr,364 const char *buf, size_t count)365{366 struct intel_engine_cs *engine = kobj_to_engine(kobj);367 unsigned long long delay, clamped;368 int err;369 370 /*371 * We monitor the health of the system via periodic heartbeat pulses.372 * The pulses also provide the opportunity to perform garbage373 * collection. However, we interpret an incomplete pulse (a missed374 * heartbeat) as an indication that the system is no longer responsive,375 * i.e. hung, and perform an engine or full GPU reset. Given that the376 * preemption granularity can be very coarse on a system, the optimal377 * value for any workload is unknowable!378 */379 380 err = kstrtoull(buf, 0, &delay);381 if (err)382 return err;383 384 clamped = intel_clamp_heartbeat_interval_ms(engine, delay);385 if (delay != clamped)386 return -EINVAL;387 388 err = intel_engine_set_heartbeat(engine, delay);389 if (err)390 return err;391 392 return count;393}394 395static ssize_t396heartbeat_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)397{398 struct intel_engine_cs *engine = kobj_to_engine(kobj);399 400 return sysfs_emit(buf, "%lu\n", engine->props.heartbeat_interval_ms);401}402 403static const struct kobj_attribute heartbeat_interval_attr =404__ATTR(heartbeat_interval_ms, 0644, heartbeat_show, heartbeat_store);405 406static ssize_t407heartbeat_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)408{409 struct intel_engine_cs *engine = kobj_to_engine(kobj);410 411 return sysfs_emit(buf, "%lu\n", engine->defaults.heartbeat_interval_ms);412}413 414static const struct kobj_attribute heartbeat_interval_def =415__ATTR(heartbeat_interval_ms, 0444, heartbeat_default, NULL);416 417static void kobj_engine_release(struct kobject *kobj)418{419 kfree(kobj);420}421 422static const struct kobj_type kobj_engine_type = {423 .release = kobj_engine_release,424 .sysfs_ops = &kobj_sysfs_ops425};426 427static struct kobject *428kobj_engine(struct kobject *dir, struct intel_engine_cs *engine)429{430 struct kobj_engine *ke;431 432 ke = kzalloc(sizeof(*ke), GFP_KERNEL);433 if (!ke)434 return NULL;435 436 kobject_init(&ke->base, &kobj_engine_type);437 ke->engine = engine;438 439 if (kobject_add(&ke->base, dir, "%s", engine->name)) {440 kobject_put(&ke->base);441 return NULL;442 }443 444 /* xfer ownership to sysfs tree */445 return &ke->base;446}447 448static void add_defaults(struct kobj_engine *parent)449{450 static const struct attribute * const files[] = {451 &max_spin_def.attr,452 &stop_timeout_def.attr,453#if CONFIG_DRM_I915_HEARTBEAT_INTERVAL454 &heartbeat_interval_def.attr,455#endif456 NULL457 };458 struct kobj_engine *ke;459 460 ke = kzalloc(sizeof(*ke), GFP_KERNEL);461 if (!ke)462 return;463 464 kobject_init(&ke->base, &kobj_engine_type);465 ke->engine = parent->engine;466 467 if (kobject_add(&ke->base, &parent->base, "%s", ".defaults")) {468 kobject_put(&ke->base);469 return;470 }471 472 if (sysfs_create_files(&ke->base, files))473 return;474 475 if (intel_engine_has_timeslices(ke->engine) &&476 sysfs_create_file(&ke->base, ×lice_duration_def.attr))477 return;478 479 if (intel_engine_has_preempt_reset(ke->engine) &&480 sysfs_create_file(&ke->base, &preempt_timeout_def.attr))481 return;482}483 484void intel_engines_add_sysfs(struct drm_i915_private *i915)485{486 static const struct attribute * const files[] = {487 &name_attr.attr,488 &class_attr.attr,489 &inst_attr.attr,490 &mmio_attr.attr,491 &caps_attr.attr,492 &all_caps_attr.attr,493 &max_spin_attr.attr,494 &stop_timeout_attr.attr,495#if CONFIG_DRM_I915_HEARTBEAT_INTERVAL496 &heartbeat_interval_attr.attr,497#endif498 NULL499 };500 501 struct device *kdev = i915->drm.primary->kdev;502 struct intel_engine_cs *engine;503 struct kobject *dir;504 505 dir = kobject_create_and_add("engine", &kdev->kobj);506 if (!dir)507 return;508 509 for_each_uabi_engine(engine, i915) {510 struct kobject *kobj;511 512 kobj = kobj_engine(dir, engine);513 if (!kobj)514 goto err_engine;515 516 if (sysfs_create_files(kobj, files))517 goto err_object;518 519 if (intel_engine_has_timeslices(engine) &&520 sysfs_create_file(kobj, ×lice_duration_attr.attr))521 goto err_engine;522 523 if (intel_engine_has_preempt_reset(engine) &&524 sysfs_create_file(kobj, &preempt_timeout_attr.attr))525 goto err_engine;526 527 add_defaults(container_of(kobj, struct kobj_engine, base));528 529 if (0) {530err_object:531 kobject_put(kobj);532err_engine:533 dev_warn(kdev, "Failed to add sysfs engine '%s'\n",534 engine->name);535 }536 }537}538