14ec76dbeSChris Wilson // SPDX-License-Identifier: MIT
24ec76dbeSChris Wilson /*
34ec76dbeSChris Wilson  * Copyright © 2019 Intel Corporation
44ec76dbeSChris Wilson  */
54ec76dbeSChris Wilson 
64ec76dbeSChris Wilson #include <linux/kobject.h>
74ec76dbeSChris Wilson #include <linux/sysfs.h>
84ec76dbeSChris Wilson 
94ec76dbeSChris Wilson #include "i915_drv.h"
104ec76dbeSChris Wilson #include "intel_engine.h"
119a40bdddSChris Wilson #include "intel_engine_heartbeat.h"
124ec76dbeSChris Wilson #include "sysfs_engines.h"
134ec76dbeSChris Wilson 
144ec76dbeSChris Wilson struct kobj_engine {
154ec76dbeSChris Wilson 	struct kobject base;
164ec76dbeSChris Wilson 	struct intel_engine_cs *engine;
174ec76dbeSChris Wilson };
184ec76dbeSChris Wilson 
kobj_to_engine(struct kobject * kobj)194ec76dbeSChris Wilson static struct intel_engine_cs *kobj_to_engine(struct kobject *kobj)
204ec76dbeSChris Wilson {
214ec76dbeSChris Wilson 	return container_of(kobj, struct kobj_engine, base)->engine;
224ec76dbeSChris Wilson }
234ec76dbeSChris Wilson 
244ec76dbeSChris Wilson static ssize_t
name_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)254ec76dbeSChris Wilson name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
264ec76dbeSChris Wilson {
271f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%s\n", kobj_to_engine(kobj)->name);
284ec76dbeSChris Wilson }
294ec76dbeSChris Wilson 
30*d2a9692aSJani Nikula static const struct kobj_attribute name_attr =
314ec76dbeSChris Wilson __ATTR(name, 0444, name_show, NULL);
324ec76dbeSChris Wilson 
334ec76dbeSChris Wilson static ssize_t
class_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)344ec76dbeSChris Wilson class_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
354ec76dbeSChris Wilson {
361f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_class);
374ec76dbeSChris Wilson }
384ec76dbeSChris Wilson 
39*d2a9692aSJani Nikula static const struct kobj_attribute class_attr =
404ec76dbeSChris Wilson __ATTR(class, 0444, class_show, NULL);
414ec76dbeSChris Wilson 
424ec76dbeSChris Wilson static ssize_t
inst_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)434ec76dbeSChris Wilson inst_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
444ec76dbeSChris Wilson {
451f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%d\n", kobj_to_engine(kobj)->uabi_instance);
464ec76dbeSChris Wilson }
474ec76dbeSChris Wilson 
48*d2a9692aSJani Nikula static const struct kobj_attribute inst_attr =
494ec76dbeSChris Wilson __ATTR(instance, 0444, inst_show, NULL);
504ec76dbeSChris Wilson 
516e57cc39SChris Wilson static ssize_t
mmio_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)526e57cc39SChris Wilson mmio_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
536e57cc39SChris Wilson {
541f16fdbbSNirmoy Das 	return sysfs_emit(buf, "0x%x\n", kobj_to_engine(kobj)->mmio_base);
556e57cc39SChris Wilson }
566e57cc39SChris Wilson 
57*d2a9692aSJani Nikula static const struct kobj_attribute mmio_attr =
586e57cc39SChris Wilson __ATTR(mmio_base, 0444, mmio_show, NULL);
596e57cc39SChris Wilson 
604ec76dbeSChris Wilson static const char * const vcs_caps[] = {
614ec76dbeSChris Wilson 	[ilog2(I915_VIDEO_CLASS_CAPABILITY_HEVC)] = "hevc",
624ec76dbeSChris Wilson 	[ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",
634ec76dbeSChris Wilson };
644ec76dbeSChris Wilson 
654ec76dbeSChris Wilson static const char * const vecs_caps[] = {
664ec76dbeSChris Wilson 	[ilog2(I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC)] = "sfc",
674ec76dbeSChris Wilson };
684ec76dbeSChris Wilson 
repr_trim(char * buf,ssize_t len)694ec76dbeSChris Wilson static ssize_t repr_trim(char *buf, ssize_t len)
704ec76dbeSChris Wilson {
714ec76dbeSChris Wilson 	/* Trim off the trailing space and replace with a newline */
724ec76dbeSChris Wilson 	if (len > PAGE_SIZE)
734ec76dbeSChris Wilson 		len = PAGE_SIZE;
744ec76dbeSChris Wilson 	if (len > 0)
754ec76dbeSChris Wilson 		buf[len - 1] = '\n';
764ec76dbeSChris Wilson 
774ec76dbeSChris Wilson 	return len;
784ec76dbeSChris Wilson }
794ec76dbeSChris Wilson 
804ec76dbeSChris Wilson static ssize_t
__caps_show(struct intel_engine_cs * engine,unsigned long caps,char * buf,bool show_unknown)814ec76dbeSChris Wilson __caps_show(struct intel_engine_cs *engine,
826971e07bSChris Wilson 	    unsigned long caps, char *buf, bool show_unknown)
834ec76dbeSChris Wilson {
844ec76dbeSChris Wilson 	const char * const *repr;
854ec76dbeSChris Wilson 	int count, n;
864ec76dbeSChris Wilson 	ssize_t len;
874ec76dbeSChris Wilson 
884ec76dbeSChris Wilson 	switch (engine->class) {
894ec76dbeSChris Wilson 	case VIDEO_DECODE_CLASS:
904ec76dbeSChris Wilson 		repr = vcs_caps;
914ec76dbeSChris Wilson 		count = ARRAY_SIZE(vcs_caps);
924ec76dbeSChris Wilson 		break;
934ec76dbeSChris Wilson 
944ec76dbeSChris Wilson 	case VIDEO_ENHANCEMENT_CLASS:
954ec76dbeSChris Wilson 		repr = vecs_caps;
964ec76dbeSChris Wilson 		count = ARRAY_SIZE(vecs_caps);
974ec76dbeSChris Wilson 		break;
984ec76dbeSChris Wilson 
994ec76dbeSChris Wilson 	default:
1004ec76dbeSChris Wilson 		repr = NULL;
1014ec76dbeSChris Wilson 		count = 0;
1024ec76dbeSChris Wilson 		break;
1034ec76dbeSChris Wilson 	}
1046971e07bSChris Wilson 	GEM_BUG_ON(count > BITS_PER_LONG);
1054ec76dbeSChris Wilson 
1064ec76dbeSChris Wilson 	len = 0;
1076971e07bSChris Wilson 	for_each_set_bit(n, &caps, show_unknown ? BITS_PER_LONG : count) {
1084ec76dbeSChris Wilson 		if (n >= count || !repr[n]) {
1094ec76dbeSChris Wilson 			if (GEM_WARN_ON(show_unknown))
1101f16fdbbSNirmoy Das 				len += sysfs_emit_at(buf, len, "[%x] ", n);
1114ec76dbeSChris Wilson 		} else {
1121f16fdbbSNirmoy Das 			len += sysfs_emit_at(buf, len, "%s ", repr[n]);
1134ec76dbeSChris Wilson 		}
1144ec76dbeSChris Wilson 		if (GEM_WARN_ON(len >= PAGE_SIZE))
1154ec76dbeSChris Wilson 			break;
1164ec76dbeSChris Wilson 	}
1174ec76dbeSChris Wilson 	return repr_trim(buf, len);
1184ec76dbeSChris Wilson }
1194ec76dbeSChris Wilson 
1204ec76dbeSChris Wilson static ssize_t
caps_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1214ec76dbeSChris Wilson caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
1224ec76dbeSChris Wilson {
1234ec76dbeSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
1244ec76dbeSChris Wilson 
1254ec76dbeSChris Wilson 	return __caps_show(engine, engine->uabi_capabilities, buf, true);
1264ec76dbeSChris Wilson }
1274ec76dbeSChris Wilson 
128*d2a9692aSJani Nikula static const struct kobj_attribute caps_attr =
1294ec76dbeSChris Wilson __ATTR(capabilities, 0444, caps_show, NULL);
1304ec76dbeSChris Wilson 
1314ec76dbeSChris Wilson static ssize_t
all_caps_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1324ec76dbeSChris Wilson all_caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
1334ec76dbeSChris Wilson {
1344ec76dbeSChris Wilson 	return __caps_show(kobj_to_engine(kobj), -1, buf, false);
1354ec76dbeSChris Wilson }
1364ec76dbeSChris Wilson 
137*d2a9692aSJani Nikula static const struct kobj_attribute all_caps_attr =
1384ec76dbeSChris Wilson __ATTR(known_capabilities, 0444, all_caps_show, NULL);
1394ec76dbeSChris Wilson 
1401a2695a7SChris Wilson static ssize_t
max_spin_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)141062444bbSChris Wilson max_spin_store(struct kobject *kobj, struct kobj_attribute *attr,
142062444bbSChris Wilson 	       const char *buf, size_t count)
143062444bbSChris Wilson {
144062444bbSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
145568944afSJohn Harrison 	unsigned long long duration, clamped;
146062444bbSChris Wilson 	int err;
147062444bbSChris Wilson 
148062444bbSChris Wilson 	/*
149062444bbSChris Wilson 	 * When waiting for a request, if is it currently being executed
150062444bbSChris Wilson 	 * on the GPU, we busywait for a short while before sleeping. The
151062444bbSChris Wilson 	 * premise is that most requests are short, and if it is already
152062444bbSChris Wilson 	 * executing then there is a good chance that it will complete
153062444bbSChris Wilson 	 * before we can setup the interrupt handler and go to sleep.
154062444bbSChris Wilson 	 * We try to offset the cost of going to sleep, by first spinning
155062444bbSChris Wilson 	 * on the request -- if it completed in less time than it would take
156062444bbSChris Wilson 	 * to go sleep, process the interrupt and return back to the client,
157062444bbSChris Wilson 	 * then we have saved the client some latency, albeit at the cost
158062444bbSChris Wilson 	 * of spinning on an expensive CPU core.
159062444bbSChris Wilson 	 *
160062444bbSChris Wilson 	 * While we try to avoid waiting at all for a request that is unlikely
161062444bbSChris Wilson 	 * to complete, deciding how long it is worth spinning is for is an
162062444bbSChris Wilson 	 * arbitrary decision: trading off power vs latency.
163062444bbSChris Wilson 	 */
164062444bbSChris Wilson 
165062444bbSChris Wilson 	err = kstrtoull(buf, 0, &duration);
166062444bbSChris Wilson 	if (err)
167062444bbSChris Wilson 		return err;
168062444bbSChris Wilson 
169568944afSJohn Harrison 	clamped = intel_clamp_max_busywait_duration_ns(engine, duration);
170568944afSJohn Harrison 	if (duration != clamped)
171062444bbSChris Wilson 		return -EINVAL;
172062444bbSChris Wilson 
173062444bbSChris Wilson 	WRITE_ONCE(engine->props.max_busywait_duration_ns, duration);
174062444bbSChris Wilson 
175062444bbSChris Wilson 	return count;
176062444bbSChris Wilson }
177062444bbSChris Wilson 
178062444bbSChris Wilson static ssize_t
max_spin_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)179062444bbSChris Wilson max_spin_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
180062444bbSChris Wilson {
181062444bbSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
182062444bbSChris Wilson 
1831f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->props.max_busywait_duration_ns);
184062444bbSChris Wilson }
185062444bbSChris Wilson 
186*d2a9692aSJani Nikula static const struct kobj_attribute max_spin_attr =
187062444bbSChris Wilson __ATTR(max_busywait_duration_ns, 0644, max_spin_show, max_spin_store);
188062444bbSChris Wilson 
189062444bbSChris Wilson static ssize_t
max_spin_default(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1907a0ba6b4SChris Wilson max_spin_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
1917a0ba6b4SChris Wilson {
1927a0ba6b4SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
1937a0ba6b4SChris Wilson 
1941f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->defaults.max_busywait_duration_ns);
1957a0ba6b4SChris Wilson }
1967a0ba6b4SChris Wilson 
197*d2a9692aSJani Nikula static const struct kobj_attribute max_spin_def =
1987a0ba6b4SChris Wilson __ATTR(max_busywait_duration_ns, 0444, max_spin_default, NULL);
1997a0ba6b4SChris Wilson 
2007a0ba6b4SChris Wilson static ssize_t
timeslice_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)2011a2695a7SChris Wilson timeslice_store(struct kobject *kobj, struct kobj_attribute *attr,
2021a2695a7SChris Wilson 		const char *buf, size_t count)
2031a2695a7SChris Wilson {
2041a2695a7SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
205568944afSJohn Harrison 	unsigned long long duration, clamped;
2061a2695a7SChris Wilson 	int err;
2071a2695a7SChris Wilson 
2081a2695a7SChris Wilson 	/*
2091a2695a7SChris Wilson 	 * Execlists uses a scheduling quantum (a timeslice) to alternate
2101a2695a7SChris Wilson 	 * execution between ready-to-run contexts of equal priority. This
2111a2695a7SChris Wilson 	 * ensures that all users (though only if they of equal importance)
2121a2695a7SChris Wilson 	 * have the opportunity to run and prevents livelocks where contexts
2131a2695a7SChris Wilson 	 * may have implicit ordering due to userspace semaphores.
2141a2695a7SChris Wilson 	 */
2151a2695a7SChris Wilson 
2161a2695a7SChris Wilson 	err = kstrtoull(buf, 0, &duration);
2171a2695a7SChris Wilson 	if (err)
2181a2695a7SChris Wilson 		return err;
2191a2695a7SChris Wilson 
220568944afSJohn Harrison 	clamped = intel_clamp_timeslice_duration_ms(engine, duration);
221568944afSJohn Harrison 	if (duration != clamped)
2221a2695a7SChris Wilson 		return -EINVAL;
2231a2695a7SChris Wilson 
2241a2695a7SChris Wilson 	WRITE_ONCE(engine->props.timeslice_duration_ms, duration);
2251a2695a7SChris Wilson 
2261a2695a7SChris Wilson 	if (execlists_active(&engine->execlists))
2271a2695a7SChris Wilson 		set_timer_ms(&engine->execlists.timer, duration);
2281a2695a7SChris Wilson 
2291a2695a7SChris Wilson 	return count;
2301a2695a7SChris Wilson }
2311a2695a7SChris Wilson 
2321a2695a7SChris Wilson static ssize_t
timeslice_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2331a2695a7SChris Wilson timeslice_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
2341a2695a7SChris Wilson {
2351a2695a7SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
2361a2695a7SChris Wilson 
2371f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->props.timeslice_duration_ms);
2381a2695a7SChris Wilson }
2391a2695a7SChris Wilson 
240*d2a9692aSJani Nikula static const struct kobj_attribute timeslice_duration_attr =
2411a2695a7SChris Wilson __ATTR(timeslice_duration_ms, 0644, timeslice_show, timeslice_store);
2421a2695a7SChris Wilson 
24372338a1fSChris Wilson static ssize_t
timeslice_default(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2447a0ba6b4SChris Wilson timeslice_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
2457a0ba6b4SChris Wilson {
2467a0ba6b4SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
2477a0ba6b4SChris Wilson 
2481f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->defaults.timeslice_duration_ms);
2497a0ba6b4SChris Wilson }
2507a0ba6b4SChris Wilson 
251*d2a9692aSJani Nikula static const struct kobj_attribute timeslice_duration_def =
2527a0ba6b4SChris Wilson __ATTR(timeslice_duration_ms, 0444, timeslice_default, NULL);
2537a0ba6b4SChris Wilson 
2547a0ba6b4SChris Wilson static ssize_t
stop_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)25572338a1fSChris Wilson stop_store(struct kobject *kobj, struct kobj_attribute *attr,
25672338a1fSChris Wilson 	   const char *buf, size_t count)
25772338a1fSChris Wilson {
25872338a1fSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
259568944afSJohn Harrison 	unsigned long long duration, clamped;
26072338a1fSChris Wilson 	int err;
26172338a1fSChris Wilson 
26272338a1fSChris Wilson 	/*
26372338a1fSChris Wilson 	 * When we allow ourselves to sleep before a GPU reset after disabling
26472338a1fSChris Wilson 	 * submission, even for a few milliseconds, gives an innocent context
26572338a1fSChris Wilson 	 * the opportunity to clear the GPU before the reset occurs. However,
26672338a1fSChris Wilson 	 * how long to sleep depends on the typical non-preemptible duration
26772338a1fSChris Wilson 	 * (a similar problem to determining the ideal preempt-reset timeout
26872338a1fSChris Wilson 	 * or even the heartbeat interval).
26972338a1fSChris Wilson 	 */
27072338a1fSChris Wilson 
27172338a1fSChris Wilson 	err = kstrtoull(buf, 0, &duration);
27272338a1fSChris Wilson 	if (err)
27372338a1fSChris Wilson 		return err;
27472338a1fSChris Wilson 
275568944afSJohn Harrison 	clamped = intel_clamp_stop_timeout_ms(engine, duration);
276568944afSJohn Harrison 	if (duration != clamped)
27772338a1fSChris Wilson 		return -EINVAL;
27872338a1fSChris Wilson 
27972338a1fSChris Wilson 	WRITE_ONCE(engine->props.stop_timeout_ms, duration);
28072338a1fSChris Wilson 	return count;
28172338a1fSChris Wilson }
28272338a1fSChris Wilson 
28372338a1fSChris Wilson static ssize_t
stop_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)28472338a1fSChris Wilson stop_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
28572338a1fSChris Wilson {
28672338a1fSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
28772338a1fSChris Wilson 
2881f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->props.stop_timeout_ms);
28972338a1fSChris Wilson }
29072338a1fSChris Wilson 
291*d2a9692aSJani Nikula static const struct kobj_attribute stop_timeout_attr =
29272338a1fSChris Wilson __ATTR(stop_timeout_ms, 0644, stop_show, stop_store);
29372338a1fSChris Wilson 
294db3d8338SChris Wilson static ssize_t
stop_default(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2957a0ba6b4SChris Wilson stop_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
2967a0ba6b4SChris Wilson {
2977a0ba6b4SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
2987a0ba6b4SChris Wilson 
2991f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->defaults.stop_timeout_ms);
3007a0ba6b4SChris Wilson }
3017a0ba6b4SChris Wilson 
302*d2a9692aSJani Nikula static const struct kobj_attribute stop_timeout_def =
3037a0ba6b4SChris Wilson __ATTR(stop_timeout_ms, 0444, stop_default, NULL);
3047a0ba6b4SChris Wilson 
3057a0ba6b4SChris Wilson static ssize_t
preempt_timeout_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)306db3d8338SChris Wilson preempt_timeout_store(struct kobject *kobj, struct kobj_attribute *attr,
307db3d8338SChris Wilson 		      const char *buf, size_t count)
308db3d8338SChris Wilson {
309db3d8338SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
310568944afSJohn Harrison 	unsigned long long timeout, clamped;
311db3d8338SChris Wilson 	int err;
312db3d8338SChris Wilson 
313db3d8338SChris Wilson 	/*
314db3d8338SChris Wilson 	 * After initialising a preemption request, we give the current
315db3d8338SChris Wilson 	 * resident a small amount of time to vacate the GPU. The preemption
316db3d8338SChris Wilson 	 * request is for a higher priority context and should be immediate to
317db3d8338SChris Wilson 	 * maintain high quality of service (and avoid priority inversion).
318db3d8338SChris Wilson 	 * However, the preemption granularity of the GPU can be quite coarse
319db3d8338SChris Wilson 	 * and so we need a compromise.
320db3d8338SChris Wilson 	 */
321db3d8338SChris Wilson 
322db3d8338SChris Wilson 	err = kstrtoull(buf, 0, &timeout);
323db3d8338SChris Wilson 	if (err)
324db3d8338SChris Wilson 		return err;
325db3d8338SChris Wilson 
326568944afSJohn Harrison 	clamped = intel_clamp_preempt_timeout_ms(engine, timeout);
327568944afSJohn Harrison 	if (timeout != clamped)
328db3d8338SChris Wilson 		return -EINVAL;
329db3d8338SChris Wilson 
330db3d8338SChris Wilson 	WRITE_ONCE(engine->props.preempt_timeout_ms, timeout);
331db3d8338SChris Wilson 
332db3d8338SChris Wilson 	if (READ_ONCE(engine->execlists.pending[0]))
333db3d8338SChris Wilson 		set_timer_ms(&engine->execlists.preempt, timeout);
334db3d8338SChris Wilson 
335db3d8338SChris Wilson 	return count;
336db3d8338SChris Wilson }
337db3d8338SChris Wilson 
338db3d8338SChris Wilson static ssize_t
preempt_timeout_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)339db3d8338SChris Wilson preempt_timeout_show(struct kobject *kobj, struct kobj_attribute *attr,
340db3d8338SChris Wilson 		     char *buf)
341db3d8338SChris Wilson {
342db3d8338SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
343db3d8338SChris Wilson 
3441f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->props.preempt_timeout_ms);
345db3d8338SChris Wilson }
346db3d8338SChris Wilson 
347*d2a9692aSJani Nikula static const struct kobj_attribute preempt_timeout_attr =
348db3d8338SChris Wilson __ATTR(preempt_timeout_ms, 0644, preempt_timeout_show, preempt_timeout_store);
349db3d8338SChris Wilson 
3509a40bdddSChris Wilson static ssize_t
preempt_timeout_default(struct kobject * kobj,struct kobj_attribute * attr,char * buf)3517a0ba6b4SChris Wilson preempt_timeout_default(struct kobject *kobj, struct kobj_attribute *attr,
3527a0ba6b4SChris Wilson 			char *buf)
3537a0ba6b4SChris Wilson {
3547a0ba6b4SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
3557a0ba6b4SChris Wilson 
3561f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->defaults.preempt_timeout_ms);
3577a0ba6b4SChris Wilson }
3587a0ba6b4SChris Wilson 
359*d2a9692aSJani Nikula static const struct kobj_attribute preempt_timeout_def =
3607a0ba6b4SChris Wilson __ATTR(preempt_timeout_ms, 0444, preempt_timeout_default, NULL);
3617a0ba6b4SChris Wilson 
3627a0ba6b4SChris Wilson static ssize_t
heartbeat_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3639a40bdddSChris Wilson heartbeat_store(struct kobject *kobj, struct kobj_attribute *attr,
3649a40bdddSChris Wilson 		const char *buf, size_t count)
3659a40bdddSChris Wilson {
3669a40bdddSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
367568944afSJohn Harrison 	unsigned long long delay, clamped;
3689a40bdddSChris Wilson 	int err;
3699a40bdddSChris Wilson 
3709a40bdddSChris Wilson 	/*
3719a40bdddSChris Wilson 	 * We monitor the health of the system via periodic heartbeat pulses.
3729a40bdddSChris Wilson 	 * The pulses also provide the opportunity to perform garbage
3739a40bdddSChris Wilson 	 * collection.  However, we interpret an incomplete pulse (a missed
3749a40bdddSChris Wilson 	 * heartbeat) as an indication that the system is no longer responsive,
3759a40bdddSChris Wilson 	 * i.e. hung, and perform an engine or full GPU reset. Given that the
3769a40bdddSChris Wilson 	 * preemption granularity can be very coarse on a system, the optimal
3779a40bdddSChris Wilson 	 * value for any workload is unknowable!
3789a40bdddSChris Wilson 	 */
3799a40bdddSChris Wilson 
3809a40bdddSChris Wilson 	err = kstrtoull(buf, 0, &delay);
3819a40bdddSChris Wilson 	if (err)
3829a40bdddSChris Wilson 		return err;
3839a40bdddSChris Wilson 
384568944afSJohn Harrison 	clamped = intel_clamp_heartbeat_interval_ms(engine, delay);
385568944afSJohn Harrison 	if (delay != clamped)
3869a40bdddSChris Wilson 		return -EINVAL;
3879a40bdddSChris Wilson 
3889a40bdddSChris Wilson 	err = intel_engine_set_heartbeat(engine, delay);
3899a40bdddSChris Wilson 	if (err)
3909a40bdddSChris Wilson 		return err;
3919a40bdddSChris Wilson 
3929a40bdddSChris Wilson 	return count;
3939a40bdddSChris Wilson }
3949a40bdddSChris Wilson 
3959a40bdddSChris Wilson static ssize_t
heartbeat_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)3969a40bdddSChris Wilson heartbeat_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
3979a40bdddSChris Wilson {
3989a40bdddSChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
3999a40bdddSChris Wilson 
4001f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->props.heartbeat_interval_ms);
4019a40bdddSChris Wilson }
4029a40bdddSChris Wilson 
403*d2a9692aSJani Nikula static const struct kobj_attribute heartbeat_interval_attr =
4049a40bdddSChris Wilson __ATTR(heartbeat_interval_ms, 0644, heartbeat_show, heartbeat_store);
4059a40bdddSChris Wilson 
4067a0ba6b4SChris Wilson static ssize_t
heartbeat_default(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4077a0ba6b4SChris Wilson heartbeat_default(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
4087a0ba6b4SChris Wilson {
4097a0ba6b4SChris Wilson 	struct intel_engine_cs *engine = kobj_to_engine(kobj);
4107a0ba6b4SChris Wilson 
4111f16fdbbSNirmoy Das 	return sysfs_emit(buf, "%lu\n", engine->defaults.heartbeat_interval_ms);
4127a0ba6b4SChris Wilson }
4137a0ba6b4SChris Wilson 
414*d2a9692aSJani Nikula static const struct kobj_attribute heartbeat_interval_def =
4157a0ba6b4SChris Wilson __ATTR(heartbeat_interval_ms, 0444, heartbeat_default, NULL);
4167a0ba6b4SChris Wilson 
kobj_engine_release(struct kobject * kobj)4174ec76dbeSChris Wilson static void kobj_engine_release(struct kobject *kobj)
4184ec76dbeSChris Wilson {
4194ec76dbeSChris Wilson 	kfree(kobj);
4204ec76dbeSChris Wilson }
4214ec76dbeSChris Wilson 
42201361096SThomas Weißschuh static const struct kobj_type kobj_engine_type = {
4234ec76dbeSChris Wilson 	.release = kobj_engine_release,
4244ec76dbeSChris Wilson 	.sysfs_ops = &kobj_sysfs_ops
4254ec76dbeSChris Wilson };
4264ec76dbeSChris Wilson 
4274ec76dbeSChris Wilson static struct kobject *
kobj_engine(struct kobject * dir,struct intel_engine_cs * engine)4284ec76dbeSChris Wilson kobj_engine(struct kobject *dir, struct intel_engine_cs *engine)
4294ec76dbeSChris Wilson {
4304ec76dbeSChris Wilson 	struct kobj_engine *ke;
4314ec76dbeSChris Wilson 
4324ec76dbeSChris Wilson 	ke = kzalloc(sizeof(*ke), GFP_KERNEL);
4334ec76dbeSChris Wilson 	if (!ke)
4344ec76dbeSChris Wilson 		return NULL;
4354ec76dbeSChris Wilson 
4364ec76dbeSChris Wilson 	kobject_init(&ke->base, &kobj_engine_type);
4374ec76dbeSChris Wilson 	ke->engine = engine;
4384ec76dbeSChris Wilson 
4394ec76dbeSChris Wilson 	if (kobject_add(&ke->base, dir, "%s", engine->name)) {
4404ec76dbeSChris Wilson 		kobject_put(&ke->base);
4414ec76dbeSChris Wilson 		return NULL;
4424ec76dbeSChris Wilson 	}
4434ec76dbeSChris Wilson 
4444ec76dbeSChris Wilson 	/* xfer ownership to sysfs tree */
4454ec76dbeSChris Wilson 	return &ke->base;
4464ec76dbeSChris Wilson }
4474ec76dbeSChris Wilson 
add_defaults(struct kobj_engine * parent)4487a0ba6b4SChris Wilson static void add_defaults(struct kobj_engine *parent)
4497a0ba6b4SChris Wilson {
450*d2a9692aSJani Nikula 	static const struct attribute * const files[] = {
4517a0ba6b4SChris Wilson 		&max_spin_def.attr,
4527a0ba6b4SChris Wilson 		&stop_timeout_def.attr,
4537a0ba6b4SChris Wilson #if CONFIG_DRM_I915_HEARTBEAT_INTERVAL
4547a0ba6b4SChris Wilson 		&heartbeat_interval_def.attr,
4557a0ba6b4SChris Wilson #endif
4567a0ba6b4SChris Wilson 		NULL
4577a0ba6b4SChris Wilson 	};
4587a0ba6b4SChris Wilson 	struct kobj_engine *ke;
4597a0ba6b4SChris Wilson 
4607a0ba6b4SChris Wilson 	ke = kzalloc(sizeof(*ke), GFP_KERNEL);
4617a0ba6b4SChris Wilson 	if (!ke)
4627a0ba6b4SChris Wilson 		return;
4637a0ba6b4SChris Wilson 
4647a0ba6b4SChris Wilson 	kobject_init(&ke->base, &kobj_engine_type);
4657a0ba6b4SChris Wilson 	ke->engine = parent->engine;
4667a0ba6b4SChris Wilson 
4677a0ba6b4SChris Wilson 	if (kobject_add(&ke->base, &parent->base, "%s", ".defaults")) {
4687a0ba6b4SChris Wilson 		kobject_put(&ke->base);
4697a0ba6b4SChris Wilson 		return;
4707a0ba6b4SChris Wilson 	}
4717a0ba6b4SChris Wilson 
4727a0ba6b4SChris Wilson 	if (sysfs_create_files(&ke->base, files))
4737a0ba6b4SChris Wilson 		return;
4747a0ba6b4SChris Wilson 
4757a0ba6b4SChris Wilson 	if (intel_engine_has_timeslices(ke->engine) &&
4767a0ba6b4SChris Wilson 	    sysfs_create_file(&ke->base, &timeslice_duration_def.attr))
4777a0ba6b4SChris Wilson 		return;
4787a0ba6b4SChris Wilson 
4797a0ba6b4SChris Wilson 	if (intel_engine_has_preempt_reset(ke->engine) &&
4807a0ba6b4SChris Wilson 	    sysfs_create_file(&ke->base, &preempt_timeout_def.attr))
4817a0ba6b4SChris Wilson 		return;
4827a0ba6b4SChris Wilson }
4837a0ba6b4SChris Wilson 
intel_engines_add_sysfs(struct drm_i915_private * i915)4844ec76dbeSChris Wilson void intel_engines_add_sysfs(struct drm_i915_private *i915)
4854ec76dbeSChris Wilson {
486*d2a9692aSJani Nikula 	static const struct attribute * const files[] = {
4874ec76dbeSChris Wilson 		&name_attr.attr,
4884ec76dbeSChris Wilson 		&class_attr.attr,
4894ec76dbeSChris Wilson 		&inst_attr.attr,
4906e57cc39SChris Wilson 		&mmio_attr.attr,
4914ec76dbeSChris Wilson 		&caps_attr.attr,
4924ec76dbeSChris Wilson 		&all_caps_attr.attr,
493062444bbSChris Wilson 		&max_spin_attr.attr,
49472338a1fSChris Wilson 		&stop_timeout_attr.attr,
4959a40bdddSChris Wilson #if CONFIG_DRM_I915_HEARTBEAT_INTERVAL
4969a40bdddSChris Wilson 		&heartbeat_interval_attr.attr,
4979a40bdddSChris Wilson #endif
4984ec76dbeSChris Wilson 		NULL
4994ec76dbeSChris Wilson 	};
5004ec76dbeSChris Wilson 
5014ec76dbeSChris Wilson 	struct device *kdev = i915->drm.primary->kdev;
5024ec76dbeSChris Wilson 	struct intel_engine_cs *engine;
5034ec76dbeSChris Wilson 	struct kobject *dir;
5044ec76dbeSChris Wilson 
5054ec76dbeSChris Wilson 	dir = kobject_create_and_add("engine", &kdev->kobj);
5064ec76dbeSChris Wilson 	if (!dir)
5074ec76dbeSChris Wilson 		return;
5084ec76dbeSChris Wilson 
5094ec76dbeSChris Wilson 	for_each_uabi_engine(engine, i915) {
5104ec76dbeSChris Wilson 		struct kobject *kobj;
5114ec76dbeSChris Wilson 
5124ec76dbeSChris Wilson 		kobj = kobj_engine(dir, engine);
5134ec76dbeSChris Wilson 		if (!kobj)
5144ec76dbeSChris Wilson 			goto err_engine;
5154ec76dbeSChris Wilson 
5164ec76dbeSChris Wilson 		if (sysfs_create_files(kobj, files))
5174ec76dbeSChris Wilson 			goto err_object;
5184ec76dbeSChris Wilson 
5191a2695a7SChris Wilson 		if (intel_engine_has_timeslices(engine) &&
5201a2695a7SChris Wilson 		    sysfs_create_file(kobj, &timeslice_duration_attr.attr))
5211a2695a7SChris Wilson 			goto err_engine;
5221a2695a7SChris Wilson 
523db3d8338SChris Wilson 		if (intel_engine_has_preempt_reset(engine) &&
524db3d8338SChris Wilson 		    sysfs_create_file(kobj, &preempt_timeout_attr.attr))
525db3d8338SChris Wilson 			goto err_engine;
526db3d8338SChris Wilson 
5277a0ba6b4SChris Wilson 		add_defaults(container_of(kobj, struct kobj_engine, base));
5287a0ba6b4SChris Wilson 
5294ec76dbeSChris Wilson 		if (0) {
5304ec76dbeSChris Wilson err_object:
5314ec76dbeSChris Wilson 			kobject_put(kobj);
5324ec76dbeSChris Wilson err_engine:
5334ec76dbeSChris Wilson 			dev_err(kdev, "Failed to add sysfs engine '%s'\n",
5344ec76dbeSChris Wilson 				engine->name);
5354ec76dbeSChris Wilson 			break;
5364ec76dbeSChris Wilson 		}
5374ec76dbeSChris Wilson 	}
5384ec76dbeSChris Wilson }
539