1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2019 Intel Corporation
4 */
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_t
25name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
26{
27 return sysfs_emit(buf, fmt: "%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_t
34class_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
35{
36 return sysfs_emit(buf, fmt: "%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_t
43inst_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
44{
45 return sysfs_emit(buf, fmt: "%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_t
52mmio_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
53{
54 return sysfs_emit(buf, fmt: "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_t
81__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, at: len, fmt: "[%x] ", n);
111 } else {
112 len += sysfs_emit_at(buf, at: len, fmt: "%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_t
121caps_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, caps: engine->uabi_capabilities, buf, show_unknown: true);
126}
127
128static const struct kobj_attribute caps_attr =
129__ATTR(capabilities, 0444, caps_show, NULL);
130
131static ssize_t
132all_caps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
133{
134 return __caps_show(engine: kobj_to_engine(kobj), caps: -1, buf, show_unknown: false);
135}
136
137static const struct kobj_attribute all_caps_attr =
138__ATTR(known_capabilities, 0444, all_caps_show, NULL);
139
140static ssize_t
141max_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 executed
150 * on the GPU, we busywait for a short while before sleeping. The
151 * premise is that most requests are short, and if it is already
152 * executing then there is a good chance that it will complete
153 * before we can setup the interrupt handler and go to sleep.
154 * We try to offset the cost of going to sleep, by first spinning
155 * on the request -- if it completed in less time than it would take
156 * to go sleep, process the interrupt and return back to the client,
157 * then we have saved the client some latency, albeit at the cost
158 * of spinning on an expensive CPU core.
159 *
160 * While we try to avoid waiting at all for a request that is unlikely
161 * to complete, deciding how long it is worth spinning is for is an
162 * arbitrary decision: trading off power vs latency.
163 */
164
165 err = kstrtoull(s: buf, base: 0, res: &duration);
166 if (err)
167 return err;
168
169 clamped = intel_clamp_max_busywait_duration_ns(engine, value: 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_t
179max_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, fmt: "%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_t
190max_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, fmt: "%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_t
201timeslice_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 alternate
210 * execution between ready-to-run contexts of equal priority. This
211 * ensures that all users (though only if they of equal importance)
212 * have the opportunity to run and prevents livelocks where contexts
213 * may have implicit ordering due to userspace semaphores.
214 */
215
216 err = kstrtoull(s: buf, base: 0, res: &duration);
217 if (err)
218 return err;
219
220 clamped = intel_clamp_timeslice_duration_ms(engine, value: duration);
221 if (duration != clamped)
222 return -EINVAL;
223
224 WRITE_ONCE(engine->props.timeslice_duration_ms, duration);
225
226 if (execlists_active(execlists: &engine->execlists))
227 set_timer_ms(t: &engine->execlists.timer, timeout: duration);
228
229 return count;
230}
231
232static ssize_t
233timeslice_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, fmt: "%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_t
244timeslice_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, fmt: "%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_t
255stop_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 disabling
264 * submission, even for a few milliseconds, gives an innocent context
265 * the opportunity to clear the GPU before the reset occurs. However,
266 * how long to sleep depends on the typical non-preemptible duration
267 * (a similar problem to determining the ideal preempt-reset timeout
268 * or even the heartbeat interval).
269 */
270
271 err = kstrtoull(s: buf, base: 0, res: &duration);
272 if (err)
273 return err;
274
275 clamped = intel_clamp_stop_timeout_ms(engine, value: 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_t
284stop_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, fmt: "%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_t
295stop_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, fmt: "%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_t
306preempt_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 current
315 * resident a small amount of time to vacate the GPU. The preemption
316 * request is for a higher priority context and should be immediate to
317 * maintain high quality of service (and avoid priority inversion).
318 * However, the preemption granularity of the GPU can be quite coarse
319 * and so we need a compromise.
320 */
321
322 err = kstrtoull(s: buf, base: 0, res: &timeout);
323 if (err)
324 return err;
325
326 clamped = intel_clamp_preempt_timeout_ms(engine, value: 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(t: &engine->execlists.preempt, timeout);
334
335 return count;
336}
337
338static ssize_t
339preempt_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, fmt: "%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_t
351preempt_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, fmt: "%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_t
363heartbeat_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 garbage
373 * collection. However, we interpret an incomplete pulse (a missed
374 * 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 the
376 * preemption granularity can be very coarse on a system, the optimal
377 * value for any workload is unknowable!
378 */
379
380 err = kstrtoull(s: buf, base: 0, res: &delay);
381 if (err)
382 return err;
383
384 clamped = intel_clamp_heartbeat_interval_ms(engine, value: 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_t
396heartbeat_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, fmt: "%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_t
407heartbeat_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, fmt: "%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(objp: kobj);
420}
421
422static const struct kobj_type kobj_engine_type = {
423 .release = kobj_engine_release,
424 .sysfs_ops = &kobj_sysfs_ops
425};
426
427static struct kobject *
428kobj_engine(struct kobject *dir, struct intel_engine_cs *engine)
429{
430 struct kobj_engine *ke;
431
432 ke = kzalloc(size: sizeof(*ke), GFP_KERNEL);
433 if (!ke)
434 return NULL;
435
436 kobject_init(kobj: &ke->base, ktype: &kobj_engine_type);
437 ke->engine = engine;
438
439 if (kobject_add(kobj: &ke->base, parent: dir, fmt: "%s", engine->name)) {
440 kobject_put(kobj: &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_INTERVAL
454 &heartbeat_interval_def.attr,
455#endif
456 NULL
457 };
458 struct kobj_engine *ke;
459
460 ke = kzalloc(size: sizeof(*ke), GFP_KERNEL);
461 if (!ke)
462 return;
463
464 kobject_init(kobj: &ke->base, ktype: &kobj_engine_type);
465 ke->engine = parent->engine;
466
467 if (kobject_add(kobj: &ke->base, parent: &parent->base, fmt: "%s", ".defaults")) {
468 kobject_put(kobj: &ke->base);
469 return;
470 }
471
472 if (sysfs_create_files(kobj: &ke->base, attr: files))
473 return;
474
475 if (intel_engine_has_timeslices(engine: ke->engine) &&
476 sysfs_create_file(kobj: &ke->base, attr: &timeslice_duration_def.attr))
477 return;
478
479 if (intel_engine_has_preempt_reset(engine: ke->engine) &&
480 sysfs_create_file(kobj: &ke->base, attr: &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_INTERVAL
496 &heartbeat_interval_attr.attr,
497#endif
498 NULL
499 };
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(name: "engine", parent: &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, attr: files))
517 goto err_object;
518
519 if (intel_engine_has_timeslices(engine) &&
520 sysfs_create_file(kobj, attr: &timeslice_duration_attr.attr))
521 goto err_engine;
522
523 if (intel_engine_has_preempt_reset(engine) &&
524 sysfs_create_file(kobj, attr: &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_err(kdev, "Failed to add sysfs engine '%s'\n",
534 engine->name);
535 break;
536 }
537 }
538}
539

source code of linux/drivers/gpu/drm/i915/gt/sysfs_engines.c