Contributors: 9
Author Tokens Token Proportion Commits Commit Proportion
Tejas Upadhyay 2732 88.16% 6 40.00%
Rodrigo Vivi 165 5.32% 1 6.67%
Matthew Brost 134 4.32% 1 6.67%
Jani Nikula 19 0.61% 1 6.67%
Bommu Krishnaiah 18 0.58% 1 6.67%
Nirmoy Das 12 0.39% 1 6.67%
Matthew Auld 9 0.29% 1 6.67%
Himal Prasad Ghimiray 7 0.23% 2 13.33%
Lucas De Marchi 3 0.10% 1 6.67%
Total 3099 15

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2023 Intel Corporation
 */

#include <drm/drm_managed.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

#include "xe_device.h"
#include "xe_gt.h"
#include "xe_hw_engine_class_sysfs.h"
#include "xe_pm.h"

#define MAX_ENGINE_CLASS_NAME_LEN    16
static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
					   struct kobject *parent);

/**
 * xe_hw_engine_timeout_in_range - Helper to check if timeout is in range
 * @timeout: timeout to validate
 * @min: min value of valid range
 * @max: max value of valid range
 *
 * This helper helps to validate if timeout is in min-max range of HW engine
 * scheduler.
 *
 * Returns: Returns false value for failure and true for success.
 */
bool xe_hw_engine_timeout_in_range(u64 timeout, u64 min, u64 max)
{
	return timeout >= min && timeout <= max;
}

static void kobj_xe_hw_engine_release(struct kobject *kobj)
{
	kfree(kobj);
}

static const struct kobj_type kobj_xe_hw_engine_type = {
	.release = kobj_xe_hw_engine_release,
	.sysfs_ops = &kobj_sysfs_ops
};

static ssize_t job_timeout_max_store(struct kobject *kobj,
				     struct kobj_attribute *attr,
				     const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (timeout < eclass->sched_props.job_timeout_min)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(timeout,
					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.job_timeout_max, timeout);

	return count;
}

static ssize_t job_timeout_max_show(struct kobject *kobj,
				    struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_max);
}

static const struct kobj_attribute job_timeout_max_attr =
__ATTR(job_timeout_max, 0644, job_timeout_max_show, job_timeout_max_store);

static ssize_t job_timeout_min_store(struct kobject *kobj,
				     struct kobj_attribute *attr,
				     const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (timeout > eclass->sched_props.job_timeout_max)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(timeout,
					   XE_HW_ENGINE_JOB_TIMEOUT_MIN,
					   XE_HW_ENGINE_JOB_TIMEOUT_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.job_timeout_min, timeout);

	return count;
}

static ssize_t job_timeout_min_show(struct kobject *kobj,
				    struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_min);
}

static const struct kobj_attribute job_timeout_min_attr =
__ATTR(job_timeout_min, 0644, job_timeout_min_show, job_timeout_min_store);

static ssize_t job_timeout_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 min = eclass->sched_props.job_timeout_min;
	u32 max = eclass->sched_props.job_timeout_max;
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.job_timeout_ms, timeout);

	return count;
}

static ssize_t job_timeout_show(struct kobject *kobj,
				struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.job_timeout_ms);
}

static const struct kobj_attribute job_timeout_attr =
__ATTR(job_timeout_ms, 0644, job_timeout_show, job_timeout_store);

static ssize_t job_timeout_default(struct kobject *kobj,
				   struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_ms);
}

static const struct kobj_attribute job_timeout_def =
__ATTR(job_timeout_ms, 0444, job_timeout_default, NULL);

static ssize_t job_timeout_min_default(struct kobject *kobj,
				       struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_min);
}

static const struct kobj_attribute job_timeout_min_def =
__ATTR(job_timeout_min, 0444, job_timeout_min_default, NULL);

static ssize_t job_timeout_max_default(struct kobject *kobj,
				       struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.job_timeout_max);
}

static const struct kobj_attribute job_timeout_max_def =
__ATTR(job_timeout_max, 0444, job_timeout_max_default, NULL);

static ssize_t timeslice_duration_store(struct kobject *kobj,
					struct kobj_attribute *attr,
					const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 min = eclass->sched_props.timeslice_min;
	u32 max = eclass->sched_props.timeslice_max;
	u32 duration;
	int err;

	err = kstrtou32(buf, 0, &duration);
	if (err)
		return err;

	if (!xe_hw_engine_timeout_in_range(duration, min, max))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.timeslice_us, duration);

	return count;
}

static ssize_t timeslice_duration_max_store(struct kobject *kobj,
					    struct kobj_attribute *attr,
					    const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 duration;
	int err;

	err = kstrtou32(buf, 0, &duration);
	if (err)
		return err;

	if (duration < eclass->sched_props.timeslice_min)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(duration,
					   XE_HW_ENGINE_TIMESLICE_MIN,
					   XE_HW_ENGINE_TIMESLICE_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.timeslice_max, duration);

	return count;
}

static ssize_t timeslice_duration_max_show(struct kobject *kobj,
					   struct kobj_attribute *attr,
					   char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_max);
}

static const struct kobj_attribute timeslice_duration_max_attr =
	__ATTR(timeslice_duration_max, 0644, timeslice_duration_max_show,
	       timeslice_duration_max_store);

static ssize_t timeslice_duration_min_store(struct kobject *kobj,
					    struct kobj_attribute *attr,
					    const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 duration;
	int err;

	err = kstrtou32(buf, 0, &duration);
	if (err)
		return err;

	if (duration > eclass->sched_props.timeslice_max)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(duration,
					   XE_HW_ENGINE_TIMESLICE_MIN,
					   XE_HW_ENGINE_TIMESLICE_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.timeslice_min, duration);

	return count;
}

static ssize_t timeslice_duration_min_show(struct kobject *kobj,
					   struct kobj_attribute *attr,
					   char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_min);
}

static const struct kobj_attribute timeslice_duration_min_attr =
	__ATTR(timeslice_duration_min, 0644, timeslice_duration_min_show,
	       timeslice_duration_min_store);

static ssize_t timeslice_duration_show(struct kobject *kobj,
				       struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.timeslice_us);
}

static const struct kobj_attribute timeslice_duration_attr =
	__ATTR(timeslice_duration_us, 0644, timeslice_duration_show,
	       timeslice_duration_store);

static ssize_t timeslice_default(struct kobject *kobj,
				 struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_us);
}

static const struct kobj_attribute timeslice_duration_def =
__ATTR(timeslice_duration_us, 0444, timeslice_default, NULL);

static ssize_t timeslice_min_default(struct kobject *kobj,
				     struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_min);
}

static const struct kobj_attribute timeslice_duration_min_def =
__ATTR(timeslice_duration_min, 0444, timeslice_min_default, NULL);

static ssize_t timeslice_max_default(struct kobject *kobj,
				     struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.timeslice_max);
}

static const struct kobj_attribute timeslice_duration_max_def =
__ATTR(timeslice_duration_max, 0444, timeslice_max_default, NULL);

static ssize_t preempt_timeout_store(struct kobject *kobj,
				     struct kobj_attribute *attr,
				     const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 min = eclass->sched_props.preempt_timeout_min;
	u32 max = eclass->sched_props.preempt_timeout_max;
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (!xe_hw_engine_timeout_in_range(timeout, min, max))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.preempt_timeout_us, timeout);

	return count;
}

static ssize_t preempt_timeout_show(struct kobject *kobj,
				    struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_us);
}

static const struct kobj_attribute preempt_timeout_attr =
__ATTR(preempt_timeout_us, 0644, preempt_timeout_show, preempt_timeout_store);

static ssize_t preempt_timeout_default(struct kobject *kobj,
				       struct kobj_attribute *attr,
				       char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_us);
}

static const struct kobj_attribute preempt_timeout_def =
__ATTR(preempt_timeout_us, 0444, preempt_timeout_default, NULL);

static ssize_t preempt_timeout_min_default(struct kobject *kobj,
					   struct kobj_attribute *attr,
					   char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_min);
}

static const struct kobj_attribute preempt_timeout_min_def =
__ATTR(preempt_timeout_min, 0444, preempt_timeout_min_default, NULL);

static ssize_t preempt_timeout_max_default(struct kobject *kobj,
					   struct kobj_attribute *attr,
					   char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj->parent);

	return sysfs_emit(buf, "%u\n", eclass->defaults.preempt_timeout_max);
}

static const struct kobj_attribute preempt_timeout_max_def =
__ATTR(preempt_timeout_max, 0444, preempt_timeout_max_default, NULL);

static ssize_t preempt_timeout_max_store(struct kobject *kobj,
					 struct kobj_attribute *attr,
					 const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (timeout < eclass->sched_props.preempt_timeout_min)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(timeout,
					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.preempt_timeout_max, timeout);

	return count;
}

static ssize_t preempt_timeout_max_show(struct kobject *kobj,
					struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_max);
}

static const struct kobj_attribute preempt_timeout_max_attr =
	__ATTR(preempt_timeout_max, 0644, preempt_timeout_max_show,
	       preempt_timeout_max_store);

static ssize_t preempt_timeout_min_store(struct kobject *kobj,
					 struct kobj_attribute *attr,
					 const char *buf, size_t count)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);
	u32 timeout;
	int err;

	err = kstrtou32(buf, 0, &timeout);
	if (err)
		return err;

	if (timeout > eclass->sched_props.preempt_timeout_max)
		return -EINVAL;

	if (!xe_hw_engine_timeout_in_range(timeout,
					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN,
					   XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX))
		return -EINVAL;

	WRITE_ONCE(eclass->sched_props.preempt_timeout_min, timeout);

	return count;
}

static ssize_t preempt_timeout_min_show(struct kobject *kobj,
					struct kobj_attribute *attr, char *buf)
{
	struct xe_hw_engine_class_intf *eclass = kobj_to_eclass(kobj);

	return sysfs_emit(buf, "%u\n", eclass->sched_props.preempt_timeout_min);
}

static const struct kobj_attribute preempt_timeout_min_attr =
	__ATTR(preempt_timeout_min, 0644, preempt_timeout_min_show,
	       preempt_timeout_min_store);

static const struct attribute *defaults[] = {
	&job_timeout_def.attr,
	&job_timeout_min_def.attr,
	&job_timeout_max_def.attr,
	&timeslice_duration_def.attr,
	&timeslice_duration_min_def.attr,
	&timeslice_duration_max_def.attr,
	&preempt_timeout_def.attr,
	&preempt_timeout_min_def.attr,
	&preempt_timeout_max_def.attr,
	NULL
};

static const struct attribute * const files[] = {
	&job_timeout_attr.attr,
	&job_timeout_min_attr.attr,
	&job_timeout_max_attr.attr,
	&timeslice_duration_attr.attr,
	&timeslice_duration_min_attr.attr,
	&timeslice_duration_max_attr.attr,
	&preempt_timeout_attr.attr,
	&preempt_timeout_min_attr.attr,
	&preempt_timeout_max_attr.attr,
	NULL
};

static void kobj_xe_hw_engine_class_fini(void *arg)
{
	struct kobject *kobj = arg;

	sysfs_remove_files(kobj, files);
	kobject_put(kobj);
}

static struct kobj_eclass *
kobj_xe_hw_engine_class(struct xe_device *xe, struct kobject *parent, const char *name)
{
	struct kobj_eclass *keclass;
	int err = 0;

	keclass = kzalloc(sizeof(*keclass), GFP_KERNEL);
	if (!keclass)
		return NULL;

	kobject_init(&keclass->base, &kobj_xe_hw_engine_type);
	if (kobject_add(&keclass->base, parent, "%s", name)) {
		kobject_put(&keclass->base);
		return NULL;
	}
	keclass->xe = xe;

	err = devm_add_action_or_reset(xe->drm.dev, kobj_xe_hw_engine_class_fini,
				       &keclass->base);
	if (err)
		return NULL;

	return keclass;
}

static void hw_engine_class_defaults_fini(void *arg)
{
	struct kobject *kobj = arg;

	sysfs_remove_files(kobj, defaults);
	kobject_put(kobj);
}

static int xe_add_hw_engine_class_defaults(struct xe_device *xe,
					   struct kobject *parent)
{
	struct kobject *kobj;
	int err = 0;

	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
	if (!kobj)
		return -ENOMEM;

	kobject_init(kobj, &kobj_xe_hw_engine_type);
	err = kobject_add(kobj, parent, "%s", ".defaults");
	if (err)
		goto err_object;

	err = sysfs_create_files(kobj, defaults);
	if (err)
		goto err_object;

	return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_defaults_fini, kobj);

err_object:
	kobject_put(kobj);
	return err;
}

static void xe_hw_engine_sysfs_kobj_release(struct kobject *kobj)
{
	kfree(kobj);
}

static ssize_t xe_hw_engine_class_sysfs_attr_show(struct kobject *kobj,
						  struct attribute *attr,
						  char *buf)
{
	struct xe_device *xe = kobj_to_xe(kobj);
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->show) {
		xe_pm_runtime_get(xe);
		ret = kattr->show(kobj, kattr, buf);
		xe_pm_runtime_put(xe);
	}

	return ret;
}

static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
						   struct attribute *attr,
						   const char *buf,
						   size_t count)
{
	struct xe_device *xe = kobj_to_xe(kobj);
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->store) {
		xe_pm_runtime_get(xe);
		ret = kattr->store(kobj, kattr, buf, count);
		xe_pm_runtime_put(xe);
	}

	return ret;
}

static const struct sysfs_ops xe_hw_engine_class_sysfs_ops = {
	.show = xe_hw_engine_class_sysfs_attr_show,
	.store = xe_hw_engine_class_sysfs_attr_store,
};

static const struct kobj_type xe_hw_engine_sysfs_kobj_type = {
	.release = xe_hw_engine_sysfs_kobj_release,
	.sysfs_ops = &xe_hw_engine_class_sysfs_ops,
};

static void hw_engine_class_sysfs_fini(void *arg)
{
	struct kobject *kobj = arg;

	kobject_put(kobj);
}

/**
 * xe_hw_engine_class_sysfs_init - Init HW engine classes on GT.
 * @gt: Xe GT.
 *
 * This routine creates sysfs for HW engine classes and adds methods
 * to get/set different scheduling properties for HW engines class.
 *
 * Returns: Returns error value for failure and 0 for success.
 */
int xe_hw_engine_class_sysfs_init(struct xe_gt *gt)
{
	struct xe_device *xe = gt_to_xe(gt);
	struct xe_hw_engine *hwe;
	enum xe_hw_engine_id id;
	struct kobject *kobj;
	u16 class_mask = 0;
	int err = 0;

	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
	if (!kobj)
		return -ENOMEM;

	kobject_init(kobj, &xe_hw_engine_sysfs_kobj_type);

	err = kobject_add(kobj, gt->sysfs, "engines");
	if (err)
		goto err_object;

	for_each_hw_engine(hwe, gt, id) {
		const char *name;
		struct kobj_eclass *keclass;

		if (hwe->class == XE_ENGINE_CLASS_OTHER ||
		    hwe->class == XE_ENGINE_CLASS_MAX)
			continue;

		if ((class_mask >> hwe->class) & 1)
			continue;

		class_mask |= 1 << hwe->class;
		name = xe_hw_engine_class_to_str(hwe->class);
		if (!name) {
			err = -EINVAL;
			goto err_object;
		}

		keclass = kobj_xe_hw_engine_class(xe, kobj, name);
		if (!keclass) {
			err = -EINVAL;
			goto err_object;
		}

		keclass->eclass = hwe->eclass;
		err = xe_add_hw_engine_class_defaults(xe, &keclass->base);
		if (err)
			goto err_object;

		err = sysfs_create_files(&keclass->base, files);
		if (err)
			goto err_object;
	}

	return devm_add_action_or_reset(xe->drm.dev, hw_engine_class_sysfs_fini, kobj);

err_object:
	kobject_put(kobj);
	return err;
}