From f04feb5b54419443e9937f5cc871f0612608f650 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Thu, 18 Dec 2025 14:38:50 -0800 Subject: [PATCH] drm/xe/pf: Add support for enabling scheduler groups Scheduler groups are enabled by sending a specific policy configuration KLV to the GuC. We don't allow changing this policy if there are VF active, since the expectation is that the VF will only check if the feature is enabled during driver initialization. While the GuC interface supports a maximum of 8 groups, the actual number of groups that can be enabled can be lower than that and can be different on different devices. For now, all devices support up to 2 groups, so we check that we do not have more groups than that. The functions added by this patch will be used by sysfs/debugfs, coming in follow up patches. Signed-off-by: Daniele Ceraolo Spurio Cc: Michal Wajdeczko Reviewed-by: Michal Wajdeczko Link: https://patch.msgid.link/20251218223846.1146344-18-daniele.ceraolospurio@intel.com --- drivers/gpu/drm/xe/abi/guc_klvs_abi.h | 22 +++ drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c | 153 ++++++++++++++++++ drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h | 7 + .../gpu/drm/xe/xe_gt_sriov_pf_policy_types.h | 6 + drivers/gpu/drm/xe/xe_guc_klv_helpers.c | 2 + 5 files changed, 190 insertions(+) diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h index 89a4f8c504e6c..ac10cf3adbc15 100644 --- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h +++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h @@ -8,6 +8,8 @@ #include +#include "abi/guc_scheduler_abi.h" + /** * DOC: GuC KLV * @@ -200,6 +202,20 @@ enum { * :0: adverse events are not counted (default) * :n: sample period in milliseconds * + * _`GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG` : 0x8004 + * This config allows the PF to split the engines across scheduling groups. + * Each group is independently timesliced across VFs, allowing different + * VFs to be active on the HW at the same time. When enabling this feature, + * all engines must be assigned to a group (and only one group), or they + * will be excluded from scheduling after this KLV is sent. To enable + * the groups, the driver must provide a masks array with + * GUC_MAX_ENGINE_CLASSES entries for each group, with each mask indicating + * which logical instances of that class belong to the group. Therefore, + * the length of this KLV when enabling groups is + * num_groups * GUC_MAX_ENGINE_CLASSES. To disable the groups, the driver + * must send the KLV without any payload (i.e. len = 0). The maximum + * number of groups is 8. + * * _`GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH` : 0x8D00 * This enum is to reset utilized HW engine after VF Switch (i.e to clean * up Stale HW register left behind by previous VF) @@ -214,6 +230,12 @@ enum { #define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY 0x8002 #define GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_LEN 1u +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY 0x8004 +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT GUC_MAX_SCHED_GROUPS +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MIN_LEN 0 +#define GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_MAX_LEN \ + (GUC_KLV_VGT_POLICY_ENGINE_GROUP_MAX_COUNT * GUC_MAX_ENGINE_CLASSES) + #define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY 0x8D00 #define GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_LEN 1u diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c index fc60e7a014346..cf6ead37bd642 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.c @@ -97,6 +97,23 @@ static int pf_push_policy_u32(struct xe_gt *gt, u16 key, u32 value) return pf_push_policy_klvs(gt, 1, klv, ARRAY_SIZE(klv)); } +static int pf_push_policy_payload(struct xe_gt *gt, u16 key, void *payload, u32 num_dwords) +{ + CLASS(xe_guc_buf, buf)(>->uc.guc.buf, GUC_KLV_LEN_MIN + num_dwords); + u32 *klv; + + if (!xe_guc_buf_is_valid(buf)) + return -ENOBUFS; + + klv = xe_guc_buf_cpu_ptr(buf); + + klv[0] = PREP_GUC_KLV(key, num_dwords); + if (num_dwords) + memcpy(&klv[1], payload, num_dwords * sizeof(u32)); + + return pf_push_policy_buf_klvs(gt, 1, buf, GUC_KLV_LEN_MIN + num_dwords); +} + static int pf_update_policy_bool(struct xe_gt *gt, u16 key, bool *policy, bool value) { int err; @@ -397,6 +414,17 @@ static void pf_sched_group_media_slices(struct xe_gt *gt, struct guc_sched_group if (group < 2) return; + /* + * If we have more groups than the GuC can support then we don't want to + * expose this specific mode, because the GuC will return an error if we + * try to enable it. + */ + if (group > gt->sriov.pf.policy.guc.sched_groups.max_groups) { + xe_gt_sriov_notice(gt, "media_slice mode has too many groups: %u vs %u\n", + group, gt->sriov.pf.policy.guc.sched_groups.max_groups); + return; + } + /* The GuC expects an array with a guc_sched_group entry for each group */ values = drmm_kcalloc(>_to_xe(gt)->drm, group, sizeof(struct guc_sched_group), GFP_KERNEL); @@ -459,6 +487,15 @@ static void pf_init_sched_groups(struct xe_gt *gt) if (!xe_sriov_gt_pf_policy_has_sched_groups_support(gt)) return; + /* + * The GuC interface supports up to 8 groups. However, the GuC only + * fully allocates resources for a subset of groups, based on the number + * of engines and expected usage. The plan is for this to become + * queryable via H2G, but for now GuC FW for all devices supports a + * maximum of 2 groups so we can just hardcode that. + */ + gt->sriov.pf.policy.guc.sched_groups.max_groups = 2; + for (m = XE_SRIOV_SCHED_GROUPS_DISABLED + 1; m < XE_SRIOV_SCHED_GROUPS_MODES_COUNT; m++) { u32 *num_groups = >->sriov.pf.policy.guc.sched_groups.modes[m].num_groups; struct guc_sched_group **groups = @@ -484,14 +521,129 @@ static void pf_init_sched_groups(struct xe_gt *gt) } xe_gt_assert(gt, *num_groups < GUC_MAX_SCHED_GROUPS); + + if (*num_groups) + gt->sriov.pf.policy.guc.sched_groups.supported_modes |= BIT(m); } } +/** + * xe_sriov_gt_pf_policy_has_multi_group_modes() - check whether the GT supports + * any scheduler modes that have multiple groups + * @gt: the &xe_gt to check + * + * This function can only be called on PF. + * + * Return: true if the GT supports modes with multiple groups, false otherwise. + */ +bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt) +{ + return gt->sriov.pf.policy.guc.sched_groups.supported_modes; +} + +/** + * xe_sriov_gt_pf_policy_has_sched_group_mode() - check whether the GT supports + * a specific scheduler group mode + * @gt: the &xe_gt to check + * @mode: the mode to check + * + * This function can only be called on PF. + * + * Return: true if the GT supports the specified mode, false otherwise. + */ +bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode) +{ + if (mode == XE_SRIOV_SCHED_GROUPS_DISABLED) + return true; + + return gt->sriov.pf.policy.guc.sched_groups.supported_modes & BIT(mode); +} + +static int __pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode) +{ + struct guc_sched_group *groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].groups; + u32 num_groups = gt->sriov.pf.policy.guc.sched_groups.modes[mode].num_groups; + + return pf_push_policy_payload(gt, GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY, + groups, num_groups * GUC_MAX_ENGINE_CLASSES); +} + +static int pf_provision_sched_groups(struct xe_gt *gt, enum xe_sriov_sched_group_modes mode) +{ + int err; + + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + if (!xe_sriov_gt_pf_policy_has_sched_group_mode(gt, mode)) + return -EINVAL; + + /* already in the desired mode */ + if (gt->sriov.pf.policy.guc.sched_groups.current_mode == mode) + return 0; + + /* + * We don't allow changing this with VFs active since it is hard for + * VFs to check. + */ + if (xe_sriov_pf_num_vfs(gt_to_xe(gt))) + return -EBUSY; + + err = __pf_provision_sched_groups(gt, mode); + if (err) + return err; + + gt->sriov.pf.policy.guc.sched_groups.current_mode = mode; + + return 0; +} + +static int pf_reprovision_sched_groups(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + /* We only have something to provision if we have possible groups */ + if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) + return 0; + + return __pf_provision_sched_groups(gt, gt->sriov.pf.policy.guc.sched_groups.current_mode); +} + +static void pf_sanitize_sched_groups(struct xe_gt *gt) +{ + xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt))); + lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt)); + + gt->sriov.pf.policy.guc.sched_groups.current_mode = XE_SRIOV_SCHED_GROUPS_DISABLED; +} + +/** + * xe_gt_sriov_pf_policy_set_sched_groups_mode() - Control the 'sched_groups' policy. + * @gt: the &xe_gt where to apply the policy + * @mode: the sched_group mode to be activated + * + * This function can only be called on PF. + * + * Return: 0 on success or a negative error code on failure. + */ +int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode) +{ + if (!xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) + return -ENODEV; + + guard(mutex)(xe_gt_sriov_pf_master_mutex(gt)); + return pf_provision_sched_groups(gt, mode); +} + static void pf_sanitize_guc_policies(struct xe_gt *gt) { pf_sanitize_sched_if_idle(gt); pf_sanitize_reset_engine(gt); pf_sanitize_sample_period(gt); + pf_sanitize_sched_groups(gt); } /** @@ -530,6 +682,7 @@ int xe_gt_sriov_pf_policy_reprovision(struct xe_gt *gt, bool reset) err |= pf_reprovision_sched_if_idle(gt); err |= pf_reprovision_reset_engine(gt); err |= pf_reprovision_sample_period(gt); + err |= pf_reprovision_sched_groups(gt); mutex_unlock(xe_gt_sriov_pf_master_mutex(gt)); xe_pm_runtime_put(gt_to_xe(gt)); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h index f5e3b2595063e..d6e96302ff68c 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy.h @@ -8,6 +8,8 @@ #include +#include "xe_gt_sriov_pf_policy_types.h" + struct drm_printer; struct xe_gt; @@ -18,6 +20,11 @@ bool xe_gt_sriov_pf_policy_get_reset_engine(struct xe_gt *gt); int xe_gt_sriov_pf_policy_set_sample_period(struct xe_gt *gt, u32 value); u32 xe_gt_sriov_pf_policy_get_sample_period(struct xe_gt *gt); bool xe_sriov_gt_pf_policy_has_sched_groups_support(struct xe_gt *gt); +bool xe_sriov_gt_pf_policy_has_multi_group_modes(struct xe_gt *gt); +bool xe_sriov_gt_pf_policy_has_sched_group_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode); +int xe_gt_sriov_pf_policy_set_sched_groups_mode(struct xe_gt *gt, + enum xe_sriov_sched_group_modes mode); void xe_gt_sriov_pf_policy_init(struct xe_gt *gt); void xe_gt_sriov_pf_policy_sanitize(struct xe_gt *gt); diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h index 11527ab1db7a8..97d2781905219 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_policy_types.h @@ -24,6 +24,9 @@ enum xe_sriov_sched_group_modes { /** * struct xe_gt_sriov_scheduler_groups - Scheduler groups policy info + * @max_groups: max number of groups supported by the GuC for the platform + * @supported_modes: mask of supported modes + * @current_mode: active scheduler groups mode * @modes: array of masks and their number for each mode * @modes.groups: array of engine instance groups in given mode, with each group * consisting of GUC_MAX_ENGINE_CLASSES engine instances masks. A @@ -33,6 +36,9 @@ enum xe_sriov_sched_group_modes { * are in the same group. */ struct xe_gt_sriov_scheduler_groups { + u8 max_groups; + u32 supported_modes; + enum xe_sriov_sched_group_modes current_mode; struct { struct guc_sched_group *groups; u32 num_groups; diff --git a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c index 146a6eda9e064..1b08b443606e0 100644 --- a/drivers/gpu/drm/xe/xe_guc_klv_helpers.c +++ b/drivers/gpu/drm/xe/xe_guc_klv_helpers.c @@ -26,6 +26,8 @@ const char *xe_guc_klv_key_to_string(u16 key) return "sched_if_idle"; case GUC_KLV_VGT_POLICY_ADVERSE_SAMPLE_PERIOD_KEY: return "sample_period"; + case GUC_KLV_VGT_POLICY_ENGINE_GROUP_CONFIG_KEY: + return "engine_group_config"; case GUC_KLV_VGT_POLICY_RESET_AFTER_VF_SWITCH_KEY: return "reset_engine"; /* VF CFG keys */ -- 2.47.3