brintos

brintos / linux-shallow public Read only

0
0
Text · 14.1 KiB · aab2759 Raw
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, &timeslice_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, &timeslice_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