mirror of
https://github.com/torvalds/linux.git
synced 2026-04-25 10:02:31 -04:00
To properly support VF Save/Restore procedure, fixups need to be applied after PF driver finishes its part of VF Restore. The fixups are required to adjust the ongoing execution for a hardware switch that happened, because some GFX resources are not fully virtualized, and assigned to a VF as range from a global pool. The VF on which a VM is restored will often have different ranges provisioned than the VF on which save process happened. Those resource fixups are applied by the VF driver within a restored VM. A VF driver gets informed that it was migrated by receiving an interrupt from each GuC. The interrupt assigned for that purpose is "GUC SW interrupt 0". Seeing that fields set from within the irq handler should be the trigger for fixups. The VF can safely do post-migration fixups on resources associated to each GuC only after that GuC issued the MIGRATED interrupt. This change introduces a worker to be used for post-migration fixups, and a mechanism to schedule said worker when all GuCs sent the irq. v2: renamed and moved functions, updated logged messages, removed unused includes, used anon struct (Michal) v3: ordering, kerneldoc, asserts, debug messages, on_all_tiles -> on_all_gts (Michal) v4: fixed missing header include v5: Explained what fixups are, explained which IRQ is used, style fixes (Michal) Bspec: 50868 Signed-off-by: Tomasz Lis <tomasz.lis@intel.com> Reviewed-by: Michal Wajdeczko <michal.wajdeczko@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20241104213449.1455694-2-tomasz.lis@intel.com Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
160 lines
4.0 KiB
C
160 lines
4.0 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2023 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/fault-inject.h>
|
|
|
|
#include <drm/drm_managed.h>
|
|
|
|
#include "regs/xe_regs.h"
|
|
|
|
#include "xe_assert.h"
|
|
#include "xe_device.h"
|
|
#include "xe_mmio.h"
|
|
#include "xe_sriov.h"
|
|
#include "xe_sriov_pf.h"
|
|
#include "xe_sriov_vf.h"
|
|
|
|
/**
|
|
* xe_sriov_mode_to_string - Convert enum value to string.
|
|
* @mode: the &xe_sriov_mode to convert
|
|
*
|
|
* Returns: SR-IOV mode as a user friendly string.
|
|
*/
|
|
const char *xe_sriov_mode_to_string(enum xe_sriov_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case XE_SRIOV_MODE_NONE:
|
|
return "none";
|
|
case XE_SRIOV_MODE_PF:
|
|
return "SR-IOV PF";
|
|
case XE_SRIOV_MODE_VF:
|
|
return "SR-IOV VF";
|
|
default:
|
|
return "<invalid>";
|
|
}
|
|
}
|
|
|
|
static bool test_is_vf(struct xe_device *xe)
|
|
{
|
|
u32 value = xe_mmio_read32(xe_root_tile_mmio(xe), VF_CAP_REG);
|
|
|
|
return value & VF_CAP;
|
|
}
|
|
|
|
/**
|
|
* xe_sriov_probe_early - Probe a SR-IOV mode.
|
|
* @xe: the &xe_device to probe mode on
|
|
*
|
|
* This function should be called only once and as soon as possible during
|
|
* driver probe to detect whether we are running a SR-IOV Physical Function
|
|
* (PF) or a Virtual Function (VF) device.
|
|
*
|
|
* SR-IOV PF mode detection is based on PCI @dev_is_pf() function.
|
|
* SR-IOV VF mode detection is based on dedicated MMIO register read.
|
|
*/
|
|
void xe_sriov_probe_early(struct xe_device *xe)
|
|
{
|
|
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
|
|
enum xe_sriov_mode mode = XE_SRIOV_MODE_NONE;
|
|
bool has_sriov = xe->info.has_sriov;
|
|
|
|
if (has_sriov) {
|
|
if (test_is_vf(xe))
|
|
mode = XE_SRIOV_MODE_VF;
|
|
else if (xe_sriov_pf_readiness(xe))
|
|
mode = XE_SRIOV_MODE_PF;
|
|
} else if (pci_sriov_get_totalvfs(pdev)) {
|
|
/*
|
|
* Even if we have not enabled SR-IOV support using the
|
|
* platform specific has_sriov flag, the hardware may still
|
|
* report SR-IOV capability and the PCI layer may wrongly
|
|
* advertise driver support to enable VFs. Explicitly reset
|
|
* the number of supported VFs to zero to avoid confusion.
|
|
*/
|
|
drm_info(&xe->drm, "Support for SR-IOV is not available\n");
|
|
pci_sriov_set_totalvfs(pdev, 0);
|
|
}
|
|
|
|
xe_assert(xe, !xe->sriov.__mode);
|
|
xe->sriov.__mode = mode;
|
|
xe_assert(xe, xe->sriov.__mode);
|
|
|
|
if (has_sriov)
|
|
drm_info(&xe->drm, "Running in %s mode\n",
|
|
xe_sriov_mode_to_string(xe_device_sriov_mode(xe)));
|
|
}
|
|
|
|
static void fini_sriov(struct drm_device *drm, void *arg)
|
|
{
|
|
struct xe_device *xe = arg;
|
|
|
|
destroy_workqueue(xe->sriov.wq);
|
|
xe->sriov.wq = NULL;
|
|
}
|
|
|
|
/**
|
|
* xe_sriov_init - Initialize SR-IOV specific data.
|
|
* @xe: the &xe_device to initialize
|
|
*
|
|
* In this function we create dedicated workqueue that will be used
|
|
* by the SR-IOV specific workers.
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int xe_sriov_init(struct xe_device *xe)
|
|
{
|
|
if (!IS_SRIOV(xe))
|
|
return 0;
|
|
|
|
if (IS_SRIOV_PF(xe)) {
|
|
int err = xe_sriov_pf_init_early(xe);
|
|
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (IS_SRIOV_VF(xe))
|
|
xe_sriov_vf_init_early(xe);
|
|
|
|
xe_assert(xe, !xe->sriov.wq);
|
|
xe->sriov.wq = alloc_workqueue("xe-sriov-wq", 0, 0);
|
|
if (!xe->sriov.wq)
|
|
return -ENOMEM;
|
|
|
|
return drmm_add_action_or_reset(&xe->drm, fini_sriov, xe);
|
|
}
|
|
ALLOW_ERROR_INJECTION(xe_sriov_init, ERRNO); /* See xe_pci_probe() */
|
|
|
|
/**
|
|
* xe_sriov_print_info - Print basic SR-IOV information.
|
|
* @xe: the &xe_device to print info from
|
|
* @p: the &drm_printer
|
|
*
|
|
* Print SR-IOV related information into provided DRM printer.
|
|
*/
|
|
void xe_sriov_print_info(struct xe_device *xe, struct drm_printer *p)
|
|
{
|
|
drm_printf(p, "supported: %s\n", str_yes_no(xe_device_has_sriov(xe)));
|
|
drm_printf(p, "enabled: %s\n", str_yes_no(IS_SRIOV(xe)));
|
|
drm_printf(p, "mode: %s\n", xe_sriov_mode_to_string(xe_device_sriov_mode(xe)));
|
|
}
|
|
|
|
/**
|
|
* xe_sriov_function_name() - Get SR-IOV Function name.
|
|
* @n: the Function number (identifier) to get name of
|
|
* @buf: the buffer to format to
|
|
* @size: size of the buffer (shall be at least 5 bytes)
|
|
*
|
|
* Return: formatted function name ("PF" or "VF%u").
|
|
*/
|
|
const char *xe_sriov_function_name(unsigned int n, char *buf, size_t size)
|
|
{
|
|
if (n)
|
|
snprintf(buf, size, "VF%u", n);
|
|
else
|
|
strscpy(buf, "PF", size);
|
|
return buf;
|
|
}
|