stratovirt/machine-add-realization-for-multifunction.patch

637 lines
26 KiB
Diff
Raw Normal View History

From 4e8272a490b9901fc92fbaad1c10fee6d8b43fe3 Mon Sep 17 00:00:00 2001
From: Ming Yang <yangming73@huawei.com>
Date: Sat, 28 Aug 2021 10:33:25 +0800
Subject: [PATCH 10/10] machine: add realization for multifunction.
1. Add realization for multifunction of virtio pci devices,
vfio pci devices and pcie-root-port.
2. Add docs for multifunction.
Signed-off-by: Xinle.Guo <guoxinle1@huawei.com>
Signed-off-by: Ming Yang <yangming73@huawei.com>
---
docs/config_guidebook.md | 14 ++--
machine/src/lib.rs | 93 +++++++++++++++++-----
machine/src/standard_vm/x86_64/ich9_lpc.rs | 10 ++-
pci/src/host.rs | 6 +-
pci/src/root_port.rs | 29 +++++--
vfio/src/vfio_pci.rs | 31 ++++++--
virtio/src/virtio_pci.rs | 53 +++++++++++-
7 files changed, 192 insertions(+), 44 deletions(-)
diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md
index b1f7b08..946d155 100644
--- a/docs/config_guidebook.md
+++ b/docs/config_guidebook.md
@@ -212,7 +212,7 @@ is a single function device, the function number should be set to zero.
-device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC]
# virtio pci net device
-netdev tap,id=netdevid,ifname=host_dev_name
--device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,iothread=iothread1,mac=12:34:56:78:9A:BC]
+-device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC]
```
StratoVirt also supports vhost-net to get a higher performance in network. It can be set by
@@ -227,7 +227,7 @@ given when `vhost=on`, StratoVirt gets it by opening "/dev/vhost-net" automatica
-device virtio-net-device,netdev=netdevid,id=netid[,iothread=iothread1,mac=12:34:56:78:9A:BC]
# virtio pci net device
-netdev tap,id=netdevid,ifname=host_dev_name,vhost=on[,vhostfd=2]
--device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,iothread=iothread1,mac=12:34:56:78:9A:BC]
+-device virtio-net-pci,netdev=netdevid,id=netid,bus=pcie.0,addr=0x2.0x0[,multifunction=on,iothread=iothread1,mac=12:34:56:78:9A:BC]
```
*How to set a tap device?*
@@ -273,7 +273,7 @@ of device and the second one represents function number of it.
-device virtconsole,chardev=virtioconsole1,id=console_id
# virtio pci device
--device virtio-serial-pci,bus=pcie.0,addr=0x1[,id=virtio-serial0]
+-device virtio-serial-pci,bus=pcie.0,addr=0x1.0x0[,multifunction=on,id=virtio-serial0]
-chardev socket,path=socket_path,id=virtioconsole1,server,nowait
-device virtconsole,chardev=virtioconsole1,id=console_id
```
@@ -307,7 +307,7 @@ of device and the second one represents function number of it.
-device vhost-vsock-device,id=vsock_id,guest-cid=3
# virtio pci device.
--device vhost-vsock-pci,id=vsock_id,guest-cid=3,bus=pcie.0,addr=0x1.0x0
+-device vhost-vsock-pci,id=vsock_id,guest-cid=3,bus=pcie.0,addr=0x1.0x0[,multifunction=on]
```
*You can only set one virtio vsock device for one VM.*
@@ -363,7 +363,7 @@ of device and the second one represents function number of it.
# virtio mmio balloon device
-device virtio-balloon-device,deflate-on-oom=true
# virtio pci balloon device
--device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,deflate-on-oom=true
+-device virtio-balloon-pci,bus=pcie.0,addr=0x4.0x0,deflate-on-oom=true[,multifunction=on]
```
### 2.8 Virtio-rng
@@ -396,7 +396,7 @@ single function device, the function number should be set to zero.
-device virtio-rng-device,rng=objrng0,max-bytes=1234,period=1000
# virtio pci rng device
-object rng-random,id=objrng0,filename=/path/to/random_file
--device virtio-rng-pci,rng=objrng0,max-bytes=1234,period=1000,bus=pcie.0,addr=0x1
+-device virtio-rng-pci,rng=objrng0,max-bytes=1234,period=1000,bus=pcie.0,addr=0x1.0x0[,multifunction=on]
```
### 2.9 PCIe root port
@@ -458,7 +458,7 @@ Four properties are supported for VFIO device
* addr: including slot number and function number.
```shell
# cmdline
--device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03
+-device vfio-pci,host=0000:1a:00.3,id=net,bus=pcie.0,addr=0x03.0x0[,multifunction=on]
```
### 2.12 Chardev
diff --git a/machine/src/lib.rs b/machine/src/lib.rs
index 22aed89..23394ef 100644
--- a/machine/src/lib.rs
+++ b/machine/src/lib.rs
@@ -123,9 +123,9 @@ use devices::InterruptController;
use hypervisor::KVM_FDS;
use kvm_ioctls::VcpuFd;
use machine_manager::config::{
- get_pci_bdf, parse_balloon, parse_blk, parse_net, parse_rng_dev, parse_root_port, parse_vfio,
- parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, PFlashConfig, PciBdf,
- SerialConfig, VfioConfig, VmConfig,
+ get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_net, parse_rng_dev,
+ parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock,
+ MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig,
};
use machine_manager::event_loop::EventLoop;
use machine_manager::machine::{KvmVmState, MachineInterface};
@@ -297,9 +297,16 @@ pub trait MachineOps {
);
} else {
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
- let virtio_pci_device =
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem, vsock.clone(), parent_bus);
+ let virtio_pci_device = VirtioPciDevice::new(
+ device_cfg.id,
+ devfn,
+ sys_mem,
+ vsock.clone(),
+ parent_bus,
+ multi_func,
+ );
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci vsock device")?;
@@ -342,9 +349,11 @@ pub trait MachineOps {
} else {
let name = device_cfg.id;
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
- let virtio_pci_device = VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus);
+ let virtio_pci_device =
+ VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func);
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci balloon device")?;
@@ -373,16 +382,24 @@ pub trait MachineOps {
);
} else {
let name = device_cfg.id;
- let bdf = if let Some(virtio_serial_info) = &vm_config.virtio_serial {
- // Reasonable, because for virtio-serial-pci device, the bdf has been checked.
- virtio_serial_info.pci_bdf.clone().unwrap()
+ let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial {
+ serial_info
} else {
- bail!("No pci BDF configured for virtconsole");
+ bail!("No virtio-serial-pci device configured for virtconsole");
};
+ // Reasonable, because for virtio-serial-pci device, the bdf has been checked.
+ let bdf = virtio_serial_info.pci_bdf.clone().unwrap();
+ let multi_func = virtio_serial_info.multifunction;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
- let virtio_pci_device =
- VirtioPciDevice::new(name, devfn, sys_mem, console.clone(), parent_bus);
+ let virtio_pci_device = VirtioPciDevice::new(
+ name,
+ devfn,
+ sys_mem,
+ console.clone(),
+ parent_bus,
+ multi_func,
+ );
virtio_pci_device
.realize()
.chain_err(|| "Failed to add virtio pci console device")?;
@@ -416,10 +433,17 @@ pub trait MachineOps {
.chain_err(|| "Failed to add virtio mmio rng device")?;
} else {
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem().clone();
- let vitio_pci_device =
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem, rng_dev.clone(), parent_bus);
+ let vitio_pci_device = VirtioPciDevice::new(
+ device_cfg.id,
+ devfn,
+ sys_mem,
+ rng_dev.clone(),
+ parent_bus,
+ multi_func,
+ );
vitio_pci_device
.realize()
.chain_err(|| "Failed to add pci rng device")?;
@@ -434,6 +458,7 @@ pub trait MachineOps {
fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem();
let device_cfg = parse_blk(vm_config, cfg_args)?;
@@ -444,6 +469,7 @@ pub trait MachineOps {
sys_mem.clone(),
device.clone(),
parent_bus,
+ multi_func,
);
pcidev
.realize()
@@ -454,19 +480,34 @@ pub trait MachineOps {
fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let sys_mem = self.get_sys_mem();
let device_cfg = parse_net(vm_config, cfg_args)?;
let virtio_pci_device = if device_cfg.vhost_type.is_some() {
let device = Arc::new(Mutex::new(VhostKern::Net::new(&device_cfg, &sys_mem)));
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem.clone(), device, parent_bus)
+ VirtioPciDevice::new(
+ device_cfg.id,
+ devfn,
+ sys_mem.clone(),
+ device,
+ parent_bus,
+ multi_func,
+ )
} else {
let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone())));
MigrationManager::register_device_instance_mutex(
VirtioNetState::descriptor(),
device.clone(),
);
- VirtioPciDevice::new(device_cfg.id, devfn, sys_mem.clone(), device, parent_bus)
+ VirtioPciDevice::new(
+ device_cfg.id,
+ devfn,
+ sys_mem.clone(),
+ device,
+ parent_bus,
+ multi_func,
+ )
};
virtio_pci_device
.realize()
@@ -484,9 +525,17 @@ pub trait MachineOps {
let path = "/sys/bus/pci/devices/".to_string() + &device_cfg.host;
let name = device_cfg.id;
let bdf = get_pci_bdf(cfg_args)?;
+ let multi_func = get_multi_function(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
- let vfio_pci_dev = VfioPciDevice::new(Path::new(&path), container, devfn, name, parent_bus)
- .chain_err(|| "Failed to create vfio pci device")?;
+ let vfio_pci_dev = VfioPciDevice::new(
+ Path::new(&path),
+ container,
+ devfn,
+ name,
+ parent_bus,
+ multi_func,
+ )
+ .chain_err(|| "Failed to create vfio pci device")?;
VfioPciDevice::realize(vfio_pci_dev).chain_err(|| "Failed to realize vfio pci device")?;
Ok(())
@@ -513,7 +562,13 @@ pub trait MachineOps {
if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() {
bail!("ID {} already exists.");
}
- let rootport = RootPort::new(device_cfg.id, devfn, device_cfg.port, parent_bus);
+ let rootport = RootPort::new(
+ device_cfg.id,
+ devfn,
+ device_cfg.port,
+ parent_bus,
+ device_cfg.multifunction,
+ );
rootport
.realize()
.chain_err(|| "Failed to add pci root port")?;
diff --git a/machine/src/standard_vm/x86_64/ich9_lpc.rs b/machine/src/standard_vm/x86_64/ich9_lpc.rs
index b5d7524..0b1b3d5 100644
--- a/machine/src/standard_vm/x86_64/ich9_lpc.rs
+++ b/machine/src/standard_vm/x86_64/ich9_lpc.rs
@@ -15,7 +15,10 @@ use std::sync::{Arc, Mutex, Weak};
use acpi::AcpiPMTimer;
use address_space::{AddressSpace, GuestAddress, Region, RegionOps};
use error_chain::ChainedError;
-use pci::config::{PciConfig, DEVICE_ID, PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID};
+use pci::config::{
+ PciConfig, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC,
+ PCI_CONFIG_SPACE_SIZE, SUB_CLASS_CODE, VENDOR_ID,
+};
use pci::errors::Result as PciResult;
use pci::{le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps};
use util::byte_code::ByteCode;
@@ -96,6 +99,11 @@ impl PciDevOps for LPCBridge {
CLASS_CODE_ISA_BRIDGE,
)?;
le_write_u32(&mut self.config.write_mask, PM_BASE_OFFSET as usize, 0xff80)?;
+ le_write_u16(
+ &mut self.config.config,
+ HEADER_TYPE as usize,
+ (HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC) as u16,
+ )?;
let parent_bus = self.parent_bus.clone();
parent_bus
diff --git a/pci/src/host.rs b/pci/src/host.rs
index 19bbee7..153ba14 100644
--- a/pci/src/host.rs
+++ b/pci/src/host.rs
@@ -440,7 +440,7 @@ pub mod tests {
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
let pio_addr_ops = PciHost::build_pio_addr_ops(pci_host.clone());
let pio_data_ops = PciHost::build_pio_data_ops(pci_host.clone());
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
root_port.realize().unwrap();
let mut data = [0_u8; 4];
@@ -517,10 +517,10 @@ pub mod tests {
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
let mmconfig_region_ops = PciHost::build_mmconfig_ops(pci_host.clone());
- let mut root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone());
+ let mut root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus.clone(), false);
root_port.write_config(SECONDARY_BUS_NUM as usize, &[1]);
root_port.realize().unwrap();
- let mut root_port = RootPort::new("pcie.2".to_string(), 16, 0, root_bus);
+ let mut root_port = RootPort::new("pcie.2".to_string(), 16, 0, root_bus, false);
root_port.write_config(SECONDARY_BUS_NUM as usize, &[2]);
root_port.realize().unwrap();
diff --git a/pci/src/root_port.rs b/pci/src/root_port.rs
index 948e31d..321c55e 100644
--- a/pci/src/root_port.rs
+++ b/pci/src/root_port.rs
@@ -20,12 +20,13 @@ use util::byte_code::ByteCode;
use super::config::{
PciConfig, PcieDevType, BAR_0, CLASS_CODE_PCI_BRIDGE, COMMAND, COMMAND_IO_SPACE,
- COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, HEADER_TYPE_MULTIFUNC,
- IO_BASE, MEMORY_BASE, PCIE_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE,
- PREF_MEMORY_LIMIT, PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID,
+ COMMAND_MEMORY_SPACE, DEVICE_ID, HEADER_TYPE, HEADER_TYPE_BRIDGE, IO_BASE, MEMORY_BASE,
+ PCIE_CONFIG_SPACE_SIZE, PCI_VENDOR_ID_REDHAT, PREF_MEMORY_BASE, PREF_MEMORY_LIMIT,
+ PREF_MEM_RANGE_64BIT, REG_SIZE, SUB_CLASS_CODE, VENDOR_ID,
};
use crate::bus::PciBus;
use crate::errors::{Result, ResultExt};
+use crate::init_multifunction;
use crate::msix::init_msix;
use crate::{le_read_u16, le_write_u16, ranges_overlap, PciDevOps};
@@ -56,6 +57,7 @@ pub struct RootPort {
io_region: Region,
mem_region: Region,
dev_id: u16,
+ multifunction: bool,
}
impl RootPort {
@@ -68,7 +70,13 @@ impl RootPort {
/// * `port_num` - Root port number.
/// * `parent_bus` - Weak reference to the parent bus.
#[allow(dead_code)]
- pub fn new(name: String, devfn: u8, port_num: u8, parent_bus: Weak<Mutex<PciBus>>) -> Self {
+ pub fn new(
+ name: String,
+ devfn: u8,
+ port_num: u8,
+ parent_bus: Weak<Mutex<PciBus>>,
+ multifunction: bool,
+ ) -> Self {
#[cfg(target_arch = "x86_64")]
let io_region = Region::init_container_region(1 << 16);
let mem_region = Region::init_container_region(u64::max_value());
@@ -90,6 +98,7 @@ impl RootPort {
io_region,
mem_region,
dev_id: 0,
+ multifunction,
}
}
}
@@ -113,9 +122,15 @@ impl PciDevOps for RootPort {
le_write_u16(config_space, VENDOR_ID as usize, PCI_VENDOR_ID_REDHAT)?;
le_write_u16(config_space, DEVICE_ID as usize, DEVICE_ID_RP)?;
le_write_u16(config_space, SUB_CLASS_CODE as usize, CLASS_CODE_PCI_BRIDGE)?;
- config_space[HEADER_TYPE as usize] = HEADER_TYPE_BRIDGE | HEADER_TYPE_MULTIFUNC;
+ config_space[HEADER_TYPE as usize] = HEADER_TYPE_BRIDGE;
config_space[PREF_MEMORY_BASE as usize] = PREF_MEM_RANGE_64BIT;
config_space[PREF_MEMORY_LIMIT as usize] = PREF_MEM_RANGE_64BIT;
+ init_multifunction(
+ self.multifunction,
+ config_space,
+ self.devfn,
+ self.parent_bus.clone(),
+ )?;
self.config
.add_pcie_cap(self.devfn, self.port_num, PcieDevType::RootPort as u8)?;
@@ -294,7 +309,7 @@ mod tests {
fn test_read_config() {
let pci_host = create_pci_host();
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
root_port.realize().unwrap();
let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap();
@@ -310,7 +325,7 @@ mod tests {
fn test_write_config() {
let pci_host = create_pci_host();
let root_bus = Arc::downgrade(&pci_host.lock().unwrap().root_bus);
- let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus);
+ let root_port = RootPort::new("pcie.1".to_string(), 8, 0, root_bus, false);
root_port.realize().unwrap();
let root_port = pci_host.lock().unwrap().find_device(0, 8).unwrap();
diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs
index e366e47..40004a5 100644
--- a/vfio/src/vfio_pci.rs
+++ b/vfio/src/vfio_pci.rs
@@ -32,7 +32,7 @@ use pci::config::SECONDARY_BUS_NUM;
use pci::config::{
PciConfig, RegionType, BAR_0, BAR_5, BAR_IO_SPACE, BAR_MEM_64BIT, BAR_SPACE_UNMAPPED, COMMAND,
COMMAND_BUS_MASTER, COMMAND_INTERRUPT_DISABLE, COMMAND_IO_SPACE, COMMAND_MEMORY_SPACE,
- IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, REG_SIZE,
+ HEADER_TYPE, IO_BASE_ADDR_MASK, MEM_BASE_ADDR_MASK, PCIE_CONFIG_SPACE_SIZE, REG_SIZE,
};
use pci::errors::Result as PciResult;
use pci::msix::{
@@ -41,7 +41,8 @@ use pci::msix::{
MSIX_TABLE_OFFSET, MSIX_TABLE_SIZE_MAX,
};
use pci::{
- le_read_u16, le_read_u32, le_write_u16, le_write_u32, ranges_overlap, PciBus, PciDevOps,
+ init_multifunction, le_read_u16, le_read_u32, le_write_u16, le_write_u32, ranges_overlap,
+ PciBus, PciDevOps,
};
use util::unix::host_page_size;
@@ -96,6 +97,8 @@ pub struct VfioPciDevice {
dev_id: Arc<AtomicU16>,
name: String,
parent_bus: Weak<Mutex<PciBus>>,
+ /// Multi-Function flag.
+ multi_func: bool,
}
impl VfioPciDevice {
@@ -106,6 +109,7 @@ impl VfioPciDevice {
devfn: u8,
name: String,
parent_bus: Weak<Mutex<PciBus>>,
+ multi_func: bool,
) -> Result<Self> {
Ok(VfioPciDevice {
// Unknown PCI or PCIe type here, allocate enough space to match the two types.
@@ -122,6 +126,7 @@ impl VfioPciDevice {
dev_id: Arc::new(AtomicU16::new(0)),
name,
parent_bus,
+ multi_func,
})
}
@@ -710,6 +715,15 @@ impl PciDevOps for VfioPciDevice {
PciResultExt::chain_err(self.pci_config_reset(), || {
"Failed to reset vfio device pci config space"
})?;
+ PciResultExt::chain_err(
+ init_multifunction(
+ self.multi_func,
+ &mut self.pci_config.config,
+ self.devfn,
+ self.parent_bus.clone(),
+ ),
+ || "Failed to init vfio device multifunction.",
+ )?;
#[cfg(target_arch = "aarch64")]
{
@@ -762,7 +776,15 @@ impl PciDevOps for VfioPciDevice {
return;
}
- if ranges_overlap(offset, end, BAR_0 as usize, BAR_5 as usize + REG_SIZE) {
+ // BAR and header_type are always controlled by StratoVirt.
+ if ranges_overlap(offset, end, BAR_0 as usize, (BAR_5 as usize) + REG_SIZE)
+ || ranges_overlap(
+ offset,
+ end,
+ HEADER_TYPE as usize,
+ (HEADER_TYPE as usize) + 2,
+ )
+ {
self.pci_config.read(offset, data);
return;
}
@@ -778,9 +800,6 @@ impl PciDevOps for VfioPciDevice {
if i + offset == 0x3d {
// Clear INIx
*data &= 0;
- } else if i + offset == 0x0e {
- // Clear multi-function
- *data &= 0x7f;
}
}
}
diff --git a/virtio/src/virtio_pci.rs b/virtio/src/virtio_pci.rs
index 8bdfce6..5a5b991 100644
--- a/virtio/src/virtio_pci.rs
+++ b/virtio/src/virtio_pci.rs
@@ -25,7 +25,10 @@ use pci::config::{
};
use pci::errors::{ErrorKind, Result as PciResult, ResultExt};
use pci::msix::update_dev_id;
-use pci::{config::PciConfig, init_msix, le_write_u16, ranges_overlap, PciBus, PciDevOps};
+use pci::{
+ config::PciConfig, init_msix, init_multifunction, le_write_u16, ranges_overlap, PciBus,
+ PciDevOps,
+};
use util::byte_code::ByteCode;
use vmm_sys_util::eventfd::EventFd;
@@ -487,6 +490,8 @@ pub struct VirtioPciDevice {
interrupt_cb: Option<Arc<VirtioInterrupt>>,
/// Virtio queues. The vector and Queue will be shared acrossing thread, so all with Arc<Mutex<..>> wrapper.
queues: Arc<Mutex<Vec<Arc<Mutex<Queue>>>>>,
+ /// Multi-Function flag.
+ multi_func: bool,
}
impl VirtioPciDevice {
@@ -496,6 +501,7 @@ impl VirtioPciDevice {
sys_mem: Arc<AddressSpace>,
device: Arc<Mutex<dyn VirtioDevice>>,
parent_bus: Weak<Mutex<PciBus>>,
+ multi_func: bool,
) -> Self {
let queue_num = device.lock().unwrap().queue_num();
let queue_size = device.lock().unwrap().queue_size();
@@ -515,6 +521,7 @@ impl VirtioPciDevice {
notify_eventfds: NotifyEventFds::new(queue_num),
interrupt_cb: None,
queues: Arc::new(Mutex::new(Vec::with_capacity(queue_num))),
+ multi_func,
}
}
@@ -869,6 +876,12 @@ impl PciDevOps for VirtioPciDevice {
SUBSYSTEM_ID,
0x40 + device_type as u16,
)?;
+ init_multifunction(
+ self.multi_func,
+ &mut self.config.config,
+ self.devfn,
+ self.parent_bus.clone(),
+ )?;
let common_cap = VirtioPciCap::new(
size_of::<VirtioPciCap>() as u8 + PCI_CAP_VNDR_AND_NEXT_SIZE,
@@ -1146,6 +1159,10 @@ mod tests {
use std::sync::{Arc, Mutex};
use address_space::{AddressSpace, GuestAddress, HostMemMapping};
+ use pci::{
+ config::{HEADER_TYPE, HEADER_TYPE_MULTIFUNC},
+ le_read_u16,
+ };
use util::num_ops::{read_u32, write_u32};
use vmm_sys_util::eventfd::EventFd;
@@ -1347,6 +1364,7 @@ mod tests {
sys_mem,
virtio_dev,
Arc::downgrade(&parent_bus),
+ false,
);
virtio_pci.init_write_mask().unwrap();
virtio_pci.init_write_clear_mask().unwrap();
@@ -1381,6 +1399,7 @@ mod tests {
sys_mem,
virtio_dev,
Arc::downgrade(&parent_bus),
+ false,
);
assert!(virtio_pci.realize().is_ok());
}
@@ -1414,6 +1433,7 @@ mod tests {
sys_mem,
virtio_dev,
Arc::downgrade(&parent_bus),
+ false,
);
// Prepare msix and interrupt callback
@@ -1467,4 +1487,35 @@ mod tests {
(common_cfg_ops.write)(0_u32.as_bytes(), GuestAddress(0), COMMON_STATUS_REG);
assert_eq!(virtio_pci.device_activated.load(Ordering::Relaxed), false);
}
+
+ #[test]
+ fn test_multifunction() {
+ let virtio_dev: Arc<Mutex<dyn VirtioDevice>> =
+ Arc::new(Mutex::new(VirtioDeviceTest::new()));
+ let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())).unwrap();
+ let parent_bus = Arc::new(Mutex::new(PciBus::new(
+ String::from("test bus"),
+ #[cfg(target_arch = "x86_64")]
+ Region::init_container_region(1 << 16),
+ sys_mem.root().clone(),
+ )));
+ let mut virtio_pci = VirtioPciDevice::new(
+ String::from("test device"),
+ 24,
+ sys_mem,
+ virtio_dev,
+ Arc::downgrade(&parent_bus),
+ true,
+ );
+
+ assert!(init_multifunction(
+ virtio_pci.multi_func,
+ &mut virtio_pci.config.config,
+ virtio_pci.devfn,
+ virtio_pci.parent_bus.clone()
+ )
+ .is_ok());
+ let header_type = le_read_u16(&virtio_pci.config.config, HEADER_TYPE as usize).unwrap();
+ assert_eq!(header_type, HEADER_TYPE_MULTIFUNC as u16);
+ }
}
--
2.25.1