#include <linux/string_choices.h>
#include <linux/wordpart.h>
#include "abi/guc_actions_sriov_abi.h"
#include "abi/guc_klvs_abi.h"
#include "regs/xe_gtt_defs.h"
#include "regs/xe_guc_regs.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_gt_sriov_pf_config.h"
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_policy.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
#include "xe_guc_buf.h"
#include "xe_guc_ct.h"
#include "xe_guc_db_mgr.h"
#include "xe_guc_id_mgr.h"
#include "xe_guc_klv_helpers.h"
#include "xe_guc_klv_thresholds_set.h"
#include "xe_guc_submit.h"
#include "xe_lmtt.h"
#include "xe_migrate.h"
#include "xe_sriov.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vram_types.h"
#include "xe_wopcm.h"
#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
static int guc_action_update_vf_cfg(struct xe_guc *guc, u32 vfid,
u64 addr, u32 size)
{
u32 request[] = {
GUC_ACTION_PF2GUC_UPDATE_VF_CFG,
vfid,
lower_32_bits(addr),
upper_32_bits(addr),
size,
};
return xe_guc_ct_send_block(&guc->ct, request, ARRAY_SIZE(request));
}
static int pf_send_vf_cfg_reset(struct xe_gt *gt, u32 vfid)
{
struct xe_guc *guc = >->uc.guc;
int ret;
ret = guc_action_update_vf_cfg(guc, vfid, 0, 0);
return ret <= 0 ? ret : -EPROTO;
}
static int pf_send_vf_buf_klvs(struct xe_gt *gt, u32 vfid, struct xe_guc_buf buf, u32 num_dwords)
{
struct xe_guc *guc = >->uc.guc;
return guc_action_update_vf_cfg(guc, vfid, xe_guc_buf_flush(buf), num_dwords);
}
static int pf_push_vf_buf_klvs(struct xe_gt *gt, unsigned int vfid, u32 num_klvs,
struct xe_guc_buf buf, u32 num_dwords)
{
int ret;
ret = pf_send_vf_buf_klvs(gt, vfid, buf, num_dwords);
if (ret != num_klvs) {
int err = ret < 0 ? ret : ret < num_klvs ? -ENOKEY : -EPROTO;
void *klvs = xe_guc_buf_cpu_ptr(buf);
struct drm_printer p = xe_gt_info_printer(gt);
char name[8];
xe_gt_sriov_notice(gt, "Failed to push %s %u config KLV%s (%pe)\n",
xe_sriov_function_name(vfid, name, sizeof(name)),
num_klvs, str_plural(num_klvs), ERR_PTR(err));
xe_guc_klv_print(klvs, num_dwords, &p);
return err;
}
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
struct drm_printer p = xe_gt_dbg_printer(gt);
void *klvs = xe_guc_buf_cpu_ptr(buf);
char name[8];
xe_gt_sriov_dbg(gt, "pushed %s config with %u KLV%s:\n",
xe_sriov_function_name(vfid, name, sizeof(name)),
num_klvs, str_plural(num_klvs));
xe_guc_klv_print(klvs, num_dwords, &p);
}
return 0;
}
static int pf_push_vf_cfg_klvs(struct xe_gt *gt, unsigned int vfid, u32 num_klvs,
const u32 *klvs, u32 num_dwords)
{
CLASS(xe_guc_buf_from_data, buf)(>->uc.guc.buf, klvs, num_dwords * sizeof(u32));
xe_gt_assert(gt, num_klvs == xe_guc_klv_count(klvs, num_dwords));
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
return pf_push_vf_buf_klvs(gt, vfid, num_klvs, buf, num_dwords);
}
static int pf_push_vf_cfg_u32(struct xe_gt *gt, unsigned int vfid, u16 key, u32 value)
{
u32 klv[] = {
FIELD_PREP(GUC_KLV_0_KEY, key) | FIELD_PREP(GUC_KLV_0_LEN, 1),
value,
};
return pf_push_vf_cfg_klvs(gt, vfid, 1, klv, ARRAY_SIZE(klv));
}
static int pf_push_vf_cfg_u64(struct xe_gt *gt, unsigned int vfid, u16 key, u64 value)
{
u32 klv[] = {
FIELD_PREP(GUC_KLV_0_KEY, key) | FIELD_PREP(GUC_KLV_0_LEN, 2),
lower_32_bits(value),
upper_32_bits(value),
};
return pf_push_vf_cfg_klvs(gt, vfid, 1, klv, ARRAY_SIZE(klv));
}
static int pf_push_vf_cfg_ggtt(struct xe_gt *gt, unsigned int vfid, u64 start, u64 size)
{
u32 klvs[] = {
PREP_GUC_KLV_TAG(VF_CFG_GGTT_START),
lower_32_bits(start),
upper_32_bits(start),
PREP_GUC_KLV_TAG(VF_CFG_GGTT_SIZE),
lower_32_bits(size),
upper_32_bits(size),
};
return pf_push_vf_cfg_klvs(gt, vfid, 2, klvs, ARRAY_SIZE(klvs));
}
static int pf_push_vf_cfg_ctxs(struct xe_gt *gt, unsigned int vfid, u32 begin, u32 num)
{
u32 klvs[] = {
PREP_GUC_KLV_TAG(VF_CFG_BEGIN_CONTEXT_ID),
begin,
PREP_GUC_KLV_TAG(VF_CFG_NUM_CONTEXTS),
num,
};
return pf_push_vf_cfg_klvs(gt, vfid, 2, klvs, ARRAY_SIZE(klvs));
}
static int pf_push_vf_cfg_dbs(struct xe_gt *gt, unsigned int vfid, u32 begin, u32 num)
{
u32 klvs[] = {
PREP_GUC_KLV_TAG(VF_CFG_BEGIN_DOORBELL_ID),
begin,
PREP_GUC_KLV_TAG(VF_CFG_NUM_DOORBELLS),
num,
};
return pf_push_vf_cfg_klvs(gt, vfid, 2, klvs, ARRAY_SIZE(klvs));
}
static int pf_push_vf_grp_cfg_u32(struct xe_gt *gt, unsigned int vfid,
u16 key, const u32 *values, u32 count)
{
CLASS(xe_guc_buf, buf)(>->uc.guc.buf, GUC_KLV_LEN_MIN + GUC_MAX_SCHED_GROUPS);
u32 *klv;
xe_gt_assert(gt, count && count <= GUC_MAX_SCHED_GROUPS);
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
klv = xe_guc_buf_cpu_ptr(buf);
klv[0] = FIELD_PREP(GUC_KLV_0_KEY, key) | FIELD_PREP(GUC_KLV_0_LEN, count);
memcpy(&klv[1], values, count * sizeof(u32));
return pf_push_vf_buf_klvs(gt, vfid, 1, buf, GUC_KLV_LEN_MIN + count);
}
static int pf_push_vf_cfg_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 *exec_quantum)
{
*exec_quantum = min_t(u32, *exec_quantum, GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE);
return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY, *exec_quantum);
}
static int pf_push_vf_cfg_preempt_timeout(struct xe_gt *gt, unsigned int vfid, u32 *preempt_timeout)
{
*preempt_timeout = min_t(u32, *preempt_timeout, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE);
return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY, *preempt_timeout);
}
static int pf_push_vf_cfg_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY, priority);
}
static int pf_push_vf_cfg_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
{
return pf_push_vf_cfg_u64(gt, vfid, GUC_KLV_VF_CFG_LMEM_SIZE_KEY, size);
}
static int pf_push_vf_cfg_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index, u32 value)
{
u32 key = xe_guc_klv_threshold_index_to_key(index);
xe_gt_assert(gt, key);
return pf_push_vf_cfg_u32(gt, vfid, key, value);
}
static struct xe_gt_sriov_config *pf_pick_vf_config(struct xe_gt *gt, unsigned int vfid)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
return >->sriov.pf.vfs[vfid].config;
}
static u32 encode_ggtt(u32 *cfg, u64 start, u64 size, bool details)
{
u32 n = 0;
if (details) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_GGTT_START);
cfg[n++] = lower_32_bits(start);
cfg[n++] = upper_32_bits(start);
}
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_GGTT_SIZE);
cfg[n++] = lower_32_bits(size);
cfg[n++] = upper_32_bits(size);
return n;
}
static u32 encode_config_ggtt(u32 *cfg, const struct xe_gt_sriov_config *config, bool details)
{
struct xe_ggtt_node *node = config->ggtt_region;
if (!xe_ggtt_node_allocated(node))
return 0;
return encode_ggtt(cfg, xe_ggtt_node_addr(node), xe_ggtt_node_size(node), details);
}
static u32 encode_config_sched(struct xe_gt *gt, u32 *cfg, u32 n,
const struct xe_gt_sriov_config *config)
{
int i;
if (xe_sriov_gt_pf_policy_has_multi_group_modes(gt)) {
BUILD_BUG_ON(ARRAY_SIZE(config->exec_quantum) >
GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN);
BUILD_BUG_ON(ARRAY_SIZE(config->preempt_timeout) >
GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN);
cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY,
ARRAY_SIZE(config->exec_quantum));
for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++)
cfg[n++] = config->exec_quantum[i];
cfg[n++] = PREP_GUC_KLV_CONST(GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY,
ARRAY_SIZE(config->preempt_timeout));
for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++)
cfg[n++] = config->preempt_timeout[i];
} else {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM);
cfg[n++] = config->exec_quantum[0];
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT);
cfg[n++] = config->preempt_timeout[0];
}
return n;
}
static u32 encode_config(struct xe_gt *gt, u32 *cfg, const struct xe_gt_sriov_config *config,
bool details)
{
u32 n = 0;
n += encode_config_ggtt(cfg, config, details);
if (details && config->num_ctxs) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_BEGIN_CONTEXT_ID);
cfg[n++] = config->begin_ctx;
}
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_NUM_CONTEXTS);
cfg[n++] = config->num_ctxs;
if (details && config->num_dbs) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_BEGIN_DOORBELL_ID);
cfg[n++] = config->begin_db;
}
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_NUM_DOORBELLS);
cfg[n++] = config->num_dbs;
if (config->lmem_obj) {
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_LMEM_SIZE);
cfg[n++] = lower_32_bits(xe_bo_size(config->lmem_obj));
cfg[n++] = upper_32_bits(xe_bo_size(config->lmem_obj));
}
n = encode_config_sched(gt, cfg, n, config);
#define encode_threshold_config(TAG, NAME, VER...) ({ \
if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) { \
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \
cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \
} \
});
MAKE_XE_GUC_KLV_THRESHOLDS_SET(encode_threshold_config);
#undef encode_threshold_config
return n;
}
static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
u32 max_cfg_dwords = xe_guc_buf_cache_dwords(>->uc.guc.buf);
CLASS(xe_guc_buf, buf)(>->uc.guc.buf, max_cfg_dwords);
u32 num_dwords;
int num_klvs;
u32 *cfg;
int err;
if (!xe_guc_buf_is_valid(buf))
return -ENOBUFS;
cfg = xe_guc_buf_cpu_ptr(buf);
num_dwords = encode_config(gt, cfg, config, true);
xe_gt_assert(gt, num_dwords <= max_cfg_dwords);
if (xe_gt_is_media_type(gt)) {
struct xe_gt *primary = gt->tile->primary_gt;
struct xe_gt_sriov_config *other = pf_pick_vf_config(primary, vfid);
xe_gt_assert(gt, !encode_config_ggtt(cfg + num_dwords, config, true));
num_dwords += encode_config_ggtt(cfg + num_dwords, other, true);
}
xe_gt_assert(gt, num_dwords <= max_cfg_dwords);
if (vfid == PFID) {
u64 ggtt_start = xe_ggtt_start(gt_to_tile(gt)->mem.ggtt);
u64 ggtt_size = xe_ggtt_size(gt_to_tile(gt)->mem.ggtt);
xe_gt_assert(gt, !encode_config_ggtt(cfg + num_dwords, config, true));
num_dwords += encode_ggtt(cfg + num_dwords, ggtt_start, ggtt_size, true);
}
num_klvs = xe_guc_klv_count(cfg, num_dwords);
err = pf_push_vf_buf_klvs(gt, vfid, num_klvs, buf, num_dwords);
return err;
}
static int pf_push_vf_cfg(struct xe_gt *gt, unsigned int vfid, bool reset)
{
int err = 0;
xe_gt_assert(gt, vfid);
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (reset)
err = pf_send_vf_cfg_reset(gt, vfid);
if (!err)
err = pf_push_full_vf_config(gt, vfid);
return err;
}
static int pf_refresh_vf_cfg(struct xe_gt *gt, unsigned int vfid)
{
return pf_push_vf_cfg(gt, vfid, true);
}
static u64 pf_get_ggtt_alignment(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
return IS_DGFX(xe) && xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K;
}
static u64 pf_get_min_spare_ggtt(struct xe_gt *gt)
{
return IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ?
pf_get_ggtt_alignment(gt) : SZ_64M;
}
static u64 pf_get_spare_ggtt(struct xe_gt *gt)
{
u64 spare;
xe_gt_assert(gt, xe_gt_is_main_type(gt));
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
spare = gt->sriov.pf.spare.ggtt_size;
spare = max_t(u64, spare, pf_get_min_spare_ggtt(gt));
return spare;
}
static int pf_set_spare_ggtt(struct xe_gt *gt, u64 size)
{
xe_gt_assert(gt, xe_gt_is_main_type(gt));
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (size && size < pf_get_min_spare_ggtt(gt))
return -EINVAL;
size = round_up(size, pf_get_ggtt_alignment(gt));
gt->sriov.pf.spare.ggtt_size = size;
return 0;
}
static int pf_distribute_config_ggtt(struct xe_tile *tile, unsigned int vfid, u64 start, u64 size)
{
int err, err2 = 0;
err = pf_push_vf_cfg_ggtt(tile->primary_gt, vfid, start, size);
if (tile->media_gt && !err)
err2 = pf_push_vf_cfg_ggtt(tile->media_gt, vfid, start, size);
return err ?: err2;
}
static void pf_release_ggtt(struct xe_tile *tile, struct xe_ggtt_node *node)
{
if (xe_ggtt_node_allocated(node)) {
xe_ggtt_node_remove(node, false);
} else {
xe_ggtt_node_fini(node);
}
}
static void pf_release_vf_config_ggtt(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
pf_release_ggtt(gt_to_tile(gt), config->ggtt_region);
config->ggtt_region = NULL;
}
static int pf_provision_vf_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_ggtt_node *node;
struct xe_tile *tile = gt_to_tile(gt);
struct xe_ggtt *ggtt = tile->mem.ggtt;
u64 alignment = pf_get_ggtt_alignment(gt);
int err;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, xe_gt_is_main_type(gt));
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
size = round_up(size, alignment);
if (xe_ggtt_node_allocated(config->ggtt_region)) {
err = pf_distribute_config_ggtt(tile, vfid, 0, 0);
if (unlikely(err))
return err;
pf_release_vf_config_ggtt(gt, config);
err = pf_refresh_vf_cfg(gt, vfid);
if (unlikely(err))
return err;
}
xe_gt_assert(gt, !xe_ggtt_node_allocated(config->ggtt_region));
if (!size)
return 0;
node = xe_ggtt_node_init(ggtt);
if (IS_ERR(node))
return PTR_ERR(node);
err = xe_ggtt_node_insert(node, size, alignment);
if (unlikely(err))
goto err;
xe_ggtt_assign(node, vfid);
xe_gt_sriov_dbg_verbose(gt, "VF%u assigned GGTT %llx-%llx\n",
vfid, xe_ggtt_node_addr(node), xe_ggtt_node_addr(node) + size - 1);
err = pf_distribute_config_ggtt(gt->tile, vfid, xe_ggtt_node_addr(node), size);
if (unlikely(err))
goto err;
config->ggtt_region = node;
return 0;
err:
pf_release_ggtt(tile, node);
return err;
}
static u64 pf_get_vf_config_ggtt(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_ggtt_node *node = config->ggtt_region;
xe_gt_assert(gt, xe_gt_is_main_type(gt));
return xe_ggtt_node_allocated(node) ? xe_ggtt_node_size(node) : 0;
}
u64 xe_gt_sriov_pf_config_get_ggtt(struct xe_gt *gt, unsigned int vfid)
{
u64 size;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
size = pf_get_vf_config_ggtt(gt_to_tile(gt)->primary_gt, vfid);
else
size = pf_get_spare_ggtt(gt_to_tile(gt)->primary_gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return size;
}
static int pf_config_set_u64_done(struct xe_gt *gt, unsigned int vfid, u64 value,
u64 actual, const char *what, int err)
{
char size[10];
char name[8];
xe_sriov_function_name(vfid, name, sizeof(name));
if (unlikely(err)) {
string_get_size(value, 1, STRING_UNITS_2, size, sizeof(size));
xe_gt_sriov_notice(gt, "Failed to provision %s with %llu (%s) %s (%pe)\n",
name, value, size, what, ERR_PTR(err));
string_get_size(actual, 1, STRING_UNITS_2, size, sizeof(size));
xe_gt_sriov_info(gt, "%s provisioning remains at %llu (%s) %s\n",
name, actual, size, what);
return err;
}
string_get_size(actual, 1, STRING_UNITS_2, size, sizeof(size));
xe_gt_sriov_info(gt, "%s provisioned with %llu (%s) %s\n",
name, actual, size, what);
return 0;
}
int xe_gt_sriov_pf_config_set_ggtt(struct xe_gt *gt, unsigned int vfid, u64 size)
{
int err;
xe_gt_assert(gt, xe_gt_is_main_type(gt));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
err = pf_provision_vf_ggtt(gt, vfid, size);
else
err = pf_set_spare_ggtt(gt, size);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u64_done(gt, vfid, size,
xe_gt_sriov_pf_config_get_ggtt(gt, vfid),
vfid ? "GGTT" : "spare GGTT", err);
}
static int pf_config_bulk_set_u64_done(struct xe_gt *gt, unsigned int first, unsigned int num_vfs,
u64 value, u64 (*get)(struct xe_gt*, unsigned int),
const char *what, unsigned int last, int err)
{
char size[10];
xe_gt_assert(gt, first);
xe_gt_assert(gt, num_vfs);
xe_gt_assert(gt, first <= last);
if (num_vfs == 1)
return pf_config_set_u64_done(gt, first, value, get(gt, first), what, err);
if (unlikely(err)) {
xe_gt_sriov_notice(gt, "Failed to bulk provision VF%u..VF%u with %s\n",
first, first + num_vfs - 1, what);
if (last > first)
pf_config_bulk_set_u64_done(gt, first, last - first, value,
get, what, last, 0);
return pf_config_set_u64_done(gt, last, value, get(gt, last), what, err);
}
value = get(gt, first);
string_get_size(value, 1, STRING_UNITS_2, size, sizeof(size));
xe_gt_sriov_info(gt, "VF%u..VF%u provisioned with %llu (%s) %s\n",
first, first + num_vfs - 1, value, size, what);
return 0;
}
int xe_gt_sriov_pf_config_bulk_set_ggtt(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs, u64 size)
{
unsigned int n;
int err = 0;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, xe_gt_is_main_type(gt));
if (!num_vfs)
return 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = vfid; n < vfid + num_vfs; n++) {
err = pf_provision_vf_ggtt(gt, n, size);
if (err)
break;
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_bulk_set_u64_done(gt, vfid, num_vfs, size,
xe_gt_sriov_pf_config_get_ggtt,
"GGTT", n, err);
}
static u64 pf_get_max_ggtt(struct xe_gt *gt)
{
struct xe_ggtt *ggtt = gt_to_tile(gt)->mem.ggtt;
u64 alignment = pf_get_ggtt_alignment(gt);
u64 spare = pf_get_spare_ggtt(gt);
u64 max_hole;
max_hole = xe_ggtt_largest_hole(ggtt, alignment, &spare);
xe_gt_sriov_dbg_verbose(gt, "HOLE max %lluK reserved %lluK\n",
max_hole / SZ_1K, spare / SZ_1K);
return max_hole > spare ? max_hole - spare : 0;
}
static u64 pf_estimate_fair_ggtt(struct xe_gt *gt, unsigned int num_vfs)
{
u64 available = pf_get_max_ggtt(gt);
u64 alignment = pf_get_ggtt_alignment(gt);
u64 fair;
fair = div_u64(available, num_vfs);
fair = ALIGN_DOWN(fair, alignment);
xe_gt_sriov_dbg_verbose(gt, "GGTT available(%lluK) fair(%u x %lluK)\n",
available / SZ_1K, num_vfs, fair / SZ_1K);
return fair;
}
static u64 pf_profile_fair_ggtt(struct xe_gt *gt, unsigned int num_vfs)
{
bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
u64 shareable = ALIGN_DOWN(GUC_GGTT_TOP, SZ_512M);
u64 alignment = pf_get_ggtt_alignment(gt);
if (admin_only_pf && num_vfs == 1)
return ALIGN_DOWN(shareable, alignment);
if (num_vfs > 56)
return SZ_64M - SZ_8M;
return rounddown_pow_of_two(div_u64(shareable, num_vfs));
}
int xe_gt_sriov_pf_config_set_fair_ggtt(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
u64 profile = pf_profile_fair_ggtt(gt, num_vfs);
u64 fair;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, num_vfs);
xe_gt_assert(gt, xe_gt_is_main_type(gt));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
fair = pf_estimate_fair_ggtt(gt, num_vfs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (!fair)
return -ENOSPC;
fair = min(fair, profile);
if (fair < profile)
xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %llu vs %llu)\n",
"GGTT", fair, profile);
return xe_gt_sriov_pf_config_bulk_set_ggtt(gt, vfid, num_vfs, fair);
}
ssize_t xe_gt_sriov_pf_config_ggtt_save(struct xe_gt *gt, unsigned int vfid,
void *buf, size_t size)
{
struct xe_ggtt_node *node;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, !(!buf ^ !size));
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
node = pf_pick_vf_config(gt, vfid)->ggtt_region;
if (!buf)
return xe_ggtt_node_pt_size(node);
return xe_ggtt_node_save(node, buf, size, vfid);
}
int xe_gt_sriov_pf_config_ggtt_restore(struct xe_gt *gt, unsigned int vfid,
const void *buf, size_t size)
{
struct xe_ggtt_node *node;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid);
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
node = pf_pick_vf_config(gt, vfid)->ggtt_region;
return xe_ggtt_node_load(node, buf, size, vfid);
}
static u32 pf_get_min_spare_ctxs(struct xe_gt *gt)
{
return IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ?
hweight64(gt->info.engine_mask) : SZ_256;
}
static u32 pf_get_spare_ctxs(struct xe_gt *gt)
{
u32 spare;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
spare = gt->sriov.pf.spare.num_ctxs;
spare = max_t(u32, spare, pf_get_min_spare_ctxs(gt));
return spare;
}
static int pf_set_spare_ctxs(struct xe_gt *gt, u32 spare)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (spare > GUC_ID_MAX)
return -EINVAL;
if (spare && spare < pf_get_min_spare_ctxs(gt))
return -EINVAL;
gt->sriov.pf.spare.num_ctxs = spare;
return 0;
}
static int pf_reserve_ctxs(struct xe_gt *gt, u32 num)
{
struct xe_guc_id_mgr *idm = >->uc.guc.submission_state.idm;
unsigned int spare = pf_get_spare_ctxs(gt);
return xe_guc_id_mgr_reserve(idm, num, spare);
}
static void pf_release_ctxs(struct xe_gt *gt, u32 start, u32 num)
{
struct xe_guc_id_mgr *idm = >->uc.guc.submission_state.idm;
if (num)
xe_guc_id_mgr_release(idm, start, num);
}
static void pf_release_config_ctxs(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
pf_release_ctxs(gt, config->begin_ctx, config->num_ctxs);
config->begin_ctx = 0;
config->num_ctxs = 0;
}
static int pf_provision_vf_ctxs(struct xe_gt *gt, unsigned int vfid, u32 num_ctxs)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int ret;
xe_gt_assert(gt, vfid);
if (num_ctxs > GUC_ID_MAX)
return -EINVAL;
if (config->num_ctxs) {
ret = pf_push_vf_cfg_ctxs(gt, vfid, 0, 0);
if (unlikely(ret))
return ret;
pf_release_config_ctxs(gt, config);
ret = pf_refresh_vf_cfg(gt, vfid);
if (unlikely(ret))
return ret;
}
if (!num_ctxs)
return 0;
ret = pf_reserve_ctxs(gt, num_ctxs);
if (unlikely(ret < 0))
return ret;
config->begin_ctx = ret;
config->num_ctxs = num_ctxs;
ret = pf_push_vf_cfg_ctxs(gt, vfid, config->begin_ctx, config->num_ctxs);
if (unlikely(ret)) {
pf_release_config_ctxs(gt, config);
return ret;
}
xe_gt_sriov_dbg_verbose(gt, "VF%u contexts %u-%u\n",
vfid, config->begin_ctx, config->begin_ctx + config->num_ctxs - 1);
return 0;
}
static u32 pf_get_vf_config_ctxs(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->num_ctxs;
}
u32 xe_gt_sriov_pf_config_get_ctxs(struct xe_gt *gt, unsigned int vfid)
{
u32 num_ctxs;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
num_ctxs = pf_get_vf_config_ctxs(gt, vfid);
else
num_ctxs = pf_get_spare_ctxs(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return num_ctxs;
}
static const char *no_unit(u32 unused)
{
return "";
}
static const char *spare_unit(u32 unused)
{
return " spare";
}
static int pf_config_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 value, u32 actual,
const char *what, const char *(*unit)(u32), int err)
{
char name[8];
xe_sriov_function_name(vfid, name, sizeof(name));
if (unlikely(err)) {
xe_gt_sriov_notice(gt, "Failed to provision %s with %u%s %s (%pe)\n",
name, value, unit(value), what, ERR_PTR(err));
xe_gt_sriov_info(gt, "%s provisioning remains at %u%s %s\n",
name, actual, unit(actual), what);
return err;
}
xe_gt_sriov_info(gt, "%s provisioned with %u%s %s\n",
name, actual, unit(actual), what);
return 0;
}
static char *to_group_name(const char *what, u8 group, char *buf, size_t size)
{
snprintf(buf, size, "group%u%s%s", group, what ? " " : "", what ?: "");
return buf;
}
static int
pf_groups_cfg_set_u32_done(struct xe_gt *gt, unsigned int vfid, u32 *values, u32 count,
void (*get_actual)(struct xe_gt *, unsigned int, u32 *, u32),
const char *what, const char *(*unit)(u32), int err)
{
u32 actual[GUC_MAX_SCHED_GROUPS];
char group_name[32];
u8 g;
xe_gt_assert(gt, count <= ARRAY_SIZE(actual));
get_actual(gt, vfid, actual, count);
for (g = 0; g < count; g++)
pf_config_set_u32_done(gt, vfid, values[g], actual[g],
to_group_name(what, g, group_name, sizeof(group_name)),
unit, err);
return err;
}
int xe_gt_sriov_pf_config_set_ctxs(struct xe_gt *gt, unsigned int vfid, u32 num_ctxs)
{
int err;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
err = pf_provision_vf_ctxs(gt, vfid, num_ctxs);
else
err = pf_set_spare_ctxs(gt, num_ctxs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, num_ctxs,
xe_gt_sriov_pf_config_get_ctxs(gt, vfid),
"GuC context IDs", vfid ? no_unit : spare_unit, err);
}
static int pf_config_bulk_set_u32_done(struct xe_gt *gt, unsigned int first, unsigned int num_vfs,
u32 value, u32 (*get)(struct xe_gt*, unsigned int),
const char *what, const char *(*unit)(u32),
unsigned int last, int err)
{
char name[8];
xe_gt_assert(gt, num_vfs);
xe_gt_assert(gt, first <= last);
if (num_vfs == 1)
return pf_config_set_u32_done(gt, first, value, get(gt, first), what, unit, err);
if (unlikely(err)) {
xe_gt_sriov_notice(gt, "Failed to bulk provision %s..VF%u with %s\n",
xe_sriov_function_name(first, name, sizeof(name)),
first + num_vfs - 1, what);
if (last > first)
pf_config_bulk_set_u32_done(gt, first, last - first, value,
get, what, unit, last, 0);
return pf_config_set_u32_done(gt, last, value, get(gt, last), what, unit, err);
}
value = get(gt, first);
xe_gt_sriov_info(gt, "%s..VF%u provisioned with %u%s %s\n",
xe_sriov_function_name(first, name, sizeof(name)),
first + num_vfs - 1, value, unit(value), what);
return 0;
}
int xe_gt_sriov_pf_config_bulk_set_ctxs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs, u32 num_ctxs)
{
unsigned int n;
int err = 0;
xe_gt_assert(gt, vfid);
if (!num_vfs)
return 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = vfid; n < vfid + num_vfs; n++) {
err = pf_provision_vf_ctxs(gt, n, num_ctxs);
if (err)
break;
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_bulk_set_u32_done(gt, vfid, num_vfs, num_ctxs,
xe_gt_sriov_pf_config_get_ctxs,
"GuC context IDs", no_unit, n, err);
}
static u32 pf_profile_fair_ctxs(struct xe_gt *gt, unsigned int num_vfs)
{
bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
if (admin_only_pf && num_vfs == 1)
return ALIGN_DOWN(GUC_ID_MAX, SZ_1K);
return rounddown_pow_of_two(GUC_ID_MAX / num_vfs);
}
static u32 pf_estimate_fair_ctxs(struct xe_gt *gt, unsigned int num_vfs)
{
struct xe_guc_id_mgr *idm = >->uc.guc.submission_state.idm;
u32 spare = pf_get_spare_ctxs(gt);
u32 fair = (idm->total - spare) / num_vfs;
int ret;
for (; fair; --fair) {
ret = xe_guc_id_mgr_reserve(idm, fair * num_vfs, spare);
if (ret < 0)
continue;
xe_guc_id_mgr_release(idm, ret, fair * num_vfs);
break;
}
xe_gt_sriov_dbg_verbose(gt, "contexts fair(%u x %u)\n", num_vfs, fair);
return fair;
}
int xe_gt_sriov_pf_config_set_fair_ctxs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
u32 profile = pf_profile_fair_ctxs(gt, num_vfs);
u32 fair;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, num_vfs);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
fair = pf_estimate_fair_ctxs(gt, num_vfs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (!fair)
return -ENOSPC;
fair = min(fair, profile);
if (fair < profile)
xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %u vs %u)\n",
"GuC context IDs", fair, profile);
return xe_gt_sriov_pf_config_bulk_set_ctxs(gt, vfid, num_vfs, fair);
}
static u32 pf_get_min_spare_dbs(struct xe_gt *gt)
{
return IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 1 : 0;
}
static u32 pf_get_spare_dbs(struct xe_gt *gt)
{
u32 spare;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
spare = gt->sriov.pf.spare.num_dbs;
spare = max_t(u32, spare, pf_get_min_spare_dbs(gt));
return spare;
}
static int pf_set_spare_dbs(struct xe_gt *gt, u32 spare)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (spare > GUC_NUM_DOORBELLS)
return -EINVAL;
if (spare && spare < pf_get_min_spare_dbs(gt))
return -EINVAL;
gt->sriov.pf.spare.num_dbs = spare;
return 0;
}
static int pf_reserve_dbs(struct xe_gt *gt, u32 num)
{
struct xe_guc_db_mgr *dbm = >->uc.guc.dbm;
unsigned int spare = pf_get_spare_dbs(gt);
return xe_guc_db_mgr_reserve_range(dbm, num, spare);
}
static void pf_release_dbs(struct xe_gt *gt, u32 start, u32 num)
{
struct xe_guc_db_mgr *dbm = >->uc.guc.dbm;
if (num)
xe_guc_db_mgr_release_range(dbm, start, num);
}
static void pf_release_config_dbs(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
pf_release_dbs(gt, config->begin_db, config->num_dbs);
config->begin_db = 0;
config->num_dbs = 0;
}
static int pf_provision_vf_dbs(struct xe_gt *gt, unsigned int vfid, u32 num_dbs)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int ret;
xe_gt_assert(gt, vfid);
if (num_dbs > GUC_NUM_DOORBELLS)
return -EINVAL;
if (config->num_dbs) {
ret = pf_push_vf_cfg_dbs(gt, vfid, 0, 0);
if (unlikely(ret))
return ret;
pf_release_config_dbs(gt, config);
ret = pf_refresh_vf_cfg(gt, vfid);
if (unlikely(ret))
return ret;
}
if (!num_dbs)
return 0;
ret = pf_reserve_dbs(gt, num_dbs);
if (unlikely(ret < 0))
return ret;
config->begin_db = ret;
config->num_dbs = num_dbs;
ret = pf_push_vf_cfg_dbs(gt, vfid, config->begin_db, config->num_dbs);
if (unlikely(ret)) {
pf_release_config_dbs(gt, config);
return ret;
}
xe_gt_sriov_dbg_verbose(gt, "VF%u doorbells %u-%u\n",
vfid, config->begin_db, config->begin_db + config->num_dbs - 1);
return 0;
}
static u32 pf_get_vf_config_dbs(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->num_dbs;
}
u32 xe_gt_sriov_pf_config_get_dbs(struct xe_gt *gt, unsigned int vfid)
{
u32 num_dbs;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
num_dbs = pf_get_vf_config_dbs(gt, vfid);
else
num_dbs = pf_get_spare_dbs(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return num_dbs;
}
int xe_gt_sriov_pf_config_set_dbs(struct xe_gt *gt, unsigned int vfid, u32 num_dbs)
{
int err;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
err = pf_provision_vf_dbs(gt, vfid, num_dbs);
else
err = pf_set_spare_dbs(gt, num_dbs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, num_dbs,
xe_gt_sriov_pf_config_get_dbs(gt, vfid),
"GuC doorbell IDs", vfid ? no_unit : spare_unit, err);
}
int xe_gt_sriov_pf_config_bulk_set_dbs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs, u32 num_dbs)
{
unsigned int n;
int err = 0;
xe_gt_assert(gt, vfid);
if (!num_vfs)
return 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = vfid; n < vfid + num_vfs; n++) {
err = pf_provision_vf_dbs(gt, n, num_dbs);
if (err)
break;
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_bulk_set_u32_done(gt, vfid, num_vfs, num_dbs,
xe_gt_sriov_pf_config_get_dbs,
"GuC doorbell IDs", no_unit, n, err);
}
static u32 pf_profile_fair_dbs(struct xe_gt *gt, unsigned int num_vfs)
{
bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
if (admin_only_pf && num_vfs == 1)
return GUC_NUM_DOORBELLS - SZ_16;
return rounddown_pow_of_two(GUC_NUM_DOORBELLS / (num_vfs + 1));
}
static u32 pf_estimate_fair_dbs(struct xe_gt *gt, unsigned int num_vfs)
{
struct xe_guc_db_mgr *dbm = >->uc.guc.dbm;
u32 spare = pf_get_spare_dbs(gt);
u32 fair = (GUC_NUM_DOORBELLS - spare) / num_vfs;
int ret;
for (; fair; --fair) {
ret = xe_guc_db_mgr_reserve_range(dbm, fair * num_vfs, spare);
if (ret < 0)
continue;
xe_guc_db_mgr_release_range(dbm, ret, fair * num_vfs);
break;
}
xe_gt_sriov_dbg_verbose(gt, "doorbells fair(%u x %u)\n", num_vfs, fair);
return fair;
}
int xe_gt_sriov_pf_config_set_fair_dbs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
u32 profile = pf_profile_fair_dbs(gt, num_vfs);
u32 fair;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, num_vfs);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
fair = pf_estimate_fair_dbs(gt, num_vfs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (!fair)
return -ENOSPC;
fair = min(fair, profile);
if (fair < profile)
xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %u vs %u)\n",
"GuC doorbell IDs", fair, profile);
return xe_gt_sriov_pf_config_bulk_set_dbs(gt, vfid, num_vfs, fair);
}
static u64 pf_get_lmem_alignment(struct xe_gt *gt)
{
return SZ_2M;
}
static u64 pf_get_min_spare_lmem(struct xe_gt *gt)
{
return SZ_128M;
}
static u64 pf_get_spare_lmem(struct xe_gt *gt)
{
u64 spare;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
spare = gt->sriov.pf.spare.lmem_size;
spare = max_t(u64, spare, pf_get_min_spare_lmem(gt));
return spare;
}
static int pf_set_spare_lmem(struct xe_gt *gt, u64 size)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (size && size < pf_get_min_spare_lmem(gt))
return -EINVAL;
gt->sriov.pf.spare.lmem_size = size;
return 0;
}
static u64 pf_get_vf_config_lmem(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_bo *bo;
bo = config->lmem_obj;
return bo ? xe_bo_size(bo) : 0;
}
static int pf_distribute_config_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_tile *tile;
unsigned int tid;
int err;
for_each_tile(tile, xe, tid) {
if (tile->primary_gt == gt) {
err = pf_push_vf_cfg_lmem(gt, vfid, size);
} else {
u64 lmem = pf_get_vf_config_lmem(tile->primary_gt, vfid);
if (!lmem)
continue;
err = pf_push_vf_cfg_lmem(gt, vfid, lmem);
}
if (unlikely(err))
return err;
}
return 0;
}
static void pf_force_lmtt_invalidate(struct xe_device *xe)
{
struct xe_lmtt *lmtt;
struct xe_tile *tile;
unsigned int tid;
xe_assert(xe, xe_device_has_lmtt(xe));
xe_assert(xe, IS_SRIOV_PF(xe));
for_each_tile(tile, xe, tid) {
lmtt = &tile->sriov.pf.lmtt;
xe_lmtt_invalidate_hw(lmtt);
}
}
static void pf_reset_vf_lmtt(struct xe_device *xe, unsigned int vfid)
{
struct xe_lmtt *lmtt;
struct xe_tile *tile;
unsigned int tid;
xe_assert(xe, xe_device_has_lmtt(xe));
xe_assert(xe, IS_SRIOV_PF(xe));
for_each_tile(tile, xe, tid) {
lmtt = &tile->sriov.pf.lmtt;
xe_lmtt_drop_pages(lmtt, vfid);
}
}
static int pf_update_vf_lmtt(struct xe_device *xe, unsigned int vfid)
{
struct xe_gt_sriov_config *config;
struct xe_tile *tile;
struct xe_lmtt *lmtt;
struct xe_bo *bo;
struct xe_gt *gt;
u64 total, offset;
unsigned int gtid;
unsigned int tid;
int err;
xe_assert(xe, xe_device_has_lmtt(xe));
xe_assert(xe, IS_SRIOV_PF(xe));
total = 0;
for_each_tile(tile, xe, tid)
total += pf_get_vf_config_lmem(tile->primary_gt, vfid);
for_each_tile(tile, xe, tid) {
lmtt = &tile->sriov.pf.lmtt;
xe_lmtt_drop_pages(lmtt, vfid);
if (!total)
continue;
err = xe_lmtt_prepare_pages(lmtt, vfid, total);
if (err)
goto fail;
offset = 0;
for_each_gt(gt, xe, gtid) {
if (xe_gt_is_media_type(gt))
continue;
config = pf_pick_vf_config(gt, vfid);
bo = config->lmem_obj;
if (!bo)
continue;
err = xe_lmtt_populate_pages(lmtt, vfid, bo, offset);
if (err)
goto fail;
offset += xe_bo_size(bo);
}
}
pf_force_lmtt_invalidate(xe);
return 0;
fail:
for_each_tile(tile, xe, tid) {
lmtt = &tile->sriov.pf.lmtt;
xe_lmtt_drop_pages(lmtt, vfid);
}
return err;
}
static bool pf_release_vf_config_lmem(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
xe_gt_assert(gt, IS_DGFX(gt_to_xe(gt)));
xe_gt_assert(gt, xe_gt_is_main_type(gt));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
if (config->lmem_obj) {
xe_bo_unpin_map_no_vm(config->lmem_obj);
config->lmem_obj = NULL;
return true;
}
return false;
}
static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_device *xe = gt_to_xe(gt);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
int err;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, IS_DGFX(xe));
xe_gt_assert(gt, xe_gt_is_main_type(gt));
size = round_up(size, pf_get_lmem_alignment(gt));
if (config->lmem_obj) {
err = pf_distribute_config_lmem(gt, vfid, 0);
if (unlikely(err))
return err;
if (xe_device_has_lmtt(xe))
pf_reset_vf_lmtt(xe, vfid);
pf_release_vf_config_lmem(gt, config);
}
xe_gt_assert(gt, !config->lmem_obj);
if (!size)
return 0;
xe_gt_assert(gt, pf_get_lmem_alignment(gt) == SZ_2M);
bo = xe_bo_create_pin_range_novm(xe, tile,
ALIGN(size, PAGE_SIZE), 0, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_NEEDS_2M |
XE_BO_FLAG_PINNED |
XE_BO_FLAG_PINNED_LATE_RESTORE |
XE_BO_FLAG_FORCE_USER_VRAM);
if (IS_ERR(bo))
return PTR_ERR(bo);
config->lmem_obj = bo;
if (xe_device_has_lmtt(xe)) {
err = pf_update_vf_lmtt(xe, vfid);
if (unlikely(err))
goto release;
}
err = pf_push_vf_cfg_lmem(gt, vfid, xe_bo_size(bo));
if (unlikely(err))
goto reset_lmtt;
xe_gt_sriov_dbg_verbose(gt, "VF%u LMEM %zu (%zuM)\n",
vfid, xe_bo_size(bo), xe_bo_size(bo) / SZ_1M);
return 0;
reset_lmtt:
if (xe_device_has_lmtt(xe))
pf_reset_vf_lmtt(xe, vfid);
release:
pf_release_vf_config_lmem(gt, config);
return err;
}
u64 xe_gt_sriov_pf_config_get_lmem(struct xe_gt *gt, unsigned int vfid)
{
u64 size;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
size = pf_get_vf_config_lmem(gt, vfid);
else
size = pf_get_spare_lmem(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return size;
}
int xe_gt_sriov_pf_config_set_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
{
int err;
if (!xe_device_has_lmtt(gt_to_xe(gt)))
return -EPERM;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
err = pf_provision_vf_lmem(gt, vfid, size);
else
err = pf_set_spare_lmem(gt, size);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u64_done(gt, vfid, size,
xe_gt_sriov_pf_config_get_lmem(gt, vfid),
vfid ? "LMEM" : "spare LMEM", err);
}
int xe_gt_sriov_pf_config_bulk_set_lmem(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs, u64 size)
{
unsigned int n;
int err = 0;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, xe_gt_is_main_type(gt));
if (!num_vfs)
return 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = vfid; n < vfid + num_vfs; n++) {
err = pf_provision_vf_lmem(gt, n, size);
if (err)
break;
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_bulk_set_u64_done(gt, vfid, num_vfs, size,
xe_gt_sriov_pf_config_get_lmem,
"LMEM", n, err);
}
static struct xe_bo *pf_get_vf_config_lmem_obj(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->lmem_obj;
}
struct xe_bo *xe_gt_sriov_pf_config_get_lmem_obj(struct xe_gt *gt, unsigned int vfid)
{
xe_gt_assert(gt, vfid);
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
return xe_bo_get(pf_get_vf_config_lmem_obj(gt, vfid));
}
static u64 pf_query_free_lmem(struct xe_gt *gt)
{
struct xe_tile *tile = gt->tile;
return xe_ttm_vram_get_avail(&tile->mem.vram->ttm.manager);
}
static u64 pf_query_max_lmem(struct xe_gt *gt)
{
u64 alignment = pf_get_lmem_alignment(gt);
u64 spare = pf_get_spare_lmem(gt);
u64 free = pf_query_free_lmem(gt);
u64 avail;
avail = free > spare ? free - spare : 0;
avail = round_down(avail, alignment);
return avail;
}
#ifdef CONFIG_DRM_XE_DEBUG_SRIOV
#define MAX_FAIR_LMEM SZ_128M
#endif
static u64 pf_estimate_fair_lmem(struct xe_gt *gt, unsigned int num_vfs)
{
u64 available = pf_query_max_lmem(gt);
u64 alignment = pf_get_lmem_alignment(gt);
u64 fair;
fair = div_u64(available, num_vfs);
fair = ALIGN_DOWN(fair, alignment);
#ifdef MAX_FAIR_LMEM
fair = min_t(u64, MAX_FAIR_LMEM, fair);
#endif
xe_gt_sriov_dbg_verbose(gt, "LMEM available(%lluM) fair(%u x %lluM)\n",
available / SZ_1M, num_vfs, fair / SZ_1M);
return fair;
}
int xe_gt_sriov_pf_config_set_fair_lmem(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
u64 fair;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, num_vfs);
xe_gt_assert(gt, xe_gt_is_main_type(gt));
if (!xe_device_has_lmtt(gt_to_xe(gt)))
return 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
fair = pf_estimate_fair_lmem(gt, num_vfs);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (!fair)
return -ENOSPC;
return xe_gt_sriov_pf_config_bulk_set_lmem(gt, vfid, num_vfs, fair);
}
int xe_gt_sriov_pf_config_set_fair(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
int result = 0;
int err;
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, num_vfs);
if (xe_gt_is_main_type(gt)) {
err = xe_gt_sriov_pf_config_set_fair_ggtt(gt, vfid, num_vfs);
result = result ?: err;
err = xe_gt_sriov_pf_config_set_fair_lmem(gt, vfid, num_vfs);
result = result ?: err;
}
err = xe_gt_sriov_pf_config_set_fair_ctxs(gt, vfid, num_vfs);
result = result ?: err;
err = xe_gt_sriov_pf_config_set_fair_dbs(gt, vfid, num_vfs);
result = result ?: err;
return result;
}
static const char *exec_quantum_unit(u32 exec_quantum)
{
return exec_quantum ? "ms" : "(infinity)";
}
static int pf_provision_exec_quantum(struct xe_gt *gt, unsigned int vfid,
u32 exec_quantum)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
int i;
err = pf_push_vf_cfg_exec_quantum(gt, vfid, &exec_quantum);
if (unlikely(err))
return err;
for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++)
config->exec_quantum[i] = exec_quantum;
return 0;
}
static u32 pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->exec_quantum[0];
}
int xe_gt_sriov_pf_config_set_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid,
u32 exec_quantum)
{
int err;
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_exec_quantum(gt, vfid, exec_quantum);
return pf_config_set_u32_done(gt, vfid, exec_quantum,
pf_get_exec_quantum(gt, vfid),
"execution quantum", exec_quantum_unit, err);
}
int xe_gt_sriov_pf_config_set_exec_quantum(struct xe_gt *gt, unsigned int vfid,
u32 exec_quantum)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
return xe_gt_sriov_pf_config_set_exec_quantum_locked(gt, vfid, exec_quantum);
}
u32 xe_gt_sriov_pf_config_get_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
return pf_get_exec_quantum(gt, vfid);
}
u32 xe_gt_sriov_pf_config_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
return pf_get_exec_quantum(gt, vfid);
}
int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exec_quantum)
{
unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
unsigned int n;
int err = 0;
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
for (n = 0; n <= totalvfs; n++) {
err = pf_provision_exec_quantum(gt, VFID(n), exec_quantum);
if (err)
break;
}
return pf_config_bulk_set_u32_done(gt, 0, 1 + totalvfs, exec_quantum,
pf_get_exec_quantum, "execution quantum",
exec_quantum_unit, n, err);
}
static int pf_provision_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
const u32 *exec_quantums, u32 count)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
int i;
err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY,
exec_quantums, count);
if (unlikely(err))
return err;
for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) {
if (i < count)
config->exec_quantum[i] = min_t(u32, exec_quantums[i],
GUC_KLV_VF_CFG_EXEC_QUANTUM_MAX_VALUE);
else
config->exec_quantum[i] = 0;
}
return 0;
}
static void pf_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
u32 *exec_quantums, u32 max_count)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
u32 count = min_t(u32, max_count, ARRAY_SIZE(config->exec_quantum));
memcpy(exec_quantums, config->exec_quantum, sizeof(u32) * count);
}
int xe_gt_sriov_pf_config_set_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
u32 *exec_quantums, u32 count)
{
int err;
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_groups_exec_quantums(gt, vfid, exec_quantums, count);
return pf_groups_cfg_set_u32_done(gt, vfid, exec_quantums, count,
pf_get_groups_exec_quantums,
"execution quantum",
exec_quantum_unit, err);
}
void xe_gt_sriov_pf_config_get_groups_exec_quantums(struct xe_gt *gt, unsigned int vfid,
u32 *exec_quantums, u32 count)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS);
pf_get_groups_exec_quantums(gt, vfid, exec_quantums, count);
}
static const char *preempt_timeout_unit(u32 preempt_timeout)
{
return preempt_timeout ? "us" : "(infinity)";
}
static int pf_provision_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
int i;
err = pf_push_vf_cfg_preempt_timeout(gt, vfid, &preempt_timeout);
if (unlikely(err))
return err;
for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++)
config->preempt_timeout[i] = preempt_timeout;
return 0;
}
static u32 pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->preempt_timeout[0];
}
int xe_gt_sriov_pf_config_set_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout)
{
int err;
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_preempt_timeout(gt, vfid, preempt_timeout);
return pf_config_set_u32_done(gt, vfid, preempt_timeout,
pf_get_preempt_timeout(gt, vfid),
"preemption timeout", preempt_timeout_unit, err);
}
int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
return xe_gt_sriov_pf_config_set_preempt_timeout_locked(gt, vfid, preempt_timeout);
}
u32 xe_gt_sriov_pf_config_get_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
return pf_get_preempt_timeout(gt, vfid);
}
u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
return pf_get_preempt_timeout(gt, vfid);
}
int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout)
{
unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
unsigned int n;
int err = 0;
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
for (n = 0; n <= totalvfs; n++) {
err = pf_provision_preempt_timeout(gt, VFID(n), preempt_timeout);
if (err)
break;
}
return pf_config_bulk_set_u32_done(gt, 0, 1 + totalvfs, preempt_timeout,
pf_get_preempt_timeout, "preemption timeout",
preempt_timeout_unit, n, err);
}
static int pf_provision_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
const u32 *preempt_timeouts, u32 count)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
int i;
err = pf_push_vf_grp_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY,
preempt_timeouts, count);
if (unlikely(err))
return err;
for (i = 0; i < ARRAY_SIZE(config->preempt_timeout); i++) {
if (i < count)
config->preempt_timeout[i] =
min_t(u32, preempt_timeouts[i],
GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_MAX_VALUE);
else
config->preempt_timeout[i] = 0;
}
return 0;
}
static void pf_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
u32 *preempt_timeouts, u32 max_count)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
u32 count = min_t(u32, max_count, ARRAY_SIZE(config->preempt_timeout));
memcpy(preempt_timeouts, config->preempt_timeout, sizeof(u32) * count);
}
int xe_gt_sriov_pf_config_set_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
u32 *preempt_timeouts, u32 count)
{
int err;
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count);
return pf_groups_cfg_set_u32_done(gt, vfid, preempt_timeouts, count,
pf_get_groups_preempt_timeouts,
"preempt_timeout",
preempt_timeout_unit, err);
}
void xe_gt_sriov_pf_config_get_groups_preempt_timeouts(struct xe_gt *gt, unsigned int vfid,
u32 *preempt_timeouts, u32 count)
{
guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
xe_gt_assert(gt, count <= GUC_MAX_SCHED_GROUPS);
pf_get_groups_preempt_timeouts(gt, vfid, preempt_timeouts, count);
}
static const char *sched_priority_unit(u32 priority)
{
return priority == GUC_SCHED_PRIORITY_LOW ? "(low)" :
priority == GUC_SCHED_PRIORITY_NORMAL ? "(normal)" :
priority == GUC_SCHED_PRIORITY_HIGH ? "(high)" :
"(?)";
}
static int pf_provision_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
err = pf_push_vf_cfg_sched_priority(gt, vfid, priority);
if (unlikely(err))
return err;
config->sched_priority = priority;
return 0;
}
static int pf_get_sched_priority(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->sched_priority;
}
int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
int err;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_sched_priority(gt, vfid, priority);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, priority,
xe_gt_sriov_pf_config_get_sched_priority(gt, vfid),
"scheduling priority", sched_priority_unit, err);
}
u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid)
{
u32 priority;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
priority = pf_get_sched_priority(gt, vfid);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return priority;
}
static void pf_reset_config_sched(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
int i;
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
for (i = 0; i < ARRAY_SIZE(config->exec_quantum); i++) {
config->exec_quantum[i] = 0;
config->preempt_timeout[i] = 0;
}
}
static int pf_provision_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index, u32 value)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
err = pf_push_vf_cfg_threshold(gt, vfid, index, value);
if (unlikely(err))
return err;
config->thresholds[index] = value;
return 0;
}
static int pf_get_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->thresholds[index];
}
static const char *threshold_unit(u32 threshold)
{
return threshold ? "" : "(disabled)";
}
int xe_gt_sriov_pf_config_set_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index, u32 value)
{
u32 key = xe_guc_klv_threshold_index_to_key(index);
const char *name = xe_guc_klv_key_to_string(key);
int err;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_threshold(gt, vfid, index, value);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, value,
xe_gt_sriov_pf_config_get_threshold(gt, vfid, index),
name, threshold_unit, err);
}
u32 xe_gt_sriov_pf_config_get_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index)
{
u32 value;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
value = pf_get_threshold(gt, vfid, index);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return value;
}
static void pf_reset_config_thresholds(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
#define reset_threshold_config(TAG, ...) ({ \
config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)] = 0; \
});
MAKE_XE_GUC_KLV_THRESHOLDS_SET(reset_threshold_config);
#undef reset_threshold_config
}
static void pf_release_vf_config(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_device *xe = gt_to_xe(gt);
bool released;
if (xe_gt_is_main_type(gt)) {
pf_release_vf_config_ggtt(gt, config);
if (IS_DGFX(xe)) {
released = pf_release_vf_config_lmem(gt, config);
if (released && xe_device_has_lmtt(xe))
pf_update_vf_lmtt(xe, vfid);
}
}
pf_release_config_ctxs(gt, config);
pf_release_config_dbs(gt, config);
pf_reset_config_sched(gt, config);
pf_reset_config_thresholds(gt, config);
}
int xe_gt_sriov_pf_config_release(struct xe_gt *gt, unsigned int vfid, bool force)
{
int err;
xe_gt_assert(gt, vfid);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_send_vf_cfg_reset(gt, vfid);
if (!err || force)
pf_release_vf_config(gt, vfid);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (unlikely(err)) {
xe_gt_sriov_notice(gt, "VF%u unprovisioning failed with error (%pe)%s\n",
vfid, ERR_PTR(err),
force ? " but all resources were released anyway!" : "");
}
return force ? 0 : err;
}
static void pf_sanitize_ggtt(struct xe_ggtt_node *ggtt_region, unsigned int vfid)
{
if (xe_ggtt_node_allocated(ggtt_region))
xe_ggtt_assign(ggtt_region, vfid);
}
static int pf_sanitize_lmem(struct xe_tile *tile, struct xe_bo *bo, long timeout)
{
struct xe_migrate *m = tile->migrate;
struct dma_fence *fence;
int err;
if (!bo)
return 0;
xe_bo_lock(bo, false);
fence = xe_migrate_clear(m, bo, bo->ttm.resource, XE_MIGRATE_CLEAR_FLAG_FULL);
if (IS_ERR(fence)) {
err = PTR_ERR(fence);
} else if (!fence) {
err = -ENOMEM;
} else {
long ret = dma_fence_wait_timeout(fence, false, timeout);
err = ret > 0 ? 0 : ret < 0 ? ret : -ETIMEDOUT;
dma_fence_put(fence);
if (!err)
xe_gt_sriov_dbg_verbose(tile->primary_gt, "LMEM cleared in %dms\n",
jiffies_to_msecs(timeout - ret));
}
xe_bo_unlock(bo);
return err;
}
static int pf_sanitize_vf_resources(struct xe_gt *gt, u32 vfid, long timeout)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = gt_to_xe(gt);
int err = 0;
if (xe_gt_is_main_type(gt)) {
pf_sanitize_ggtt(config->ggtt_region, vfid);
if (IS_DGFX(xe))
err = pf_sanitize_lmem(tile, config->lmem_obj, timeout);
}
return err;
}
int xe_gt_sriov_pf_config_sanitize(struct xe_gt *gt, unsigned int vfid, long timeout)
{
int err;
xe_gt_assert(gt, vfid != PFID);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_sanitize_vf_resources(gt, vfid, timeout);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (unlikely(err))
xe_gt_sriov_notice(gt, "VF%u resource sanitizing failed (%pe)\n",
vfid, ERR_PTR(err));
return err;
}
int xe_gt_sriov_pf_config_push(struct xe_gt *gt, unsigned int vfid, bool refresh)
{
int err = 0;
xe_gt_assert(gt, vfid);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_push_vf_cfg(gt, vfid, refresh);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (unlikely(err)) {
xe_gt_sriov_notice(gt, "Failed to %s VF%u configuration (%pe)\n",
refresh ? "refresh" : "push", vfid, ERR_PTR(err));
}
return err;
}
static int pf_validate_vf_config(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt *primary_gt = gt_to_tile(gt)->primary_gt;
struct xe_device *xe = gt_to_xe(gt);
bool is_primary = xe_gt_is_main_type(gt);
bool valid_ggtt, valid_ctxs, valid_dbs;
bool valid_any, valid_all;
valid_ggtt = pf_get_vf_config_ggtt(primary_gt, vfid);
valid_ctxs = pf_get_vf_config_ctxs(gt, vfid);
valid_dbs = pf_get_vf_config_dbs(gt, vfid);
valid_any = valid_ctxs || valid_dbs;
valid_all = valid_ctxs;
valid_all = valid_all && valid_ggtt;
valid_any = valid_any || (valid_ggtt && is_primary);
if (xe_device_has_lmtt(xe)) {
bool valid_lmem = pf_get_vf_config_lmem(primary_gt, vfid);
valid_any = valid_any || (valid_lmem && is_primary);
valid_all = valid_all && valid_lmem;
}
return valid_all ? 0 : valid_any ? -ENOKEY : -ENODATA;
}
bool xe_gt_sriov_pf_config_is_empty(struct xe_gt *gt, unsigned int vfid)
{
bool empty;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
empty = pf_validate_vf_config(gt, vfid) == -ENODATA;
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return empty;
}
ssize_t xe_gt_sriov_pf_config_save(struct xe_gt *gt, unsigned int vfid, void *buf, size_t size)
{
struct xe_gt_sriov_config *config;
ssize_t ret;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid);
xe_gt_assert(gt, !(!buf ^ !size));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
ret = pf_validate_vf_config(gt, vfid);
if (!size) {
ret = ret ? 0 : SZ_4K;
} else if (!ret) {
if (size < SZ_4K) {
ret = -ENOBUFS;
} else {
config = pf_pick_vf_config(gt, vfid);
ret = encode_config(gt, buf, config, false) * sizeof(u32);
}
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return ret;
}
static int pf_restore_vf_config_klv(struct xe_gt *gt, unsigned int vfid,
u32 key, u32 len, const u32 *value)
{
switch (key) {
case GUC_KLV_VF_CFG_NUM_CONTEXTS_KEY:
if (len != GUC_KLV_VF_CFG_NUM_CONTEXTS_LEN)
return -EBADMSG;
return pf_provision_vf_ctxs(gt, vfid, value[0]);
case GUC_KLV_VF_CFG_NUM_DOORBELLS_KEY:
if (len != GUC_KLV_VF_CFG_NUM_DOORBELLS_LEN)
return -EBADMSG;
return pf_provision_vf_dbs(gt, vfid, value[0]);
case GUC_KLV_VF_CFG_EXEC_QUANTUM_KEY:
if (len != GUC_KLV_VF_CFG_EXEC_QUANTUM_LEN)
return -EBADMSG;
return pf_provision_exec_quantum(gt, vfid, value[0]);
case GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_KEY:
if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_EXEC_QUANTUM_MAX_LEN)
return -EBADMSG;
return pf_provision_groups_exec_quantums(gt, vfid, value, len);
case GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_KEY:
if (len > GUC_KLV_VF_CFG_ENGINE_GROUP_PREEMPT_TIMEOUT_MAX_LEN)
return -EBADMSG;
return pf_provision_groups_preempt_timeouts(gt, vfid, value, len);
case GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY:
if (len != GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_LEN)
return -EBADMSG;
return pf_provision_preempt_timeout(gt, vfid, value[0]);
#define define_threshold_key_to_provision_case(TAG, NAME, VER...) \
case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \
BUILD_BUG_ON(MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG) != 1u); \
if (len != MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG)) \
return -EBADMSG; \
if (IF_ARGS(!GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), false, VER)) \
return -EKEYREJECTED; \
return pf_provision_threshold(gt, vfid, \
MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG), \
value[0]);
MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_key_to_provision_case)
#undef define_threshold_key_to_provision_case
}
if (xe_gt_is_media_type(gt))
return -EKEYREJECTED;
switch (key) {
case GUC_KLV_VF_CFG_GGTT_SIZE_KEY:
if (len != GUC_KLV_VF_CFG_GGTT_SIZE_LEN)
return -EBADMSG;
return pf_provision_vf_ggtt(gt, vfid, make_u64_from_u32(value[1], value[0]));
case GUC_KLV_VF_CFG_LMEM_SIZE_KEY:
if (!IS_DGFX(gt_to_xe(gt)))
return -EKEYREJECTED;
if (len != GUC_KLV_VF_CFG_LMEM_SIZE_LEN)
return -EBADMSG;
return pf_provision_vf_lmem(gt, vfid, make_u64_from_u32(value[1], value[0]));
}
return -EKEYREJECTED;
}
static int pf_restore_vf_config(struct xe_gt *gt, unsigned int vfid,
const u32 *klvs, size_t num_dwords)
{
int err;
while (num_dwords >= GUC_KLV_LEN_MIN) {
u32 key = FIELD_GET(GUC_KLV_0_KEY, klvs[0]);
u32 len = FIELD_GET(GUC_KLV_0_LEN, klvs[0]);
klvs += GUC_KLV_LEN_MIN;
num_dwords -= GUC_KLV_LEN_MIN;
if (num_dwords < len)
err = -EBADMSG;
else
err = pf_restore_vf_config_klv(gt, vfid, key, len, klvs);
if (err) {
xe_gt_sriov_dbg(gt, "restore failed on key %#x (%pe)\n", key, ERR_PTR(err));
return err;
}
klvs += len;
num_dwords -= len;
}
return pf_validate_vf_config(gt, vfid);
}
int xe_gt_sriov_pf_config_restore(struct xe_gt *gt, unsigned int vfid,
const void *buf, size_t size)
{
int err;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid);
if (!size)
return -ENODATA;
if (size % sizeof(u32))
return -EINVAL;
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
struct drm_printer p = xe_gt_dbg_printer(gt);
drm_printf(&p, "restoring VF%u config:\n", vfid);
xe_guc_klv_print(buf, size / sizeof(u32), &p);
}
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_send_vf_cfg_reset(gt, vfid);
if (!err) {
pf_release_vf_config(gt, vfid);
err = pf_restore_vf_config(gt, vfid, buf, size / sizeof(u32));
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return err;
}
static void pf_prepare_self_config(struct xe_gt *gt)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, PFID);
config->num_ctxs = GUC_ID_MAX;
config->num_dbs = GUC_NUM_DOORBELLS;
}
static int pf_push_self_config(struct xe_gt *gt)
{
int err;
err = pf_push_full_vf_config(gt, PFID);
if (err) {
xe_gt_sriov_err(gt, "Failed to push self configuration (%pe)\n",
ERR_PTR(err));
return err;
}
xe_gt_sriov_dbg_verbose(gt, "self configuration completed\n");
return 0;
}
static void fini_config(void *arg)
{
struct xe_gt *gt = arg;
struct xe_device *xe = gt_to_xe(gt);
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(xe);
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = 1; n <= total_vfs; n++)
pf_release_vf_config(gt, n);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
}
int xe_gt_sriov_pf_config_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
int err;
xe_gt_assert(gt, IS_SRIOV_PF(xe));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
pf_prepare_self_config(gt);
err = pf_push_self_config(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
if (err)
return err;
return devm_add_action_or_reset(xe->drm.dev, fini_config, gt);
}
void xe_gt_sriov_pf_config_restart(struct xe_gt *gt)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
unsigned int fail = 0, skip = 0;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
pf_push_self_config(gt);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
for (n = 1; n <= total_vfs; n++) {
if (xe_gt_sriov_pf_config_is_empty(gt, n))
skip++;
else if (xe_gt_sriov_pf_config_push(gt, n, false))
fail++;
}
if (fail)
xe_gt_sriov_notice(gt, "Failed to push %u of %u VF%s configurations\n",
fail, total_vfs - skip, str_plural(total_vfs));
if (fail != total_vfs)
xe_gt_sriov_dbg(gt, "pushed %u skip %u of %u VF%s configurations\n",
total_vfs - skip - fail, skip, total_vfs, str_plural(total_vfs));
}
int xe_gt_sriov_pf_config_print_ggtt(struct xe_gt *gt, struct drm_printer *p)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
const struct xe_gt_sriov_config *config;
char buf[10];
for (n = 1; n <= total_vfs; n++) {
config = >->sriov.pf.vfs[n].config;
if (!xe_ggtt_node_allocated(config->ggtt_region))
continue;
string_get_size(xe_ggtt_node_size(config->ggtt_region), 1, STRING_UNITS_2,
buf, sizeof(buf));
drm_printf(p, "VF%u:\t%#0llx-%#llx\t(%s)\n",
n, xe_ggtt_node_addr(config->ggtt_region),
xe_ggtt_node_addr(config->ggtt_region) +
xe_ggtt_node_size(config->ggtt_region) - 1,
buf);
}
return 0;
}
int xe_gt_sriov_pf_config_print_ctxs(struct xe_gt *gt, struct drm_printer *p)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
const struct xe_gt_sriov_config *config;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = 1; n <= total_vfs; n++) {
config = >->sriov.pf.vfs[n].config;
if (!config->num_ctxs)
continue;
drm_printf(p, "VF%u:\t%u-%u\t(%u)\n",
n,
config->begin_ctx,
config->begin_ctx + config->num_ctxs - 1,
config->num_ctxs);
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return 0;
}
int xe_gt_sriov_pf_config_print_dbs(struct xe_gt *gt, struct drm_printer *p)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
const struct xe_gt_sriov_config *config;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = 1; n <= total_vfs; n++) {
config = >->sriov.pf.vfs[n].config;
if (!config->num_dbs)
continue;
drm_printf(p, "VF%u:\t%u-%u\t(%u)\n",
n,
config->begin_db,
config->begin_db + config->num_dbs - 1,
config->num_dbs);
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return 0;
}
int xe_gt_sriov_pf_config_print_lmem(struct xe_gt *gt, struct drm_printer *p)
{
unsigned int n, total_vfs = xe_sriov_pf_get_totalvfs(gt_to_xe(gt));
const struct xe_gt_sriov_config *config;
char buf[10];
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
for (n = 1; n <= total_vfs; n++) {
config = >->sriov.pf.vfs[n].config;
if (!config->lmem_obj)
continue;
string_get_size(xe_bo_size(config->lmem_obj), 1, STRING_UNITS_2,
buf, sizeof(buf));
drm_printf(p, "VF%u:\t%zu\t(%s)\n",
n, xe_bo_size(config->lmem_obj), buf);
}
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return 0;
}
int xe_gt_sriov_pf_config_print_available_ggtt(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_ggtt *ggtt = gt_to_tile(gt)->mem.ggtt;
u64 alignment = pf_get_ggtt_alignment(gt);
u64 spare, avail, total;
char buf[10];
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
spare = pf_get_spare_ggtt(gt);
total = xe_ggtt_print_holes(ggtt, alignment, p);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
string_get_size(total, 1, STRING_UNITS_2, buf, sizeof(buf));
drm_printf(p, "total:\t%llu\t(%s)\n", total, buf);
string_get_size(spare, 1, STRING_UNITS_2, buf, sizeof(buf));
drm_printf(p, "spare:\t%llu\t(%s)\n", spare, buf);
avail = total > spare ? total - spare : 0;
string_get_size(avail, 1, STRING_UNITS_2, buf, sizeof(buf));
drm_printf(p, "avail:\t%llu\t(%s)\n", avail, buf);
return 0;
}
#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_gt_sriov_pf_config_kunit.c"
#endif