pci: synchronize VfioMsix::cap and VfioMsix::bar

Currently, when snapshoting a running VFIO device with MSI-X enabled,
we get a snapshot where `msix_config.state.enabled` is not consistent
with `msix_state.cap.msg_ctl`,

```jsonc
{
  "snapshots": {
    "vfio_common": {
      "snapshots": {
        "msix_config": {
          "snapshots": {},
          "state": {
            "enabled": true
            // ...
          }
        },
        // ..
      },
      "state": {
        "msix_state": {
          "cap": {
            "msg_ctl": 3,
            "table": 1,
            "pba": 2049
          },
          // ...
        }
        // ...
      }
    }
  },
  // ...
}
```

The root cause is, after a `MsixCap` is parsed from the device PCI
config space and propagated to a corresponding `MsixConfig`,
`MsixCap::msg_ctl` is never get updated at runtime, only
`MsixConfig::msg_ctl` is updated.

This commit makes `VfioMsix::update` update both `VfioMsix::bar` (of
type `MsixConfig`) and `VfioMsix::cap` (of type `MsixCap`).

Signed-off-by: Changyuan Lyu <changyuanl@google.com>
This commit is contained in:
Changyuan Lyu
2026-04-10 11:49:05 -07:00
committed by Rob Bradford
parent bdc7a6947d
commit 490e338e16

View File

@@ -157,11 +157,14 @@ impl VfioMsix {
// Update "Message Control" word
if offset == 2 && data.len() == 2 {
self.bar.set_msg_ctl(LittleEndian::read_u16(data));
let data = LittleEndian::read_u16(data);
self.bar.set_msg_ctl(data);
self.cap.set_msg_ctl(data);
} else if offset == 0 && data.len() == 4 {
// Some guests update MSI-X control through the dword config write path.
self.bar
.set_msg_ctl((LittleEndian::read_u32(data) >> 16) as u16);
let data = (LittleEndian::read_u32(data) >> 16) as u16;
self.bar.set_msg_ctl(data);
self.cap.set_msg_ctl(data);
}
let new_enabled = self.bar.enabled();