162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2019 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kobject.h>
762306a36Sopenharmony_ci#include <linux/sysfs.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "i915_drv.h"
1062306a36Sopenharmony_ci#include "intel_engine.h"
1162306a36Sopenharmony_ci#include "intel_engine_heartbeat.h"
1262306a36Sopenharmony_ci#include "sysfs_engines.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct kobj_engine {
1562306a36Sopenharmony_ci	struct kobject base;
1662306a36Sopenharmony_ci	struct intel_engine_cs *engine;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct intel_engine_cs *kobj_to_engine(struct kobject *kobj)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return container_of(kobj, struct kobj_engine, base)->engine;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic ssize_t
2562306a36Sopenharmony_ciname_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", kobj_to_engine(kobj)->name);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct kobj_attribute name_attr =
3162306a36Sopenharmony_ci__ATTR(name, 0444, name_show, NULL);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic ssize_t
3462306a36Sopenharmony_ciclass_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_class);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct kobj_attribute class_attr =
4062306a36Sopenharmony_ci__ATTR(class, 0444, class_show, NULL);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic ssize_t
4362306a36Sopenharmony_ciinst_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_instance);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const struct kobj_attribute inst_attr =
4962306a36Sopenharmony_ci__ATTR(instance, 0444, inst_show, NULL);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic ssize_t
5262306a36Sopenharmony_cimmio_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%x\n", kobj_to_engine(kobj)->mmio_base);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct kobj_attribute mmio_attr =
5862306a36Sopenharmony_ci__ATTR(mmio_base, 0444, mmio_show, NULL);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic const char * const vcs_caps[] = {
6162306a36Sopenharmony_ci	[ilog2(I915_VIDEO_CLASS_CAPABILITY_HEVC)] = "hevc",
6262306a36Sopenharmony_ci	[ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const char * const vecs_caps[] = {
6662306a36Sopenharmony_ci	[ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic ssize_t repr_trim(char *buf, ssize_t len)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	/* Trim off the trailing space and replace with a newline */
7262306a36Sopenharmony_ci	if (len > PAGE_SIZE)
7362306a36Sopenharmony_ci		len = PAGE_SIZE;
7462306a36Sopenharmony_ci	if (len > 0)
7562306a36Sopenharmony_ci		buf[len - 1] = '\n';
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return len;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic ssize_t
8162306a36Sopenharmony_ci__caps_show(struct intel_engine_cs *engine,
8262306a36Sopenharmony_ci	    unsigned long caps, char *buf, bool show_unknown)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	const char * const *repr;
8562306a36Sopenharmony_ci	int count, n;
8662306a36Sopenharmony_ci	ssize_t len;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	switch (engine->class) {
8962306a36Sopenharmony_ci	case VIDEO_DECODE_CLASS:
9062306a36Sopenharmony_ci		repr = vcs_caps;
9162306a36Sopenharmony_ci		count = ARRAY_SIZE(vcs_caps);
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	case VIDEO_ENHANCEMENT_CLASS:
9562306a36Sopenharmony_ci		repr = vecs_caps;
9662306a36Sopenharmony_ci		count = ARRAY_SIZE(vecs_caps);
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	default:
10062306a36Sopenharmony_ci		repr = NULL;
10162306a36Sopenharmony_ci		count = 0;
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	GEM_BUG_ON(count > BITS_PER_LONG);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	len = 0;
10762306a36Sopenharmony_ci	for_each_set_bit(n, &caps, show_unknown ? BITS_PER_LONG : count) {
10862306a36Sopenharmony_ci		if (n >= count || !repr[n]) {
10962306a36Sopenharmony_ci			if (GEM_WARN_ON(show_unknown))
11062306a36Sopenharmony_ci				len += sysfs_emit_at(buf, len, "[%x] ", n);
11162306a36Sopenharmony_ci		} else {
11262306a36Sopenharmony_ci			len += sysfs_emit_at(buf, len, "%s ", repr[n]);
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci		if (GEM_WARN_ON(len >= PAGE_SIZE))
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	return repr_trim(buf, len);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic ssize_t
12162306a36Sopenharmony_cicaps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return __caps_show(engine, engine->uabi_capabilities, buf, true);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct kobj_attribute caps_attr =
12962306a36Sopenharmony_ci__ATTR(capabilities, 0444, caps_show, NULL);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic ssize_t
13262306a36Sopenharmony_ciall_caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return __caps_show(kobj_to_engine(kobj), -1, buf, false);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const struct kobj_attribute all_caps_attr =
13862306a36Sopenharmony_ci__ATTR(known_capabilities, 0444, all_caps_show, NULL);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic ssize_t
14162306a36Sopenharmony_cimax_spin_store(struct kobject *kobj, struct kobj_attribute *attr,
14262306a36Sopenharmony_ci	       const char *buf, size_t count)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
14562306a36Sopenharmony_ci	unsigned long long duration, clamped;
14662306a36Sopenharmony_ci	int err;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * When waiting for a request, if is it currently being executed
15062306a36Sopenharmony_ci	 * on the GPU, we busywait for a short while before sleeping. The
15162306a36Sopenharmony_ci	 * premise is that most requests are short, and if it is already
15262306a36Sopenharmony_ci	 * executing then there is a good chance that it will complete
15362306a36Sopenharmony_ci	 * before we can setup the interrupt handler and go to sleep.
15462306a36Sopenharmony_ci	 * We try to offset the cost of going to sleep, by first spinning
15562306a36Sopenharmony_ci	 * on the request -- if it completed in less time than it would take
15662306a36Sopenharmony_ci	 * to go sleep, process the interrupt and return back to the client,
15762306a36Sopenharmony_ci	 * then we have saved the client some latency, albeit at the cost
15862306a36Sopenharmony_ci	 * of spinning on an expensive CPU core.
15962306a36Sopenharmony_ci	 *
16062306a36Sopenharmony_ci	 * While we try to avoid waiting at all for a request that is unlikely
16162306a36Sopenharmony_ci	 * to complete, deciding how long it is worth spinning is for is an
16262306a36Sopenharmony_ci	 * arbitrary decision: trading off power vs latency.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	err = kstrtoull(buf, 0, &duration);
16662306a36Sopenharmony_ci	if (err)
16762306a36Sopenharmony_ci		return err;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	clamped = intel_clamp_max_busywait_duration_ns(engine, duration);
17062306a36Sopenharmony_ci	if (duration != clamped)
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	WRITE_ONCE(engine->props.max_busywait_duration_ns, duration);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return count;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic ssize_t
17962306a36Sopenharmony_cimax_spin_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->props.max_busywait_duration_ns);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const struct kobj_attribute max_spin_attr =
18762306a36Sopenharmony_ci__ATTR(max_busywait_duration_ns, 0644, max_spin_show, max_spin_store);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic ssize_t
19062306a36Sopenharmony_cimax_spin_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->defaults.max_busywait_duration_ns);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic const struct kobj_attribute max_spin_def =
19862306a36Sopenharmony_ci__ATTR(max_busywait_duration_ns, 0444, max_spin_default, NULL);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic ssize_t
20162306a36Sopenharmony_citimeslice_store(struct kobject *kobj, struct kobj_attribute *attr,
20262306a36Sopenharmony_ci		const char *buf, size_t count)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
20562306a36Sopenharmony_ci	unsigned long long duration, clamped;
20662306a36Sopenharmony_ci	int err;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * Execlists uses a scheduling quantum (a timeslice) to alternate
21062306a36Sopenharmony_ci	 * execution between ready-to-run contexts of equal priority. This
21162306a36Sopenharmony_ci	 * ensures that all users (though only if they of equal importance)
21262306a36Sopenharmony_ci	 * have the opportunity to run and prevents livelocks where contexts
21362306a36Sopenharmony_ci	 * may have implicit ordering due to userspace semaphores.
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	err = kstrtoull(buf, 0, &duration);
21762306a36Sopenharmony_ci	if (err)
21862306a36Sopenharmony_ci		return err;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	clamped = intel_clamp_timeslice_duration_ms(engine, duration);
22162306a36Sopenharmony_ci	if (duration != clamped)
22262306a36Sopenharmony_ci		return -EINVAL;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	WRITE_ONCE(engine->props.timeslice_duration_ms, duration);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (execlists_active(&engine->execlists))
22762306a36Sopenharmony_ci		set_timer_ms(&engine->execlists.timer, duration);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return count;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic ssize_t
23362306a36Sopenharmony_citimeslice_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->props.timeslice_duration_ms);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic const struct kobj_attribute timeslice_duration_attr =
24162306a36Sopenharmony_ci__ATTR(timeslice_duration_ms, 0644, timeslice_show, timeslice_store);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic ssize_t
24462306a36Sopenharmony_citimeslice_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->defaults.timeslice_duration_ms);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic const struct kobj_attribute timeslice_duration_def =
25262306a36Sopenharmony_ci__ATTR(timeslice_duration_ms, 0444, timeslice_default, NULL);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic ssize_t
25562306a36Sopenharmony_cistop_store(struct kobject *kobj, struct kobj_attribute *attr,
25662306a36Sopenharmony_ci	   const char *buf, size_t count)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
25962306a36Sopenharmony_ci	unsigned long long duration, clamped;
26062306a36Sopenharmony_ci	int err;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/*
26362306a36Sopenharmony_ci	 * When we allow ourselves to sleep before a GPU reset after disabling
26462306a36Sopenharmony_ci	 * submission, even for a few milliseconds, gives an innocent context
26562306a36Sopenharmony_ci	 * the opportunity to clear the GPU before the reset occurs. However,
26662306a36Sopenharmony_ci	 * how long to sleep depends on the typical non-preemptible duration
26762306a36Sopenharmony_ci	 * (a similar problem to determining the ideal preempt-reset timeout
26862306a36Sopenharmony_ci	 * or even the heartbeat interval).
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	err = kstrtoull(buf, 0, &duration);
27262306a36Sopenharmony_ci	if (err)
27362306a36Sopenharmony_ci		return err;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	clamped = intel_clamp_stop_timeout_ms(engine, duration);
27662306a36Sopenharmony_ci	if (duration != clamped)
27762306a36Sopenharmony_ci		return -EINVAL;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	WRITE_ONCE(engine->props.stop_timeout_ms, duration);
28062306a36Sopenharmony_ci	return count;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic ssize_t
28462306a36Sopenharmony_cistop_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->props.stop_timeout_ms);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic const struct kobj_attribute stop_timeout_attr =
29262306a36Sopenharmony_ci__ATTR(stop_timeout_ms, 0644, stop_show, stop_store);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic ssize_t
29562306a36Sopenharmony_cistop_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->defaults.stop_timeout_ms);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic const struct kobj_attribute stop_timeout_def =
30362306a36Sopenharmony_ci__ATTR(stop_timeout_ms, 0444, stop_default, NULL);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic ssize_t
30662306a36Sopenharmony_cipreempt_timeout_store(struct kobject *kobj, struct kobj_attribute *attr,
30762306a36Sopenharmony_ci		      const char *buf, size_t count)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
31062306a36Sopenharmony_ci	unsigned long long timeout, clamped;
31162306a36Sopenharmony_ci	int err;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/*
31462306a36Sopenharmony_ci	 * After initialising a preemption request, we give the current
31562306a36Sopenharmony_ci	 * resident a small amount of time to vacate the GPU. The preemption
31662306a36Sopenharmony_ci	 * request is for a higher priority context and should be immediate to
31762306a36Sopenharmony_ci	 * maintain high quality of service (and avoid priority inversion).
31862306a36Sopenharmony_ci	 * However, the preemption granularity of the GPU can be quite coarse
31962306a36Sopenharmony_ci	 * and so we need a compromise.
32062306a36Sopenharmony_ci	 */
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	err = kstrtoull(buf, 0, &timeout);
32362306a36Sopenharmony_ci	if (err)
32462306a36Sopenharmony_ci		return err;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	clamped = intel_clamp_preempt_timeout_ms(engine, timeout);
32762306a36Sopenharmony_ci	if (timeout != clamped)
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	WRITE_ONCE(engine->props.preempt_timeout_ms, timeout);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (READ_ONCE(engine->execlists.pending[0]))
33362306a36Sopenharmony_ci		set_timer_ms(&engine->execlists.preempt, timeout);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return count;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic ssize_t
33962306a36Sopenharmony_cipreempt_timeout_show(struct kobject *kobj, struct kobj_attribute *attr,
34062306a36Sopenharmony_ci		     char *buf)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->props.preempt_timeout_ms);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic const struct kobj_attribute preempt_timeout_attr =
34862306a36Sopenharmony_ci__ATTR(preempt_timeout_ms, 0644, preempt_timeout_show, preempt_timeout_store);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic ssize_t
35162306a36Sopenharmony_cipreempt_timeout_default(struct kobject *kobj, struct kobj_attribute *attr,
35262306a36Sopenharmony_ci			char *buf)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->defaults.preempt_timeout_ms);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic const struct kobj_attribute preempt_timeout_def =
36062306a36Sopenharmony_ci__ATTR(preempt_timeout_ms, 0444, preempt_timeout_default, NULL);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic ssize_t
36362306a36Sopenharmony_ciheartbeat_store(struct kobject *kobj, struct kobj_attribute *attr,
36462306a36Sopenharmony_ci		const char *buf, size_t count)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
36762306a36Sopenharmony_ci	unsigned long long delay, clamped;
36862306a36Sopenharmony_ci	int err;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/*
37162306a36Sopenharmony_ci	 * We monitor the health of the system via periodic heartbeat pulses.
37262306a36Sopenharmony_ci	 * The pulses also provide the opportunity to perform garbage
37362306a36Sopenharmony_ci	 * collection.  However, we interpret an incomplete pulse (a missed
37462306a36Sopenharmony_ci	 * heartbeat) as an indication that the system is no longer responsive,
37562306a36Sopenharmony_ci	 * i.e. hung, and perform an engine or full GPU reset. Given that the
37662306a36Sopenharmony_ci	 * preemption granularity can be very coarse on a system, the optimal
37762306a36Sopenharmony_ci	 * value for any workload is unknowable!
37862306a36Sopenharmony_ci	 */
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	err = kstrtoull(buf, 0, &delay);
38162306a36Sopenharmony_ci	if (err)
38262306a36Sopenharmony_ci		return err;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	clamped = intel_clamp_heartbeat_interval_ms(engine, delay);
38562306a36Sopenharmony_ci	if (delay != clamped)
38662306a36Sopenharmony_ci		return -EINVAL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	err = intel_engine_set_heartbeat(engine, delay);
38962306a36Sopenharmony_ci	if (err)
39062306a36Sopenharmony_ci		return err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return count;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic ssize_t
39662306a36Sopenharmony_ciheartbeat_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->props.heartbeat_interval_ms);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic const struct kobj_attribute heartbeat_interval_attr =
40462306a36Sopenharmony_ci__ATTR(heartbeat_interval_ms, 0644, heartbeat_show, heartbeat_store);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic ssize_t
40762306a36Sopenharmony_ciheartbeat_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct intel_engine_cs *engine = kobj_to_engine(kobj);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", engine->defaults.heartbeat_interval_ms);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic const struct kobj_attribute heartbeat_interval_def =
41562306a36Sopenharmony_ci__ATTR(heartbeat_interval_ms, 0444, heartbeat_default, NULL);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void kobj_engine_release(struct kobject *kobj)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	kfree(kobj);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic const struct kobj_type kobj_engine_type = {
42362306a36Sopenharmony_ci	.release = kobj_engine_release,
42462306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops
42562306a36Sopenharmony_ci};
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic struct kobject *
42862306a36Sopenharmony_cikobj_engine(struct kobject *dir, struct intel_engine_cs *engine)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct kobj_engine *ke;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	ke = kzalloc(sizeof(*ke), GFP_KERNEL);
43362306a36Sopenharmony_ci	if (!ke)
43462306a36Sopenharmony_ci		return NULL;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	kobject_init(&ke->base, &kobj_engine_type);
43762306a36Sopenharmony_ci	ke->engine = engine;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (kobject_add(&ke->base, dir, "%s", engine->name)) {
44062306a36Sopenharmony_ci		kobject_put(&ke->base);
44162306a36Sopenharmony_ci		return NULL;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* xfer ownership to sysfs tree */
44562306a36Sopenharmony_ci	return &ke->base;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void add_defaults(struct kobj_engine *parent)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	static const struct attribute * const files[] = {
45162306a36Sopenharmony_ci		&max_spin_def.attr,
45262306a36Sopenharmony_ci		&stop_timeout_def.attr,
45362306a36Sopenharmony_ci#if CONFIG_DRM_I915_HEARTBEAT_INTERVAL
45462306a36Sopenharmony_ci		&heartbeat_interval_def.attr,
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci		NULL
45762306a36Sopenharmony_ci	};
45862306a36Sopenharmony_ci	struct kobj_engine *ke;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ke = kzalloc(sizeof(*ke), GFP_KERNEL);
46162306a36Sopenharmony_ci	if (!ke)
46262306a36Sopenharmony_ci		return;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	kobject_init(&ke->base, &kobj_engine_type);
46562306a36Sopenharmony_ci	ke->engine = parent->engine;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (kobject_add(&ke->base, &parent->base, "%s", ".defaults")) {
46862306a36Sopenharmony_ci		kobject_put(&ke->base);
46962306a36Sopenharmony_ci		return;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (sysfs_create_files(&ke->base, files))
47362306a36Sopenharmony_ci		return;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (intel_engine_has_timeslices(ke->engine) &&
47662306a36Sopenharmony_ci	    sysfs_create_file(&ke->base, &timeslice_duration_def.attr))
47762306a36Sopenharmony_ci		return;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (intel_engine_has_preempt_reset(ke->engine) &&
48062306a36Sopenharmony_ci	    sysfs_create_file(&ke->base, &preempt_timeout_def.attr))
48162306a36Sopenharmony_ci		return;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_civoid intel_engines_add_sysfs(struct drm_i915_private *i915)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	static const struct attribute * const files[] = {
48762306a36Sopenharmony_ci		&name_attr.attr,
48862306a36Sopenharmony_ci		&class_attr.attr,
48962306a36Sopenharmony_ci		&inst_attr.attr,
49062306a36Sopenharmony_ci		&mmio_attr.attr,
49162306a36Sopenharmony_ci		&caps_attr.attr,
49262306a36Sopenharmony_ci		&all_caps_attr.attr,
49362306a36Sopenharmony_ci		&max_spin_attr.attr,
49462306a36Sopenharmony_ci		&stop_timeout_attr.attr,
49562306a36Sopenharmony_ci#if CONFIG_DRM_I915_HEARTBEAT_INTERVAL
49662306a36Sopenharmony_ci		&heartbeat_interval_attr.attr,
49762306a36Sopenharmony_ci#endif
49862306a36Sopenharmony_ci		NULL
49962306a36Sopenharmony_ci	};
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	struct device *kdev = i915->drm.primary->kdev;
50262306a36Sopenharmony_ci	struct intel_engine_cs *engine;
50362306a36Sopenharmony_ci	struct kobject *dir;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	dir = kobject_create_and_add("engine", &kdev->kobj);
50662306a36Sopenharmony_ci	if (!dir)
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for_each_uabi_engine(engine, i915) {
51062306a36Sopenharmony_ci		struct kobject *kobj;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		kobj = kobj_engine(dir, engine);
51362306a36Sopenharmony_ci		if (!kobj)
51462306a36Sopenharmony_ci			goto err_engine;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		if (sysfs_create_files(kobj, files))
51762306a36Sopenharmony_ci			goto err_object;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (intel_engine_has_timeslices(engine) &&
52062306a36Sopenharmony_ci		    sysfs_create_file(kobj, &timeslice_duration_attr.attr))
52162306a36Sopenharmony_ci			goto err_engine;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		if (intel_engine_has_preempt_reset(engine) &&
52462306a36Sopenharmony_ci		    sysfs_create_file(kobj, &preempt_timeout_attr.attr))
52562306a36Sopenharmony_ci			goto err_engine;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		add_defaults(container_of(kobj, struct kobj_engine, base));
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		if (0) {
53062306a36Sopenharmony_cierr_object:
53162306a36Sopenharmony_ci			kobject_put(kobj);
53262306a36Sopenharmony_cierr_engine:
53362306a36Sopenharmony_ci			dev_err(kdev, "Failed to add sysfs engine '%s'\n",
53462306a36Sopenharmony_ci				engine->name);
53562306a36Sopenharmony_ci			break;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci}
539