1// SPDX-License-Identifier: MIT
2/*
3 * Copyright �� 2023 Intel Corporation
4 */
5
6#include <drm/drm_managed.h>
7#include <linux/kobject.h>
8#include <linux/sysfs.h>
9
10#include "xe_gt.h"
11#include "xe_hw_engine_class_sysfs.h"
12
13#define MAX_ENGINE_CLASS_NAME_LEN    16
14static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
15					   struct kobject *parent);
16
17/**
18 * xe_hw_engine_timeout_in_range - Helper to check if timeout is in range
19 * @timeout: timeout to validate
20 * @min: min value of valid range
21 * @max: max value of valid range
22 *
23 * This helper helps to validate if timeout is in min-max range of HW engine
24 * scheduler.
25 *
26 * Returns: Returns false value for failure and true for success.
27 */
28bool xe_hw_engine_timeout_in_range(u64 timeout, u64 min, u64 max)
29{
30	return timeout >= min && timeout <= max;
31}
32
33static void kobj_xe_hw_engine_release(struct kobject *kobj)
34{
35	kfree(kobj);
36}
37
38static const struct kobj_type kobj_xe_hw_engine_type = {
39	.release = kobj_xe_hw_engine_release,
40	.sysfs_ops = &kobj_sysfs_ops
41};
42
43static ssize_t job_timeout_max_store(struct kobject *kobj,
44				     struct kobj_attribute *attr,
45				     const char *buf, size_t count)
46{
47	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
48	u32 timeout;
49	int err;
50
51	err = kstrtou32(buf, 0, &timeout);
52	if (err)
53		return err;
54
55	if (timeout < eclass->sched_props.job_timeout_min)
56		return -EINVAL;
57
58	if (!xe_hw_engine_timeout_in_range(timeout,
59					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
60					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
61		return -EINVAL;
62
63	WRITE_ONCE(eclass->sched_props.job_timeout_max, timeout);
64
65	return count;
66}
67
68static ssize_t job_timeout_max_show(struct kobject *kobj,
69				    struct kobj_attribute *attr, char *buf)
70{
71	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
72
73	return sprintf(buf, "%u\n", eclass->sched_props.job_timeout_max);
74}
75
76static const struct kobj_attribute job_timeout_max_attr =
77__ATTR(job_timeout_max, 0644, job_timeout_max_show, job_timeout_max_store);
78
79static ssize_t job_timeout_min_store(struct kobject *kobj,
80				     struct kobj_attribute *attr,
81				     const char *buf, size_t count)
82{
83	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
84	u32 timeout;
85	int err;
86
87	err = kstrtou32(buf, 0, &timeout);
88	if (err)
89		return err;
90
91	if (timeout > eclass->sched_props.job_timeout_max)
92		return -EINVAL;
93
94	if (!xe_hw_engine_timeout_in_range(timeout,
95					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
96					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
97		return -EINVAL;
98
99	WRITE_ONCE(eclass->sched_props.job_timeout_min, timeout);
100
101	return count;
102}
103
104static ssize_t job_timeout_min_show(struct kobject *kobj,
105				    struct kobj_attribute *attr, char *buf)
106{
107	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
108
109	return sprintf(buf, "%u\n", eclass->sched_props.job_timeout_min);
110}
111
112static const struct kobj_attribute job_timeout_min_attr =
113__ATTR(job_timeout_min, 0644, job_timeout_min_show, job_timeout_min_store);
114
115static ssize_t job_timeout_store(struct kobject *kobj,
116				 struct kobj_attribute *attr,
117				 const char *buf, size_t count)
118{
119	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
120	u32 min = eclass->sched_props.job_timeout_min;
121	u32 max = eclass->sched_props.job_timeout_max;
122	u32 timeout;
123	int err;
124
125	err = kstrtou32(buf, 0, &timeout);
126	if (err)
127		return err;
128
129	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
130		return -EINVAL;
131
132	WRITE_ONCE(eclass->sched_props.job_timeout_ms, timeout);
133
134	return count;
135}
136
137static ssize_t job_timeout_show(struct kobject *kobj,
138				struct kobj_attribute *attr, char *buf)
139{
140	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
141
142	return sprintf(buf, "%u\n", eclass->sched_props.job_timeout_ms);
143}
144
145static const struct kobj_attribute job_timeout_attr =
146__ATTR(job_timeout_ms, 0644, job_timeout_show, job_timeout_store);
147
148static ssize_t job_timeout_default(struct kobject *kobj,
149				   struct kobj_attribute *attr, char *buf)
150{
151	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
152
153	return sprintf(buf, "%u\n", eclass->defaults.job_timeout_ms);
154}
155
156static const struct kobj_attribute job_timeout_def =
157__ATTR(job_timeout_ms, 0444, job_timeout_default, NULL);
158
159static ssize_t job_timeout_min_default(struct kobject *kobj,
160				       struct kobj_attribute *attr, char *buf)
161{
162	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
163
164	return sprintf(buf, "%u\n", eclass->defaults.job_timeout_min);
165}
166
167static const struct kobj_attribute job_timeout_min_def =
168__ATTR(job_timeout_min, 0444, job_timeout_min_default, NULL);
169
170static ssize_t job_timeout_max_default(struct kobject *kobj,
171				       struct kobj_attribute *attr, char *buf)
172{
173	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
174
175	return sprintf(buf, "%u\n", eclass->defaults.job_timeout_max);
176}
177
178static const struct kobj_attribute job_timeout_max_def =
179__ATTR(job_timeout_max, 0444, job_timeout_max_default, NULL);
180
181static ssize_t timeslice_duration_store(struct kobject *kobj,
182					struct kobj_attribute *attr,
183					const char *buf, size_t count)
184{
185	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
186	u32 min = eclass->sched_props.timeslice_min;
187	u32 max = eclass->sched_props.timeslice_max;
188	u32 duration;
189	int err;
190
191	err = kstrtou32(buf, 0, &duration);
192	if (err)
193		return err;
194
195	if (!xe_hw_engine_timeout_in_range(duration, min, max))
196		return -EINVAL;
197
198	WRITE_ONCE(eclass->sched_props.timeslice_us, duration);
199
200	return count;
201}
202
203static ssize_t timeslice_duration_max_store(struct kobject *kobj,
204					    struct kobj_attribute *attr,
205					    const char *buf, size_t count)
206{
207	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
208	u32 duration;
209	int err;
210
211	err = kstrtou32(buf, 0, &duration);
212	if (err)
213		return err;
214
215	if (duration < eclass->sched_props.timeslice_min)
216		return -EINVAL;
217
218	if (!xe_hw_engine_timeout_in_range(duration,
219					   XE_HW_ENGINE_TIMESLICE_MIN,
220					   XE_HW_ENGINE_TIMESLICE_MAX))
221		return -EINVAL;
222
223	WRITE_ONCE(eclass->sched_props.timeslice_max, duration);
224
225	return count;
226}
227
228static ssize_t timeslice_duration_max_show(struct kobject *kobj,
229					   struct kobj_attribute *attr,
230					   char *buf)
231{
232	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
233
234	return sprintf(buf, "%u\n", eclass->sched_props.timeslice_max);
235}
236
237static const struct kobj_attribute timeslice_duration_max_attr =
238	__ATTR(timeslice_duration_max, 0644, timeslice_duration_max_show,
239	       timeslice_duration_max_store);
240
241static ssize_t timeslice_duration_min_store(struct kobject *kobj,
242					    struct kobj_attribute *attr,
243					    const char *buf, size_t count)
244{
245	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
246	u32 duration;
247	int err;
248
249	err = kstrtou32(buf, 0, &duration);
250	if (err)
251		return err;
252
253	if (duration > eclass->sched_props.timeslice_max)
254		return -EINVAL;
255
256	if (!xe_hw_engine_timeout_in_range(duration,
257					   XE_HW_ENGINE_TIMESLICE_MIN,
258					   XE_HW_ENGINE_TIMESLICE_MAX))
259		return -EINVAL;
260
261	WRITE_ONCE(eclass->sched_props.timeslice_min, duration);
262
263	return count;
264}
265
266static ssize_t timeslice_duration_min_show(struct kobject *kobj,
267					   struct kobj_attribute *attr,
268					   char *buf)
269{
270	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
271
272	return sprintf(buf, "%u\n", eclass->sched_props.timeslice_min);
273}
274
275static const struct kobj_attribute timeslice_duration_min_attr =
276	__ATTR(timeslice_duration_min, 0644, timeslice_duration_min_show,
277	       timeslice_duration_min_store);
278
279static ssize_t timeslice_duration_show(struct kobject *kobj,
280				       struct kobj_attribute *attr, char *buf)
281{
282	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
283
284	return sprintf(buf, "%u\n", eclass->sched_props.timeslice_us);
285}
286
287static const struct kobj_attribute timeslice_duration_attr =
288	__ATTR(timeslice_duration_us, 0644, timeslice_duration_show,
289	       timeslice_duration_store);
290
291static ssize_t timeslice_default(struct kobject *kobj,
292				 struct kobj_attribute *attr, char *buf)
293{
294	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
295
296	return sprintf(buf, "%u\n", eclass->defaults.timeslice_us);
297}
298
299static const struct kobj_attribute timeslice_duration_def =
300__ATTR(timeslice_duration_us, 0444, timeslice_default, NULL);
301
302static ssize_t timeslice_min_default(struct kobject *kobj,
303				     struct kobj_attribute *attr, char *buf)
304{
305	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
306
307	return sprintf(buf, "%u\n", eclass->defaults.timeslice_min);
308}
309
310static const struct kobj_attribute timeslice_duration_min_def =
311__ATTR(timeslice_duration_min, 0444, timeslice_min_default, NULL);
312
313static ssize_t timeslice_max_default(struct kobject *kobj,
314				     struct kobj_attribute *attr, char *buf)
315{
316	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
317
318	return sprintf(buf, "%u\n", eclass->defaults.timeslice_max);
319}
320
321static const struct kobj_attribute timeslice_duration_max_def =
322__ATTR(timeslice_duration_max, 0444, timeslice_max_default, NULL);
323
324static ssize_t preempt_timeout_store(struct kobject *kobj,
325				     struct kobj_attribute *attr,
326				     const char *buf, size_t count)
327{
328	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
329	u32 min = eclass->sched_props.preempt_timeout_min;
330	u32 max = eclass->sched_props.preempt_timeout_max;
331	u32 timeout;
332	int err;
333
334	err = kstrtou32(buf, 0, &timeout);
335	if (err)
336		return err;
337
338	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
339		return -EINVAL;
340
341	WRITE_ONCE(eclass->sched_props.preempt_timeout_us, timeout);
342
343	return count;
344}
345
346static ssize_t preempt_timeout_show(struct kobject *kobj,
347				    struct kobj_attribute *attr, char *buf)
348{
349	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
350
351	return sprintf(buf, "%u\n", eclass->sched_props.preempt_timeout_us);
352}
353
354static const struct kobj_attribute preempt_timeout_attr =
355__ATTR(preempt_timeout_us, 0644, preempt_timeout_show, preempt_timeout_store);
356
357static ssize_t preempt_timeout_default(struct kobject *kobj,
358				       struct kobj_attribute *attr,
359				       char *buf)
360{
361	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
362
363	return sprintf(buf, "%u\n", eclass->defaults.preempt_timeout_us);
364}
365
366static const struct kobj_attribute preempt_timeout_def =
367__ATTR(preempt_timeout_us, 0444, preempt_timeout_default, NULL);
368
369static ssize_t preempt_timeout_min_default(struct kobject *kobj,
370					   struct kobj_attribute *attr,
371					   char *buf)
372{
373	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
374
375	return sprintf(buf, "%u\n", eclass->defaults.preempt_timeout_min);
376}
377
378static const struct kobj_attribute preempt_timeout_min_def =
379__ATTR(preempt_timeout_min, 0444, preempt_timeout_min_default, NULL);
380
381static ssize_t preempt_timeout_max_default(struct kobject *kobj,
382					   struct kobj_attribute *attr,
383					   char *buf)
384{
385	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);
386
387	return sprintf(buf, "%u\n", eclass->defaults.preempt_timeout_max);
388}
389
390static const struct kobj_attribute preempt_timeout_max_def =
391__ATTR(preempt_timeout_max, 0444, preempt_timeout_max_default, NULL);
392
393static ssize_t preempt_timeout_max_store(struct kobject *kobj,
394					 struct kobj_attribute *attr,
395					 const char *buf, size_t count)
396{
397	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
398	u32 timeout;
399	int err;
400
401	err = kstrtou32(buf, 0, &timeout);
402	if (err)
403		return err;
404
405	if (timeout < eclass->sched_props.preempt_timeout_min)
406		return -EINVAL;
407
408	if (!xe_hw_engine_timeout_in_range(timeout,
409					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
410					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
411		return -EINVAL;
412
413	WRITE_ONCE(eclass->sched_props.preempt_timeout_max, timeout);
414
415	return count;
416}
417
418static ssize_t preempt_timeout_max_show(struct kobject *kobj,
419					struct kobj_attribute *attr, char *buf)
420{
421	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
422
423	return sprintf(buf, "%u\n", eclass->sched_props.preempt_timeout_max);
424}
425
426static const struct kobj_attribute preempt_timeout_max_attr =
427	__ATTR(preempt_timeout_max, 0644, preempt_timeout_max_show,
428	       preempt_timeout_max_store);
429
430static ssize_t preempt_timeout_min_store(struct kobject *kobj,
431					 struct kobj_attribute *attr,
432					 const char *buf, size_t count)
433{
434	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
435	u32 timeout;
436	int err;
437
438	err = kstrtou32(buf, 0, &timeout);
439	if (err)
440		return err;
441
442	if (timeout > eclass->sched_props.preempt_timeout_max)
443		return -EINVAL;
444
445	if (!xe_hw_engine_timeout_in_range(timeout,
446					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
447					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
448		return -EINVAL;
449
450	WRITE_ONCE(eclass->sched_props.preempt_timeout_min, timeout);
451
452	return count;
453}
454
455static ssize_t preempt_timeout_min_show(struct kobject *kobj,
456					struct kobj_attribute *attr, char *buf)
457{
458	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
459
460	return sprintf(buf, "%u\n", eclass->sched_props.preempt_timeout_min);
461}
462
463static const struct kobj_attribute preempt_timeout_min_attr =
464	__ATTR(preempt_timeout_min, 0644, preempt_timeout_min_show,
465	       preempt_timeout_min_store);
466
467static const struct attribute *defaults[] = {
468	&job_timeout_def.attr,
469	&job_timeout_min_def.attr,
470	&job_timeout_max_def.attr,
471	&timeslice_duration_def.attr,
472	&timeslice_duration_min_def.attr,
473	&timeslice_duration_max_def.attr,
474	&preempt_timeout_def.attr,
475	&preempt_timeout_min_def.attr,
476	&preempt_timeout_max_def.attr,
477	NULL
478};
479
480static const struct attribute * const files[] = {
481	&job_timeout_attr.attr,
482	&job_timeout_min_attr.attr,
483	&job_timeout_max_attr.attr,
484	&timeslice_duration_attr.attr,
485	&timeslice_duration_min_attr.attr,
486	&timeslice_duration_max_attr.attr,
487	&preempt_timeout_attr.attr,
488	&preempt_timeout_min_attr.attr,
489	&preempt_timeout_max_attr.attr,
490	NULL
491};
492
493static void kobj_xe_hw_engine_class_fini(struct drm_device *drm, void *arg)
494{
495	struct kobject *kobj = arg;
496
497	sysfs_remove_files(kobj, files);
498	kobject_put(kobj);
499}
500
501	static struct kobj_eclass *
502kobj_xe_hw_engine_class(struct xe_device *xe, struct kobject *parent, char *name)
503{
504	struct kobj_eclass *keclass;
505	int err = 0;
506
507	keclass = kzalloc(sizeof(*keclass), GFP_KERNEL);
508	if (!keclass)
509		return NULL;
510
511	kobject_init(&keclass->base, &kobj_xe_hw_engine_type);
512	if (kobject_add(&keclass->base, parent, "%s", name)) {
513		kobject_put(&keclass->base);
514		return NULL;
515	}
516
517	err = drmm_add_action_or_reset(&xe->drm, kobj_xe_hw_engine_class_fini,
518				       &keclass->base);
519	if (err)
520		drm_warn(&xe->drm,
521			 "%s: drmm_add_action_or_reset failed, err: %d\n",
522			 __func__, err);
523	return keclass;
524}
525
526static void hw_engine_class_defaults_fini(struct drm_device *drm, void *arg)
527{
528	struct kobject *kobj = arg;
529
530	sysfs_remove_files(kobj, defaults);
531	kobject_put(kobj);
532}
533
534static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
535					   struct kobject *parent)
536{
537	struct kobject *kobj;
538	int err = 0;
539
540	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
541	if (!kobj)
542		return -ENOMEM;
543
544	kobject_init(kobj, &kobj_xe_hw_engine_type);
545	err = kobject_add(kobj, parent, "%s", ".defaults");
546	if (err)
547		goto err_object;
548
549	err = sysfs_create_files(kobj, defaults);
550	if (err)
551		goto err_object;
552
553	err = drmm_add_action_or_reset(&xe->drm, hw_engine_class_defaults_fini,
554				       kobj);
555	if (err)
556		drm_warn(&xe->drm,
557			 "%s: drmm_add_action_or_reset failed, err: %d\n",
558			 __func__, err);
559	return err;
560err_object:
561	kobject_put(kobj);
562	return err;
563}
564
565static void xe_hw_engine_sysfs_kobj_release(struct kobject *kobj)
566{
567	kfree(kobj);
568}
569
570static const struct kobj_type xe_hw_engine_sysfs_kobj_type = {
571	.release = xe_hw_engine_sysfs_kobj_release,
572	.sysfs_ops = &kobj_sysfs_ops,
573};
574
575static void hw_engine_class_sysfs_fini(struct drm_device *drm, void *arg)
576{
577	struct kobject *kobj = arg;
578
579	kobject_put(kobj);
580}
581
582/**
583 * xe_hw_engine_class_sysfs_init - Init HW engine classes on GT.
584 * @gt: Xe GT.
585 *
586 * This routine creates sysfs for HW engine classes and adds methods
587 * to get/set different scheduling properties for HW engines class.
588 *
589 * Returns: Returns error value for failure and 0 for success.
590 */
591int xe_hw_engine_class_sysfs_init(struct xe_gt *gt)
592{
593	struct xe_device *xe = gt_to_xe(gt);
594	struct xe_hw_engine *hwe;
595	enum xe_hw_engine_id id;
596	struct kobject *kobj;
597	u16 class_mask = 0;
598	int err = 0;
599
600	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
601	if (!kobj)
602		return -ENOMEM;
603
604	kobject_init(kobj, &xe_hw_engine_sysfs_kobj_type);
605
606	err = kobject_add(kobj, gt->sysfs, "engines");
607	if (err)
608		goto err_object;
609
610	for_each_hw_engine(hwe, gt, id) {
611		char name[MAX_ENGINE_CLASS_NAME_LEN];
612		struct kobj_eclass *keclass;
613
614		if (hwe->class == XE_ENGINE_CLASS_OTHER ||
615		    hwe->class == XE_ENGINE_CLASS_MAX)
616			continue;
617
618		if ((class_mask >> hwe->class) & 1)
619			continue;
620
621		class_mask |= 1 << hwe->class;
622
623		switch (hwe->class) {
624		case XE_ENGINE_CLASS_RENDER:
625			strcpy(name, "rcs");
626			break;
627		case XE_ENGINE_CLASS_VIDEO_DECODE:
628			strcpy(name, "vcs");
629			break;
630		case XE_ENGINE_CLASS_VIDEO_ENHANCE:
631			strcpy(name, "vecs");
632			break;
633		case XE_ENGINE_CLASS_COPY:
634			strcpy(name, "bcs");
635			break;
636		case XE_ENGINE_CLASS_COMPUTE:
637			strcpy(name, "ccs");
638			break;
639		default:
640			err = -EINVAL;
641			goto err_object;
642		}
643
644		keclass = kobj_xe_hw_engine_class(xe, kobj, name);
645		if (!keclass) {
646			err = -EINVAL;
647			goto err_object;
648		}
649
650		keclass->eclass = hwe->eclass;
651		err = xe_add_hw_engine_class_defaults(xe, &keclass->base);
652		if (err) {
653			drm_warn(&xe->drm,
654				 "Add .defaults to engines failed!, err: %d\n",
655				 err);
656			goto err_object;
657		}
658
659		err = sysfs_create_files(&keclass->base, files);
660		if (err)
661			goto err_object;
662	}
663
664	err = drmm_add_action_or_reset(&xe->drm, hw_engine_class_sysfs_fini,
665				       kobj);
666	if (err)
667		drm_warn(&xe->drm,
668			 "%s: drmm_add_action_or_reset failed, err: %d\n",
669			 __func__, err);
670
671	return err;
672err_object:
673	kobject_put(kobj);
674	return err;
675}
676