Add an "x-vf-migration" property to the IGB PF device and expose a DVSEC (Designated Vendor-Specific Extended Capability) at offset 0x160 in VF extended config space when migration is enabled.
The DVSEC provides the register interface for VF live migration: - CAPS: supported features (state migration) - CTRL: command doorbell - STATUS: state and error reporting - BUF_ADDR: shared DMA buffer address (GPA) Move IgbVfState from igbvf.c to igb_common.h so it can be shared with the migration module, and add the migration state field. AI-used-for: code (prototype) Signed-off-by: Cédric Le Goater <[email protected]> --- MAINTAINERS | 5 ++ hw/net/igb_common.h | 16 +++++++ hw/net/igb_migration.h | 72 ++++++++++++++++++++++++++++ hw/net/igb.c | 2 + hw/net/igb_migration.c | 106 +++++++++++++++++++++++++++++++++++++++++ hw/net/igbvf.c | 52 ++++++++++++++++---- hw/net/meson.build | 2 +- 7 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 hw/net/igb_migration.h create mode 100644 hw/net/igb_migration.c diff --git a/MAINTAINERS b/MAINTAINERS index 4a49a40294eb..f88b526be238 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2816,6 +2816,11 @@ F: tests/functional/x86_64/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c +igb VF migration +M: Cédric Le Goater <[email protected]> +S: Maintained +F: hw/net/igb_migration.* + eepro100 M: Stefan Weil <[email protected]> S: Maintained diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h index b316a5bcfa5c..f0e2529e757b 100644 --- a/hw/net/igb_common.h +++ b/hw/net/igb_common.h @@ -26,7 +26,9 @@ #ifndef HW_NET_IGB_COMMON_H #define HW_NET_IGB_COMMON_H +#include "hw/pci/pci_device.h" #include "igb_regs.h" +#include "igb_migration.h" #define TYPE_IGBVF "igbvf" @@ -154,4 +156,18 @@ uint64_t igb_mmio_read(void *opaque, hwaddr addr, unsigned size); void igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); void igb_vf_reset(void *opaque, uint16_t vfn); +OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF) + +struct IgbVfState { + PCIDevice parent_obj; + + uint16_t vfn; + bool migration_enabled; + + MemoryRegion mmio; + MemoryRegion msix; + + IgbVfMigState mig; +}; + #endif diff --git a/hw/net/igb_migration.h b/hw/net/igb_migration.h new file mode 100644 index 000000000000..3da28e11e49e --- /dev/null +++ b/hw/net/igb_migration.h @@ -0,0 +1,72 @@ +/* + * QEMU Intel 82576 SR/IOV VF Migration Support + * + * Copyright (c) 2026 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_NET_IGB_MIGRATION_H +#define HW_NET_IGB_MIGRATION_H + +#include "hw/pci/pci_device.h" + +/* + * Migration interface exposed as a DVSEC (Designated Vendor-Specific + * Extended Capability) in VF extended config space. + * + * DVSEC layout at IGB_MIG_DVSEC_OFFSET (0x160): + * + * +0x00 PCIe extended cap header (cap_id=0x23, ver=1, next) + * +0x04 DVSEC header 1 (len | rev | vendor_id) + * +0x08 DVSEC header 2 (DVSEC ID) + * +0x0A Reserved (padding for DWORD alignment) + * +0x0C CAPS (RO: F_STATE[0]) + * +0x10 CTRL (WO: doorbell command) + * +0x14 STATUS (RO: state[7:0], error_code[15:8]) + * +0x18 BUF_ADDR_LO (RW: shared buffer GPA low) + * +0x1C BUF_ADDR_HI (RW: shared buffer GPA high) + */ + +#define IGB_MIG_DVSEC_OFFSET 0x160 +#define IGB_MIG_DVSEC_SIZE 0x20 +#define IGB_MIG_DVSEC_VER 1 +#define IGB_MIG_DVSEC_ID 1 + +/* Register offsets relative to DVSEC base */ +#define IGB_MIG_CAPS 0x0C +#define IGB_MIG_CTRL 0x10 +#define IGB_MIG_STATUS 0x14 +#define IGB_MIG_BUF_ADDR_LO 0x18 +#define IGB_MIG_BUF_ADDR_HI 0x1C + +/* CAPS register layout */ +#define IGB_MIG_CAP_F_STATE (1u << 0) + +/* STATUS register: state in [7:0], error code [15:8] */ +#define IGB_MIG_STATUS_STATE_MASK 0xFF +#define IGB_MIG_STATUS_ERROR_CODE_SHIFT 8 +#define IGB_MIG_STATUS_ERR(code) \ + ((uint32_t)(code) << IGB_MIG_STATUS_ERROR_CODE_SHIFT) + +/* Device states (based on VFIO migration v2) */ +#define IGB_MIG_STATE_ERROR 0 +#define IGB_MIG_STATE_STOP 1 +#define IGB_MIG_STATE_RUNNING 2 +#define IGB_MIG_STATE_STOP_COPY 3 +#define IGB_MIG_STATE_RESUMING 4 + +typedef struct IgbVfMigState { + uint32_t mig_state; + uint64_t mig_data_buf_addr; +} IgbVfMigState; + +typedef struct IgbVfState IgbVfState; + +bool igbvf_add_migration_dvsec(PCIDevice *dev, Error **errp); +void igbvf_mig_state_reset(IgbVfState *s); +uint32_t igbvf_mig_config_read(IgbVfState *s, uint32_t addr, int size); +bool igbvf_mig_config_write(IgbVfState *s, uint32_t addr, uint32_t val, + int size); + +#endif diff --git a/hw/net/igb.c b/hw/net/igb.c index c076807e7110..7268e5473fc3 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -79,6 +79,7 @@ struct IGBState { IGBCore core; bool has_flr; + bool vf_migration; }; #define IGB_CAP_SRIOV_OFFSET (0x160) @@ -597,6 +598,7 @@ static const VMStateDescription igb_vmstate = { static const Property igb_properties[] = { DEFINE_NIC_PROPERTIES(IGBState, conf), DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), + DEFINE_PROP_BOOL("x-vf-migration", IGBState, vf_migration, false), }; static void igb_class_init(ObjectClass *class, const void *data) diff --git a/hw/net/igb_migration.c b/hw/net/igb_migration.c new file mode 100644 index 000000000000..4dfebd82344c --- /dev/null +++ b/hw/net/igb_migration.c @@ -0,0 +1,106 @@ +/* + * QEMU Intel 82576 SR/IOV VF Migration Support + * + * Copyright (c) 2026 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pcie.h" +#include "igb_common.h" +#include "igb_migration.h" + +static void igbvf_mig_update_status(IgbVfState *s, uint8_t err) +{ + IgbVfMigState *ms = &s->mig; + PCIDevice *dev = PCI_DEVICE(s); + uint32_t status; + + status = ms->mig_state & IGB_MIG_STATUS_STATE_MASK; + + if (err) { + status = IGB_MIG_STATE_ERROR | IGB_MIG_STATUS_ERR(err); + } + + pci_set_long(dev->config + IGB_MIG_DVSEC_OFFSET + IGB_MIG_STATUS, status); +} + + +bool igbvf_add_migration_dvsec(PCIDevice *dev, Error **errp) +{ + uint16_t offset = IGB_MIG_DVSEC_OFFSET; + uint32_t caps; + + pcie_add_capability(dev, PCI_EXT_CAP_ID_DVSEC, 1, offset, + IGB_MIG_DVSEC_SIZE); + + /* DVSEC header 1: length[31:20] | rev[19:16] | vendor_id[15:0] */ + pci_set_long(dev->config + offset + 0x4, + (IGB_MIG_DVSEC_SIZE << 20) | + (IGB_MIG_DVSEC_VER << 16) | + PCI_VENDOR_ID_INTEL); + + /* DVSEC header 2: DVSEC ID */ + pci_set_word(dev->config + offset + 0x8, IGB_MIG_DVSEC_ID); + + /* CAPS: features (state migration only) */ + caps = IGB_MIG_CAP_F_STATE; + pci_set_long(dev->config + offset + IGB_MIG_CAPS, caps); + + /* STATUS: initial state is RUNNING */ + pci_set_long(dev->config + offset + IGB_MIG_STATUS, + IGB_MIG_STATE_RUNNING); + + /* BUF_ADDR_LO and BUF_ADDR_HI are writable */ + memset(dev->wmask + offset + IGB_MIG_BUF_ADDR_LO, 0xff, 4); + memset(dev->wmask + offset + IGB_MIG_BUF_ADDR_HI, 0xff, 4); + + return true; +} + +uint32_t igbvf_mig_config_read(IgbVfState *s, uint32_t addr, int size) +{ + PCIDevice *dev = PCI_DEVICE(s); + + return pci_default_read_config(dev, addr, size); +} + +bool igbvf_mig_config_write(IgbVfState *s, uint32_t addr, uint32_t val, + int size) +{ + PCIDevice *dev = PCI_DEVICE(s); + uint32_t offset = addr - IGB_MIG_DVSEC_OFFSET; + + switch (offset) { + case IGB_MIG_CTRL: + /* Command handling will be added in a later commit */ + break; + + case IGB_MIG_BUF_ADDR_LO: + case IGB_MIG_BUF_ADDR_HI: + pci_default_write_config(dev, addr, val, size); + break; + + default: + break; + } + + return true; +} + +void igbvf_mig_state_reset(IgbVfState *s) +{ + IgbVfMigState *ms = &s->mig; + + ms->mig_state = IGB_MIG_STATE_RUNNING; + ms->mig_data_buf_addr = 0; + + pci_set_long(PCI_DEVICE(s)->config + + IGB_MIG_DVSEC_OFFSET + IGB_MIG_BUF_ADDR_LO, 0); + pci_set_long(PCI_DEVICE(s)->config + + IGB_MIG_DVSEC_OFFSET + IGB_MIG_BUF_ADDR_HI, 0); + + igbvf_mig_update_status(s, 0); +} diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 9a165c7063ee..30dfdb574ac7 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -38,27 +38,21 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "hw/core/hw-error.h" #include "hw/net/mii.h" #include "hw/pci/pci_device.h" #include "hw/pci/pcie.h" +#include "hw/pci/pcie_sriov.h" #include "hw/pci/msix.h" #include "net/eth.h" #include "net/net.h" #include "igb_common.h" #include "igb_core.h" +#include "igb_migration.h" #include "trace.h" #include "qapi/error.h" -OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF) - -struct IgbVfState { - PCIDevice parent_obj; - - MemoryRegion mmio; - MemoryRegion msix; -}; - static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, bool write) { switch (addr) { @@ -199,10 +193,35 @@ static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, bool write) return HWADDR_MAX; } +static bool igbvf_addr_in_dvsec(uint32_t addr, int len) +{ + return ranges_overlap(addr, len, + IGB_MIG_DVSEC_OFFSET, IGB_MIG_DVSEC_SIZE); +} + +static uint32_t igbvf_read_config(PCIDevice *dev, uint32_t addr, int size) +{ + IgbVfState *s = IGBVF(dev); + + if (s->migration_enabled && igbvf_addr_in_dvsec(addr, size)) { + return igbvf_mig_config_read(s, addr, size); + } + + return pci_default_read_config(dev, addr, size); +} + static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { + IgbVfState *s = IGBVF(dev); + trace_igbvf_write_config(addr, val, len); + + if (s->migration_enabled && igbvf_addr_in_dvsec(addr, len)) { + igbvf_mig_config_write(s, addr, val, len); + return; + } + pci_default_write_config(dev, addr, val, len); if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), "x-pcie-flr-init", &error_abort)) { @@ -282,13 +301,27 @@ static void igbvf_pci_realize(PCIDevice *dev, Error **errp) } pcie_ari_init(dev, 0x150); + + if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)), + "x-vf-migration", &error_abort)) { + s->vfn = pcie_sriov_vf_number(dev); + s->migration_enabled = true; + if (!igbvf_add_migration_dvsec(dev, errp)) { + return; + } + } } static void igbvf_qdev_reset_hold(Object *obj, ResetType type) { PCIDevice *vf = PCI_DEVICE(obj); + IgbVfState *s = IGBVF(vf); igb_vf_reset(pcie_sriov_get_pf(vf), pcie_sriov_vf_number(vf)); + + if (s->migration_enabled) { + igbvf_mig_state_reset(s); + } } static void igbvf_pci_uninit(PCIDevice *dev) @@ -309,6 +342,7 @@ static void igbvf_class_init(ObjectClass *class, const void *data) c->realize = igbvf_pci_realize; c->exit = igbvf_pci_uninit; + c->config_read = igbvf_read_config; c->vendor_id = PCI_VENDOR_ID_INTEL; c->device_id = E1000_DEV_ID_82576_VF; c->revision = 1; diff --git a/hw/net/meson.build b/hw/net/meson.build index 84f142df222a..bb4b449b25ba 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -11,7 +11,7 @@ system_ss.add(when: 'CONFIG_E1000_PCI', if_true: files('e1000.c', 'e1000x_common system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 'e1000e_core.c', 'e1000x_common.c')) system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c')) +system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 'igbvf.c', 'igb_core.c', 'igb_migration.c')) system_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c')) system_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c')) system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 'net_rx_pkt.c')) -- 2.55.0
