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, ×lice_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, ×lice_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