// SPDX-License-Identifier: MIT /* * Copyright © 2024 Intel Corporation */ #include #include "regs/xe_guc_regs.h" #include "abi/guc_actions_sriov_abi.h" #include "xe_bo.h" #include "xe_ggtt.h" #include "xe_gt.h" #include "xe_gt_sriov_pf.h" #include "xe_gt_sriov_pf_config.h" #include "xe_gt_sriov_pf_control.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_buf.h" #include "xe_guc_ct.h" #include "xe_migrate.h" #include "xe_mmio.h" #include "xe_sriov.h" #include "xe_sriov_packet.h" #include "xe_sriov_packet_types.h" #include "xe_sriov_pf_migration.h" #define XE_GT_SRIOV_PF_MIGRATION_RING_SIZE 5 static struct xe_gt_sriov_migration_data *pf_pick_gt_migration(struct xe_gt *gt, unsigned int vfid) { 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))); return >->sriov.pf.vfs[vfid].migration; } static void pf_dump_mig_data(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data, const char *what) { if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) { struct drm_printer p = xe_gt_dbg_printer(gt); drm_printf(&p, "VF%u %s (%llu bytes)\n", vfid, what, data->hdr.size); drm_print_hex_dump(&p, "mig_hdr: ", (void *)&data->hdr, sizeof(data->hdr)); drm_print_hex_dump(&p, "mig_data: ", data->vaddr, min(SZ_64, data->hdr.size)); } } static ssize_t pf_migration_ggtt_size(struct xe_gt *gt, unsigned int vfid) { if (!xe_gt_is_main_type(gt)) return 0; return xe_gt_sriov_pf_config_ggtt_save(gt, vfid, NULL, 0); } static int pf_save_vf_ggtt_mig_data(struct xe_gt *gt, unsigned int vfid) { struct xe_sriov_packet *data; size_t size; int ret; size = pf_migration_ggtt_size(gt, vfid); xe_gt_assert(gt, size); data = xe_sriov_packet_alloc(gt_to_xe(gt)); if (!data) return -ENOMEM; ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id, XE_SRIOV_PACKET_TYPE_GGTT, 0, size); if (ret) goto fail; ret = xe_gt_sriov_pf_config_ggtt_save(gt, vfid, data->vaddr, size); if (ret) goto fail; pf_dump_mig_data(gt, vfid, data, "GGTT data save"); ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data); if (ret) goto fail; return 0; fail: xe_sriov_packet_free(data); xe_gt_sriov_err(gt, "Failed to save VF%u GGTT data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } static int pf_restore_vf_ggtt_mig_data(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { int ret; pf_dump_mig_data(gt, vfid, data, "GGTT data restore"); ret = xe_gt_sriov_pf_config_ggtt_restore(gt, vfid, data->vaddr, data->hdr.size); if (ret) { xe_gt_sriov_err(gt, "Failed to restore VF%u GGTT data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } return 0; } /** * xe_gt_sriov_pf_migration_ggtt_save() - Save VF GGTT migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_ggtt_save(struct xe_gt *gt, unsigned int vfid) { 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))); return pf_save_vf_ggtt_mig_data(gt, vfid); } /** * xe_gt_sriov_pf_migration_ggtt_restore() - Restore VF GGTT migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * @data: the &xe_sriov_packet containing migration data * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_ggtt_restore(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { 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))); return pf_restore_vf_ggtt_mig_data(gt, vfid, data); } /* 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_mig_data_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_mig_data(struct xe_gt *gt, unsigned int vfid, void *dst, size_t size) { const int ndwords = size / sizeof(u32); struct xe_guc *guc = >->uc.guc; CLASS(xe_guc_buf, buf)(&guc->buf, ndwords); int ret; xe_gt_assert(gt, size % sizeof(u32) == 0); xe_gt_assert(gt, size == ndwords * sizeof(u32)); if (!xe_guc_buf_is_valid(buf)) return -ENOBUFS; /* FW expects this buffer to be zero-initialized */ memset(xe_guc_buf_cpu_ptr(buf), 0, size); ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_SAVE, xe_guc_buf_flush(buf), ndwords); if (!ret) ret = -ENODATA; else if (ret > ndwords) ret = -EPROTO; else if (ret > 0) memcpy(dst, xe_guc_buf_sync_read(buf), ret * sizeof(u32)); return ret; } /* Return: number of state dwords restored or a negative error code on failure */ static int pf_send_guc_restore_vf_mig_data(struct xe_gt *gt, unsigned int vfid, const void *src, size_t size) { const int ndwords = size / sizeof(u32); struct xe_guc *guc = >->uc.guc; CLASS(xe_guc_buf_from_data, buf)(&guc->buf, src, size); int ret; xe_gt_assert(gt, size % sizeof(u32) == 0); xe_gt_assert(gt, size == ndwords * sizeof(u32)); if (!xe_guc_buf_is_valid(buf)) return -ENOBUFS; ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_RESTORE, xe_guc_buf_flush(buf), ndwords); if (!ret) ret = -ENODATA; else if (ret > ndwords) ret = -EPROTO; return ret; } static bool pf_migration_supported(struct xe_gt *gt) { return xe_sriov_pf_migration_supported(gt_to_xe(gt)); } static int pf_save_vf_guc_mig_data(struct xe_gt *gt, unsigned int vfid) { struct xe_sriov_packet *data; size_t size; int ret; ret = pf_send_guc_query_vf_mig_data_size(gt, vfid); if (ret < 0) goto fail; size = ret * sizeof(u32); data = xe_sriov_packet_alloc(gt_to_xe(gt)); if (!data) { ret = -ENOMEM; goto fail; } ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id, XE_SRIOV_PACKET_TYPE_GUC, 0, size); if (ret) goto fail_free; ret = pf_send_guc_save_vf_mig_data(gt, vfid, data->vaddr, size); if (ret < 0) goto fail_free; size = ret * sizeof(u32); xe_gt_assert(gt, size); xe_gt_assert(gt, size <= data->hdr.size); data->hdr.size = size; data->remaining = size; pf_dump_mig_data(gt, vfid, data, "GuC data save"); ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data); if (ret) goto fail_free; return 0; fail_free: xe_sriov_packet_free(data); fail: xe_gt_sriov_err(gt, "Failed to save VF%u GuC data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } static ssize_t pf_migration_guc_size(struct xe_gt *gt, unsigned int vfid) { ssize_t size; if (!pf_migration_supported(gt)) return -ENOPKG; size = pf_send_guc_query_vf_mig_data_size(gt, vfid); if (size >= 0) size *= sizeof(u32); return size; } /** * xe_gt_sriov_pf_migration_guc_save() - Save VF GuC migration data. * @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_guc_save(struct xe_gt *gt, unsigned int vfid) { 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; return pf_save_vf_guc_mig_data(gt, vfid); } static int pf_restore_vf_guc_state(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { int ret; xe_gt_assert(gt, data->hdr.size); pf_dump_mig_data(gt, vfid, data, "GuC data restore"); ret = pf_send_guc_restore_vf_mig_data(gt, vfid, data->vaddr, data->hdr.size); if (ret < 0) goto fail; return 0; fail: xe_gt_sriov_err(gt, "Failed to restore VF%u GuC data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } /** * xe_gt_sriov_pf_migration_guc_restore() - Restore VF GuC migration data. * @gt: the &xe_gt * @vfid: the VF identifier * @data: the &xe_sriov_packet containing migration data * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_guc_restore(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { 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; return pf_restore_vf_guc_state(gt, vfid, data); } static ssize_t pf_migration_mmio_size(struct xe_gt *gt, unsigned int vfid) { if (xe_gt_is_media_type(gt)) return MED_VF_SW_FLAG_COUNT * sizeof(u32); else return VF_SW_FLAG_COUNT * sizeof(u32); } static int pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid, void *buf, size_t size) { struct xe_mmio mmio; u32 *regs = buf; int n; if (size != pf_migration_mmio_size(gt, vfid)) return -EINVAL; xe_mmio_init_vf_view(&mmio, >->mmio, vfid); if (xe_gt_is_media_type(gt)) for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++) regs[n] = xe_mmio_read32(>->mmio, MED_VF_SW_FLAG(n)); else for (n = 0; n < VF_SW_FLAG_COUNT; n++) regs[n] = xe_mmio_read32(>->mmio, VF_SW_FLAG(n)); return 0; } static int pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid, const void *buf, size_t size) { const u32 *regs = buf; struct xe_mmio mmio; int n; if (size != pf_migration_mmio_size(gt, vfid)) return -EINVAL; xe_mmio_init_vf_view(&mmio, >->mmio, vfid); if (xe_gt_is_media_type(gt)) for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++) xe_mmio_write32(>->mmio, MED_VF_SW_FLAG(n), regs[n]); else for (n = 0; n < VF_SW_FLAG_COUNT; n++) xe_mmio_write32(>->mmio, VF_SW_FLAG(n), regs[n]); return 0; } static int pf_save_vf_mmio_mig_data(struct xe_gt *gt, unsigned int vfid) { struct xe_sriov_packet *data; size_t size; int ret; size = pf_migration_mmio_size(gt, vfid); xe_gt_assert(gt, size); data = xe_sriov_packet_alloc(gt_to_xe(gt)); if (!data) return -ENOMEM; ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id, XE_SRIOV_PACKET_TYPE_MMIO, 0, size); if (ret) goto fail; ret = pf_migration_mmio_save(gt, vfid, data->vaddr, size); if (ret) goto fail; pf_dump_mig_data(gt, vfid, data, "MMIO data save"); ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data); if (ret) goto fail; return 0; fail: xe_sriov_packet_free(data); xe_gt_sriov_err(gt, "Failed to save VF%u MMIO data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } static int pf_restore_vf_mmio_mig_data(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { int ret; pf_dump_mig_data(gt, vfid, data, "MMIO data restore"); ret = pf_migration_mmio_restore(gt, vfid, data->vaddr, data->hdr.size); if (ret) { xe_gt_sriov_err(gt, "Failed to restore VF%u MMIO data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } return 0; } /** * xe_gt_sriov_pf_migration_mmio_save() - Save VF MMIO migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid) { 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))); return pf_save_vf_mmio_mig_data(gt, vfid); } /** * xe_gt_sriov_pf_migration_mmio_restore() - Restore VF MMIO migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * @data: the &xe_sriov_packet containing migration data * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { 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))); return pf_restore_vf_mmio_mig_data(gt, vfid, data); } static ssize_t pf_migration_vram_size(struct xe_gt *gt, unsigned int vfid) { if (!xe_gt_is_main_type(gt)) return 0; return xe_gt_sriov_pf_config_get_lmem(gt, vfid); } static struct dma_fence *__pf_save_restore_vram(struct xe_gt *gt, unsigned int vfid, struct xe_bo *vram, u64 vram_offset, struct xe_bo *sysmem, u64 sysmem_offset, size_t size, bool save) { struct dma_fence *ret = NULL; struct drm_exec exec; int err; drm_exec_init(&exec, 0, 0); drm_exec_until_all_locked(&exec) { err = drm_exec_lock_obj(&exec, &vram->ttm.base); drm_exec_retry_on_contention(&exec); if (err) { ret = ERR_PTR(err); goto err; } err = drm_exec_lock_obj(&exec, &sysmem->ttm.base); drm_exec_retry_on_contention(&exec); if (err) { ret = ERR_PTR(err); goto err; } } ret = xe_migrate_vram_copy_chunk(vram, vram_offset, sysmem, sysmem_offset, size, save ? XE_MIGRATE_COPY_TO_SRAM : XE_MIGRATE_COPY_TO_VRAM); err: drm_exec_fini(&exec); return ret; } #define PF_VRAM_SAVE_RESTORE_TIMEOUT (5 * HZ) static int pf_save_vram_chunk(struct xe_gt *gt, unsigned int vfid, struct xe_bo *src_vram, u64 src_vram_offset, size_t size) { struct xe_sriov_packet *data; struct dma_fence *fence; int ret; data = xe_sriov_packet_alloc(gt_to_xe(gt)); if (!data) return -ENOMEM; ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id, XE_SRIOV_PACKET_TYPE_VRAM, src_vram_offset, size); if (ret) goto fail; fence = __pf_save_restore_vram(gt, vfid, src_vram, src_vram_offset, data->bo, 0, size, true); if (IS_ERR(fence)) { ret = PTR_ERR(fence); goto fail; } ret = dma_fence_wait_timeout(fence, false, PF_VRAM_SAVE_RESTORE_TIMEOUT); dma_fence_put(fence); if (!ret) { ret = -ETIME; goto fail; } pf_dump_mig_data(gt, vfid, data, "VRAM data save"); ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data); if (ret) goto fail; return 0; fail: xe_sriov_packet_free(data); return ret; } #define VF_VRAM_STATE_CHUNK_MAX_SIZE SZ_512M static int pf_save_vf_vram_mig_data(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid); loff_t *offset = &migration->save.vram_offset; struct xe_bo *vram; size_t vram_size, chunk_size; int ret; vram = xe_gt_sriov_pf_config_get_lmem_obj(gt, vfid); if (!vram) return -ENXIO; vram_size = xe_bo_size(vram); xe_gt_assert(gt, *offset < vram_size); chunk_size = min(vram_size - *offset, VF_VRAM_STATE_CHUNK_MAX_SIZE); ret = pf_save_vram_chunk(gt, vfid, vram, *offset, chunk_size); if (ret) goto fail; *offset += chunk_size; xe_bo_put(vram); if (*offset < vram_size) return -EAGAIN; return 0; fail: xe_bo_put(vram); xe_gt_sriov_err(gt, "Failed to save VF%u VRAM data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } static int pf_restore_vf_vram_mig_data(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { u64 end = data->hdr.offset + data->hdr.size; struct dma_fence *fence; struct xe_bo *vram; size_t size; int ret = 0; vram = xe_gt_sriov_pf_config_get_lmem_obj(gt, vfid); if (!vram) return -ENXIO; size = xe_bo_size(vram); if (end > size || end < data->hdr.size) { ret = -EINVAL; goto err; } pf_dump_mig_data(gt, vfid, data, "VRAM data restore"); fence = __pf_save_restore_vram(gt, vfid, vram, data->hdr.offset, data->bo, 0, data->hdr.size, false); if (IS_ERR(fence)) { ret = PTR_ERR(fence); goto err; } ret = dma_fence_wait_timeout(fence, false, PF_VRAM_SAVE_RESTORE_TIMEOUT); dma_fence_put(fence); if (!ret) { ret = -ETIME; goto err; } xe_bo_put(vram); return 0; err: xe_bo_put(vram); xe_gt_sriov_err(gt, "Failed to restore VF%u VRAM data (%pe)\n", vfid, ERR_PTR(ret)); return ret; } /** * xe_gt_sriov_pf_migration_vram_save() - Save VF VRAM migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_vram_save(struct xe_gt *gt, unsigned int vfid) { 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))); return pf_save_vf_vram_mig_data(gt, vfid); } /** * xe_gt_sriov_pf_migration_vram_restore() - Restore VF VRAM migration data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * @data: the &xe_sriov_packet containing migration data * * This function is for PF only. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_vram_restore(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { 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))); return pf_restore_vf_vram_mig_data(gt, vfid, data); } /** * xe_gt_sriov_pf_migration_size() - Total size of migration data from all components within a GT. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * * This function is for PF only. * * Return: total migration data size in bytes or a negative error code on failure. */ ssize_t xe_gt_sriov_pf_migration_size(struct xe_gt *gt, unsigned int vfid) { ssize_t total = 0; ssize_t size; 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))); size = pf_migration_guc_size(gt, vfid); if (size < 0) return size; if (size > 0) size += sizeof(struct xe_sriov_packet_hdr); total += size; size = pf_migration_ggtt_size(gt, vfid); if (size < 0) return size; if (size > 0) size += sizeof(struct xe_sriov_packet_hdr); total += size; size = pf_migration_mmio_size(gt, vfid); if (size < 0) return size; if (size > 0) size += sizeof(struct xe_sriov_packet_hdr); total += size; size = pf_migration_vram_size(gt, vfid); if (size < 0) return size; if (size > 0) size += sizeof(struct xe_sriov_packet_hdr); total += size; return total; } /** * xe_gt_sriov_pf_migration_ring_empty() - Check if a migration ring is empty. * @gt: the &xe_gt * @vfid: the VF identifier * * Return: true if the ring is empty, otherwise false. */ bool xe_gt_sriov_pf_migration_ring_empty(struct xe_gt *gt, unsigned int vfid) { return ptr_ring_empty(&pf_pick_gt_migration(gt, vfid)->ring); } /** * xe_gt_sriov_pf_migration_ring_full() - Check if a migration ring is full. * @gt: the &xe_gt * @vfid: the VF identifier * * Return: true if the ring is full, otherwise false. */ bool xe_gt_sriov_pf_migration_ring_full(struct xe_gt *gt, unsigned int vfid) { return ptr_ring_full(&pf_pick_gt_migration(gt, vfid)->ring); } /** * xe_gt_sriov_pf_migration_ring_free() - Consume and free all data in migration ring * @gt: the &xe_gt * @vfid: the VF identifier */ void xe_gt_sriov_pf_migration_ring_free(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid); struct xe_sriov_packet *data; if (ptr_ring_empty(&migration->ring)) return; xe_gt_sriov_notice(gt, "VF%u unprocessed migration data left in the ring!\n", vfid); while ((data = ptr_ring_consume(&migration->ring))) xe_sriov_packet_free(data); } static void pf_migration_save_data_todo(struct xe_gt *gt, unsigned int vfid, enum xe_sriov_packet_type type) { set_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining); } /** * xe_gt_sriov_pf_migration_save_init() - Initialize per-GT migration related data. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) */ void xe_gt_sriov_pf_migration_save_init(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid); migration->save.data_remaining = 0; migration->save.vram_offset = 0; xe_gt_assert(gt, pf_migration_guc_size(gt, vfid) > 0); pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_GUC); if (pf_migration_ggtt_size(gt, vfid) > 0) pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_GGTT); xe_gt_assert(gt, pf_migration_mmio_size(gt, vfid) > 0); pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_MMIO); if (pf_migration_vram_size(gt, vfid) > 0) pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_VRAM); } /** * xe_gt_sriov_pf_migration_save_data_pending() - Check if migration data type needs to be saved. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * @type: the &xe_sriov_packet_type of data to be checked * * Return: true if the data needs saving, otherwise false. */ bool xe_gt_sriov_pf_migration_save_data_pending(struct xe_gt *gt, unsigned int vfid, enum xe_sriov_packet_type type) { return test_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining); } /** * xe_gt_sriov_pf_migration_save_data_complete() - Complete migration data type save. * @gt: the &xe_gt * @vfid: the VF identifier (can't be 0) * @type: the &xe_sriov_packet_type to be marked as completed. */ void xe_gt_sriov_pf_migration_save_data_complete(struct xe_gt *gt, unsigned int vfid, enum xe_sriov_packet_type type) { clear_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining); } /** * xe_gt_sriov_pf_migration_save_produce() - Add VF save data packet to migration ring. * @gt: the &xe_gt * @vfid: the VF identifier * @data: the &xe_sriov_packet * * Called by the save migration data producer (PF SR-IOV Control worker) when * processing migration data. * Wakes up the save migration data consumer (userspace), that is potentially * waiting for data when the ring was empty. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_save_produce(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { int ret; ret = ptr_ring_produce(&pf_pick_gt_migration(gt, vfid)->ring, data); if (ret) return ret; wake_up_all(xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid)); return 0; } /** * xe_gt_sriov_pf_migration_restore_consume() - Get VF restore data packet from migration ring. * @gt: the &xe_gt * @vfid: the VF identifier * * Called by the restore migration data consumer (PF SR-IOV Control worker) when * processing migration data. * Wakes up the restore migration data producer (userspace), that is * potentially waiting to add more data when the ring is full. * * Return: Pointer to &xe_sriov_packet on success, * NULL if ring is empty. */ struct xe_sriov_packet * xe_gt_sriov_pf_migration_restore_consume(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid); struct wait_queue_head *wq = xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid); struct xe_sriov_packet *data; data = ptr_ring_consume(&migration->ring); if (data) wake_up_all(wq); return data; } static bool pf_restore_data_ready(struct xe_gt *gt, unsigned int vfid) { if (xe_gt_sriov_pf_control_check_restore_failed(gt, vfid) || !ptr_ring_full(&pf_pick_gt_migration(gt, vfid)->ring)) return true; return false; } /** * xe_gt_sriov_pf_migration_restore_produce() - Add VF restore data packet to migration ring. * @gt: the &xe_gt * @vfid: the VF identifier * @data: the &xe_sriov_packet * * Called by the restore migration data producer (userspace) when processing * migration data. * If the ring is full, waits until there is space. * Queues the restore migration data consumer (PF SR-IOV Control worker), that * is potentially waiting for data when the ring was empty. * * Return: 0 on success or a negative error code on failure. */ int xe_gt_sriov_pf_migration_restore_produce(struct xe_gt *gt, unsigned int vfid, struct xe_sriov_packet *data) { int ret; xe_gt_assert(gt, data->hdr.tile_id == gt->tile->id); xe_gt_assert(gt, data->hdr.gt_id == gt->info.id); for (;;) { if (xe_gt_sriov_pf_control_check_restore_failed(gt, vfid)) return -EIO; ret = ptr_ring_produce(&pf_pick_gt_migration(gt, vfid)->ring, data); if (!ret) break; ret = wait_event_interruptible(*xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid), pf_restore_data_ready(gt, vfid)); if (ret) return ret; } return xe_gt_sriov_pf_control_process_restore_data(gt, vfid); } /** * xe_gt_sriov_pf_migration_save_consume() - Get VF save data packet from migration ring. * @gt: the &xe_gt * @vfid: the VF identifier * * Called by the save migration data consumer (userspace) when * processing migration data. * Queues the save migration data producer (PF SR-IOV Control worker), that is * potentially waiting to add more data when the ring is full. * * Return: Pointer to &xe_sriov_packet on success, * NULL if ring is empty and there's no more data available, * ERR_PTR(-EAGAIN) if the ring is empty, but data is still produced. */ struct xe_sriov_packet * xe_gt_sriov_pf_migration_save_consume(struct xe_gt *gt, unsigned int vfid) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid); struct xe_sriov_packet *data; int ret; data = ptr_ring_consume(&migration->ring); if (data) { ret = xe_gt_sriov_pf_control_process_save_data(gt, vfid); if (ret) { xe_sriov_packet_free(data); return ERR_PTR(ret); } return data; } if (xe_gt_sriov_pf_control_check_save_data_done(gt, vfid)) return NULL; if (xe_gt_sriov_pf_control_check_save_failed(gt, vfid)) return ERR_PTR(-EIO); return ERR_PTR(-EAGAIN); } static void destroy_pf_packet(void *ptr) { struct xe_sriov_packet *data = ptr; xe_sriov_packet_free(data); } static void action_ring_cleanup(void *arg) { struct ptr_ring *r = arg; ptr_ring_cleanup(r, destroy_pf_packet); } static void pf_gt_migration_check_support(struct xe_gt *gt) { if (GUC_FIRMWARE_VER(>->uc.guc) < MAKE_GUC_VER(70, 54, 0)) xe_sriov_pf_migration_disable(gt_to_xe(gt), "requires GuC version >= 70.54.0"); } /** * 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); unsigned int n, totalvfs; int err; xe_gt_assert(gt, IS_SRIOV_PF(xe)); pf_gt_migration_check_support(gt); if (!pf_migration_supported(gt)) return 0; totalvfs = xe_sriov_pf_get_totalvfs(xe); for (n = 1; n <= totalvfs; n++) { struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, n); err = ptr_ring_init(&migration->ring, XE_GT_SRIOV_PF_MIGRATION_RING_SIZE, GFP_KERNEL); if (err) return err; err = devm_add_action_or_reset(xe->drm.dev, action_ring_cleanup, &migration->ring); if (err) return err; } return 0; }