// SPDX-License-Identifier: MIT
/*
* Copyright © 2024 Intel Corporation
*/
#include <drm/drm_managed.h>
#include "abi/guc_actions_sriov_abi.h"
#include "xe_bo.h"
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_migration.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
#include "xe_guc_ct.h"
#include "xe_sriov.h"
/* Return: number of dwords saved/restored/required or a negative error code on failure */
static int guc_action_vf_save_restore(struct xe_guc *guc, u32 vfid, u32 opcode,
u64 addr, u32 ndwords)
{
u32 request[PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_LEN] = {
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_PF2GUC_SAVE_RESTORE_VF) |
FIELD_PREP(PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_0_OPCODE, opcode),
FIELD_PREP(PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_1_VFID, vfid),
FIELD_PREP(PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_2_ADDR_LO, lower_32_bits(addr)),
FIELD_PREP(PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_3_ADDR_HI, upper_32_bits(addr)),
FIELD_PREP(PF2GUC_SAVE_RESTORE_VF_REQUEST_MSG_4_SIZE, ndwords),
};
return xe_guc_ct_send_block(&guc->ct, request, ARRAY_SIZE(request));
}
/* Return: size of the state in dwords or a negative error code on failure */
static int pf_send_guc_query_vf_state_size(struct xe_gt *gt, unsigned int vfid)
{
int ret;
ret = guc_action_vf_save_restore(>->uc.guc, vfid, GUC_PF_OPCODE_VF_SAVE, 0 , 0 );
return ret ?: -ENODATA;
}
/* Return: number of state dwords saved or a negative error code on failure */
static int pf_send_guc_save_vf_state(struct xe_gt *gt, unsigned int vfid,
void *buff, size_t size)
{
const int ndwords = size / sizeof (u32);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = tile_to_xe(tile);
struct xe_guc *guc = >->uc.guc;
struct xe_bo *bo;
int ret;
xe_gt_assert(gt, size % sizeof (u32) == 0 );
xe_gt_assert(gt, size == ndwords * sizeof (u32));
bo = xe_bo_create_pin_map(xe, tile, NULL,
ALIGN(size, PAGE_SIZE),
ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE);
if (IS_ERR(bo))
return PTR_ERR(bo);
ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_SAVE,
xe_bo_ggtt_addr(bo), ndwords);
if (!ret)
ret = -ENODATA;
else if (ret > ndwords)
ret = -EPROTO;
else if (ret > 0 )
xe_map_memcpy_from(xe, buff, &bo->vmap, 0 , ret * sizeof (u32));
xe_bo_unpin_map_no_vm(bo);
return ret;
}
/* Return: number of state dwords restored or a negative error code on failure */
static int pf_send_guc_restore_vf_state(struct xe_gt *gt, unsigned int vfid,
const void *buff, size_t size)
{
const int ndwords = size / sizeof (u32);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = tile_to_xe(tile);
struct xe_guc *guc = >->uc.guc;
struct xe_bo *bo;
int ret;
xe_gt_assert(gt, size % sizeof (u32) == 0 );
xe_gt_assert(gt, size == ndwords * sizeof (u32));
bo = xe_bo_create_pin_map(xe, tile, NULL,
ALIGN(size, PAGE_SIZE),
ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE);
if (IS_ERR(bo))
return PTR_ERR(bo);
xe_map_memcpy_to(xe, &bo->vmap, 0 , buff, size);
ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_RESTORE,
xe_bo_ggtt_addr(bo), ndwords);
if (!ret)
ret = -ENODATA;
else if (ret > ndwords)
ret = -EPROTO;
xe_bo_unpin_map_no_vm(bo);
return ret;
}
static bool pf_migration_supported(struct xe_gt *gt)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
return gt->sriov.pf.migration.supported;
}
static struct mutex *pf_migration_mutex(struct xe_gt *gt)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
return >->sriov.pf.migration.snapshot_lock;
}
static struct xe_gt_sriov_state_snapshot *pf_pick_vf_snapshot(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(pf_migration_mutex(gt));
return >->sriov.pf.vfs[vfid].snapshot;
}
static unsigned int pf_snapshot_index(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
{
return container_of(snapshot, struct xe_gt_sriov_metadata, snapshot) - gt->sriov.pf.vfs;
}
static void pf_free_guc_state(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
{
struct xe_device *xe = gt_to_xe(gt);
drmm_kfree(&xe->drm, snapshot->guc.buff);
snapshot->guc.buff = NULL;
snapshot->guc.size = 0 ;
}
static int pf_alloc_guc_state(struct xe_gt *gt,
struct xe_gt_sriov_state_snapshot *snapshot,
size_t size)
{
struct xe_device *xe = gt_to_xe(gt);
void *p;
pf_free_guc_state(gt, snapshot);
if (!size)
return -ENODATA;
if (size % sizeof (u32))
return -EINVAL;
if (size > SZ_2M)
return -EFBIG;
p = drmm_kzalloc(&xe->drm, size, GFP_KERNEL);
if (!p)
return -ENOMEM;
snapshot->guc.buff = p;
snapshot->guc.size = size;
return 0 ;
}
static void pf_dump_guc_state(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
{
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
unsigned int vfid __maybe_unused = pf_snapshot_index(gt, snapshot);
xe_gt_sriov_dbg_verbose(gt, "VF%u GuC state is %zu dwords:\n" ,
vfid, snapshot->guc.size / sizeof (u32));
print_hex_dump_bytes("state: " , DUMP_PREFIX_OFFSET,
snapshot->guc.buff, min(SZ_64, snapshot->guc.size));
}
}
static int pf_save_vf_guc_state(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_state_snapshot *snapshot = pf_pick_vf_snapshot(gt, vfid);
size_t size;
int ret;
ret = pf_send_guc_query_vf_state_size(gt, vfid);
if (ret < 0 )
goto fail;
size = ret * sizeof (u32);
xe_gt_sriov_dbg_verbose(gt, "VF%u state size is %d dwords (%zu bytes)\n" , vfid, ret, size);
ret = pf_alloc_guc_state(gt, snapshot, size);
if (ret < 0 )
goto fail;
ret = pf_send_guc_save_vf_state(gt, vfid, snapshot->guc.buff, size);
if (ret < 0 )
goto fail;
size = ret * sizeof (u32);
xe_gt_assert(gt, size);
xe_gt_assert(gt, size <= snapshot->guc.size);
snapshot->guc.size = size;
pf_dump_guc_state(gt, snapshot);
return 0 ;
fail:
xe_gt_sriov_dbg(gt, "Unable to save VF%u state (%pe)\n" , vfid, ERR_PTR(ret));
pf_free_guc_state(gt, snapshot);
return ret;
}
/**
* xe_gt_sriov_pf_migration_save_guc_state ( ) - Take a GuC VF state snapshot .
* @ gt : the & xe_gt
* @ vfid : the VF identifier
*
* This function is for PF only .
*
* Return : 0 on success or a negative error code on failure .
*/
int xe_gt_sriov_pf_migration_save_guc_state(struct xe_gt *gt, unsigned int vfid)
{
int err;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
if (!pf_migration_supported(gt))
return -ENOPKG;
mutex_lock(pf_migration_mutex(gt));
err = pf_save_vf_guc_state(gt, vfid);
mutex_unlock(pf_migration_mutex(gt));
return err;
}
static int pf_restore_vf_guc_state(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_state_snapshot *snapshot = pf_pick_vf_snapshot(gt, vfid);
int ret;
if (!snapshot->guc.size)
return -ENODATA;
xe_gt_sriov_dbg_verbose(gt, "restoring %zu dwords of VF%u GuC state\n" ,
snapshot->guc.size / sizeof (u32), vfid);
ret = pf_send_guc_restore_vf_state(gt, vfid, snapshot->guc.buff, snapshot->guc.size);
if (ret < 0 )
goto fail;
xe_gt_sriov_dbg_verbose(gt, "restored %d dwords of VF%u GuC state\n" , ret, vfid);
return 0 ;
fail:
xe_gt_sriov_dbg(gt, "Failed to restore VF%u GuC state (%pe)\n" , vfid, ERR_PTR(ret));
return ret;
}
/**
* xe_gt_sriov_pf_migration_restore_guc_state ( ) - Restore a GuC VF state .
* @ gt : the & xe_gt
* @ vfid : the VF identifier
*
* This function is for PF only .
*
* Return : 0 on success or a negative error code on failure .
*/
int xe_gt_sriov_pf_migration_restore_guc_state(struct xe_gt *gt, unsigned int vfid)
{
int ret;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
if (!pf_migration_supported(gt))
return -ENOPKG;
mutex_lock(pf_migration_mutex(gt));
ret = pf_restore_vf_guc_state(gt, vfid);
mutex_unlock(pf_migration_mutex(gt));
return ret;
}
#ifdef CONFIG_DEBUG_FS
/**
* xe_gt_sriov_pf_migration_read_guc_state ( ) - Read a GuC VF state .
* @ gt : the & xe_gt
* @ vfid : the VF identifier
* @ buf : the user space buffer to read to
* @ count : the maximum number of bytes to read
* @ pos : the current position in the buffer
*
* This function is for PF only .
*
* This function reads up to @ count bytes from the saved VF GuC state buffer
* at offset @ pos into the user space address starting at @ buf .
*
* Return : the number of bytes read or a negative error code on failure .
*/
ssize_t xe_gt_sriov_pf_migration_read_guc_state(struct xe_gt *gt, unsigned int vfid,
char __user *buf, size_t count, loff_t *pos)
{
struct xe_gt_sriov_state_snapshot *snapshot;
ssize_t ret;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
if (!pf_migration_supported(gt))
return -ENOPKG;
mutex_lock(pf_migration_mutex(gt));
snapshot = pf_pick_vf_snapshot(gt, vfid);
if (snapshot->guc.size)
ret = simple_read_from_buffer(buf, count, pos, snapshot->guc.buff,
snapshot->guc.size);
else
ret = -ENODATA;
mutex_unlock(pf_migration_mutex(gt));
return ret;
}
/**
* xe_gt_sriov_pf_migration_write_guc_state ( ) - Write a GuC VF state .
* @ gt : the & xe_gt
* @ vfid : the VF identifier
* @ buf : the user space buffer with GuC VF state
* @ size : the size of GuC VF state ( in bytes )
*
* This function is for PF only .
*
* This function reads @ size bytes of the VF GuC state stored at user space
* address @ buf and writes it into a internal VF state buffer .
*
* Return : the number of bytes used or a negative error code on failure .
*/
ssize_t xe_gt_sriov_pf_migration_write_guc_state(struct xe_gt *gt, unsigned int vfid,
const char __user *buf, size_t size)
{
struct xe_gt_sriov_state_snapshot *snapshot;
loff_t pos = 0 ;
ssize_t ret;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
if (!pf_migration_supported(gt))
return -ENOPKG;
mutex_lock(pf_migration_mutex(gt));
snapshot = pf_pick_vf_snapshot(gt, vfid);
ret = pf_alloc_guc_state(gt, snapshot, size);
if (!ret) {
ret = simple_write_to_buffer(snapshot->guc.buff, size, &pos, buf, size);
if (ret < 0 )
pf_free_guc_state(gt, snapshot);
else
pf_dump_guc_state(gt, snapshot);
}
mutex_unlock(pf_migration_mutex(gt));
return ret;
}
#endif /* CONFIG_DEBUG_FS */
static bool pf_check_migration_support(struct xe_gt *gt)
{
/* GuC 70.25 with save/restore v2 is required */
xe_gt_assert(gt, GUC_FIRMWARE_VER(>->uc.guc) >= MAKE_GUC_VER(70 , 25 , 0 ));
/* XXX: for now this is for feature enabling only */
return IS_ENABLED(CONFIG_DRM_XE_DEBUG);
}
/**
* xe_gt_sriov_pf_migration_init ( ) - Initialize support for VF migration .
* @ gt : the & xe_gt
*
* This function is for PF only .
*
* Return : 0 on success or a negative error code on failure .
*/
int xe_gt_sriov_pf_migration_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
int err;
xe_gt_assert(gt, IS_SRIOV_PF(xe));
gt->sriov.pf.migration.supported = pf_check_migration_support(gt);
if (!pf_migration_supported(gt))
return 0 ;
err = drmm_mutex_init(&xe->drm, >->sriov.pf.migration.snapshot_lock);
if (err)
return err;
return 0 ;
}
Messung V0.5 in Prozent C=97 H=97 G=96
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland