agent: Add VFIO-AP device handling

Initial VFIO-AP support (#578) was simple, but somewhat hacky; a
different code path would be chosen for performing the hotplug, and
agent-side device handling was bound to knowing the assigned queue
numbers (APQNs) through some other means; plus the code for awaiting
them was written for the Go agent and never released. This code also
artificially increased the hotplug timeout to wait for the (relatively
expensive, thus limited to 5 seconds at the quickest) AP rescan, which
is impractical for e.g. common k8s timeouts.

Since then, the general handling logic was improved (#1190), but it
assumed PCI in several places.

In the runtime, introduce and parse AP devices. Annotate them as such
when passing to the agent, and include information about the associated
APQNs.

The agent awaits the passed APQNs through uevents and triggers a
rescan directly.

Fixes: #3678
Signed-off-by: Jakob Naucke <jakob.naucke@ibm.com>
This commit is contained in:
Jakob Naucke 2022-03-08 18:51:08 +01:00 committed by Hyounggyu Choi
parent b546eca26f
commit f666f8e2df
11 changed files with 335 additions and 74 deletions

79
src/agent/src/ap.rs Normal file
View File

@ -0,0 +1,79 @@
// Copyright (c) IBM Corp. 2022
//
// SPDX-License-Identifier: Apache-2.0
//
use std::fmt;
use std::str::FromStr;
use anyhow::{anyhow, Context};
// IBM Adjunct Processor (AP) is the bus used by IBM Crypto Express hardware security modules on
// IBM Z & LinuxONE (s390x)
// AP bus ID follow the format <xx>.<xxxx> [1, p. 476], where
// - <xx> is the adapter ID, i.e. the card and
// - <xxxx> is the adapter domain.
// [1] https://www.ibm.com/docs/en/linuxonibm/pdf/lku5dd05.pdf
#[derive(Debug)]
pub struct Address {
pub adapter_id: u8,
pub adapter_domain: u16,
}
impl Address {
pub fn new(adapter_id: u8, adapter_domain: u16) -> Address {
Address {
adapter_id,
adapter_domain,
}
}
}
impl FromStr for Address {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
let split: Vec<&str> = s.split('.').collect();
if split.len() != 2 {
return Err(anyhow!(
"Wrong AP bus format. It needs to be in the form <xx>.<xxxx>, got {:?}",
s
));
}
let adapter_id = u8::from_str_radix(split[0], 16).context(format!(
"Wrong AP bus format. AP ID needs to be in the form <xx>, got {:?}",
split[0]
))?;
let adapter_domain = u16::from_str_radix(split[1], 16).context(format!(
"Wrong AP bus format. AP domain needs to be in the form <xxxx>, got {:?}",
split[1]
))?;
Ok(Address::new(adapter_id, adapter_domain))
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:02x}.{:04x}", self.adapter_id, self.adapter_domain)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_str() {
let device = Address::from_str("a.1").unwrap();
assert_eq!(format!("{}", device), "0a.0001");
assert!(Address::from_str("").is_err());
assert!(Address::from_str(".").is_err());
assert!(Address::from_str("0.0.0").is_err());
assert!(Address::from_str("0g.0000").is_err());
assert!(Address::from_str("0a.10000").is_err());
}
}

View File

@ -50,11 +50,13 @@ pub const DRIVER_VFIO_PCI_GK_TYPE: &str = "vfio-pci-gk";
// VFIO PCI device to be bound to vfio-pci and made available inside the // VFIO PCI device to be bound to vfio-pci and made available inside the
// container as a VFIO device node // container as a VFIO device node
pub const DRIVER_VFIO_PCI_TYPE: &str = "vfio-pci"; pub const DRIVER_VFIO_PCI_TYPE: &str = "vfio-pci";
pub const DRIVER_VFIO_AP_TYPE: &str = "vfio-ap";
pub const DRIVER_OVERLAYFS_TYPE: &str = "overlayfs"; pub const DRIVER_OVERLAYFS_TYPE: &str = "overlayfs";
pub const FS_TYPE_HUGETLB: &str = "hugetlbfs"; pub const FS_TYPE_HUGETLB: &str = "hugetlbfs";
cfg_if! { cfg_if! {
if #[cfg(target_arch = "s390x")] { if #[cfg(target_arch = "s390x")] {
use crate::ap;
use crate::ccw; use crate::ccw;
} }
} }
@ -406,6 +408,39 @@ async fn get_vfio_device_name(sandbox: &Arc<Mutex<Sandbox>>, grp: IommuGroup) ->
Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname)) Ok(format!("{}/{}", SYSTEM_DEV_PATH, &uev.devname))
} }
#[cfg(target_arch = "s390x")]
#[derive(Debug)]
struct ApMatcher {
syspath: String,
}
#[cfg(target_arch = "s390x")]
impl ApMatcher {
fn new(address: ap::Address) -> ApMatcher {
ApMatcher {
syspath: format!(
"{}/card{:02x}/{}",
AP_ROOT_BUS_PATH, address.adapter_id, address
),
}
}
}
#[cfg(target_arch = "s390x")]
impl UeventMatcher for ApMatcher {
fn is_match(&self, uev: &Uevent) -> bool {
uev.action == "add" && uev.devpath == self.syspath
}
}
#[cfg(target_arch = "s390x")]
#[instrument]
async fn wait_for_ap_device(sandbox: &Arc<Mutex<Sandbox>>, address: ap::Address) -> Result<()> {
let matcher = ApMatcher::new(address);
wait_for_uevent(sandbox, matcher).await?;
Ok(())
}
/// Scan SCSI bus for the given SCSI address(SCSI-Id and LUN) /// Scan SCSI bus for the given SCSI address(SCSI-Id and LUN)
#[instrument] #[instrument]
fn scan_scsi_bus(scsi_addr: &str) -> Result<()> { fn scan_scsi_bus(scsi_addr: &str) -> Result<()> {
@ -772,6 +807,28 @@ async fn vfio_pci_device_handler(
}) })
} }
// The VFIO AP (Adjunct Processor) device handler takes all the APQNs provided as device options
// and awaits them. It sets the minimum AP rescan time of 5 seconds and temporarily adds that
// amoutn to the hotplug timeout.
#[cfg(target_arch = "s390x")]
#[instrument]
async fn vfio_ap_device_handler(
device: &Device,
sandbox: &Arc<Mutex<Sandbox>>,
) -> Result<SpecUpdate> {
// Force AP bus rescan
fs::write(AP_SCANS_PATH, "1")?;
for apqn in device.options.iter() {
wait_for_ap_device(sandbox, ap::Address::from_str(apqn)?).await?;
}
Ok(Default::default())
}
#[cfg(not(target_arch = "s390x"))]
async fn vfio_ap_device_handler(_: &Device, _: &Arc<Mutex<Sandbox>>) -> Result<SpecUpdate> {
Err(anyhow!("AP is only supported on s390x"))
}
#[instrument] #[instrument]
pub async fn add_devices( pub async fn add_devices(
devices: &[Device], devices: &[Device],
@ -840,6 +897,7 @@ async fn add_device(device: &Device, sandbox: &Arc<Mutex<Sandbox>>) -> Result<Sp
DRIVER_VFIO_PCI_GK_TYPE | DRIVER_VFIO_PCI_TYPE => { DRIVER_VFIO_PCI_GK_TYPE | DRIVER_VFIO_PCI_TYPE => {
vfio_pci_device_handler(device, sandbox).await vfio_pci_device_handler(device, sandbox).await
} }
DRIVER_VFIO_AP_TYPE => vfio_ap_device_handler(device, sandbox).await,
_ => Err(anyhow!("Unknown device type {}", device.field_type)), _ => Err(anyhow!("Unknown device type {}", device.field_type)),
} }
} }
@ -1583,4 +1641,35 @@ mod tests {
// Test dev2 // Test dev2
assert!(pci_iommu_group(&syspci, dev2).is_err()); assert!(pci_iommu_group(&syspci, dev2).is_err());
} }
#[cfg(target_arch = "s390x")]
#[tokio::test]
async fn test_vfio_ap_matcher() {
let subsystem = "ap";
let card = "0a";
let relpath = format!("{}.0001", card);
let mut uev = Uevent::default();
uev.action = U_EVENT_ACTION_ADD.to_string();
uev.subsystem = subsystem.to_string();
uev.devpath = format!("{}/card{}/{}", AP_ROOT_BUS_PATH, card, relpath);
let ap_address = ap::Address::from_str(&relpath).unwrap();
let matcher = ApMatcher::new(ap_address);
assert!(matcher.is_match(&uev));
let mut uev_remove = uev.clone();
uev_remove.action = U_EVENT_ACTION_REMOVE.to_string();
assert!(!matcher.is_match(&uev_remove));
let mut uev_other_device = uev.clone();
uev_other_device.devpath = format!(
"{}/card{}/{}",
AP_ROOT_BUS_PATH,
card,
format!("{}.0002", card)
);
assert!(!matcher.is_match(&uev_other_device));
}
} }

View File

@ -69,6 +69,8 @@ pub fn create_pci_root_bus_path() -> String {
cfg_if! { cfg_if! {
if #[cfg(target_arch = "s390x")] { if #[cfg(target_arch = "s390x")] {
pub const CCW_ROOT_BUS_PATH: &str = "/devices/css0"; pub const CCW_ROOT_BUS_PATH: &str = "/devices/css0";
pub const AP_ROOT_BUS_PATH: &str = "/devices/ap";
pub const AP_SCANS_PATH: &str = "/sys/bus/ap/scans";
} }
} }

View File

@ -75,6 +75,7 @@ mod tracer;
cfg_if! { cfg_if! {
if #[cfg(target_arch = "s390x")] { if #[cfg(target_arch = "s390x")] {
mod ap;
mod ccw; mod ccw;
} }
} }

View File

@ -263,6 +263,9 @@ const (
// VFIOPCIDeviceMediatedType is a VFIO PCI mediated device type // VFIOPCIDeviceMediatedType is a VFIO PCI mediated device type
VFIOPCIDeviceMediatedType VFIOPCIDeviceMediatedType
// VFIOAPDeviceMediatedType is a VFIO AP mediated device type
VFIOAPDeviceMediatedType
) )
type VFIODev interface { type VFIODev interface {
@ -316,6 +319,32 @@ func (d VFIOPCIDev) GetSysfsDev() *string {
return &d.SysfsDev return &d.SysfsDev
} }
type VFIOAPDev struct {
// ID is used to identify this drive in the hypervisor options.
ID string
// sysfsdev of VFIO mediated device
SysfsDev string
// APDevices are the Adjunct Processor devices assigned to the mdev
APDevices []string
// Type of VFIO device
Type VFIODeviceType
}
func (d VFIOAPDev) GetID() *string {
return &d.ID
}
func (d VFIOAPDev) GetType() VFIODeviceType {
return d.Type
}
func (d VFIOAPDev) GetSysfsDev() *string {
return &d.SysfsDev
}
// RNGDev represents a random number generator device // RNGDev represents a random number generator device
type RNGDev struct { type RNGDev struct {
// ID is used to identify the device in the hypervisor options. // ID is used to identify the device in the hypervisor options.

View File

@ -89,18 +89,47 @@ func readPCIProperty(propertyPath string) (string, error) {
return strings.Split(string(buf), "\n")[0], nil return strings.Split(string(buf), "\n")[0], nil
} }
func GetVFIODeviceType(deviceFileName string) config.VFIODeviceType { func GetVFIODeviceType(deviceFilePath string) (config.VFIODeviceType, error) {
deviceFileName := filepath.Base(deviceFilePath)
//For example, 0000:04:00.0 //For example, 0000:04:00.0
tokens := strings.Split(deviceFileName, ":") tokens := strings.Split(deviceFileName, ":")
vfioDeviceType := config.VFIODeviceErrorType
if len(tokens) == 3 { if len(tokens) == 3 {
vfioDeviceType = config.VFIOPCIDeviceNormalType return config.VFIOPCIDeviceNormalType, nil
} else {
//For example, 83b8f4f2-509f-382f-3c1e-e6bfe0fa1001
tokens = strings.Split(deviceFileName, "-")
if len(tokens) == 5 {
vfioDeviceType = config.VFIOPCIDeviceMediatedType
}
} }
return vfioDeviceType
//For example, 83b8f4f2-509f-382f-3c1e-e6bfe0fa1001
tokens = strings.Split(deviceFileName, "-")
if len(tokens) != 5 {
return config.VFIODeviceErrorType, fmt.Errorf("Incorrect tokens found while parsing VFIO details: %s", deviceFileName)
}
deviceSysfsDev, err := GetSysfsDev(deviceFilePath)
if err != nil {
return config.VFIODeviceErrorType, err
}
if strings.HasPrefix(deviceSysfsDev, vfioAPSysfsDir) {
return config.VFIOAPDeviceMediatedType, nil
}
return config.VFIOPCIDeviceMediatedType, nil
}
// GetSysfsDev returns the sysfsdev of mediated device
// Expected input string format is absolute path to the sysfs dev node
// eg. /sys/kernel/iommu_groups/0/devices/f79944e4-5a3d-11e8-99ce-479cbab002e4
func GetSysfsDev(sysfsDevStr string) (string, error) {
return filepath.EvalSymlinks(sysfsDevStr)
}
// GetAPVFIODevices retrieves all APQNs associated with a mediated VFIO-AP
// device
func GetAPVFIODevices(sysfsdev string) ([]string, error) {
data, err := os.ReadFile(filepath.Join(sysfsdev, "matrix"))
if err != nil {
return []string{}, err
}
// Split by newlines, omitting final newline
return strings.Split(string(data[:len(data)-1]), "\n"), nil
} }

View File

@ -30,6 +30,7 @@ const (
iommuGroupPath = "/sys/bus/pci/devices/%s/iommu_group" iommuGroupPath = "/sys/bus/pci/devices/%s/iommu_group"
vfioDevPath = "/dev/vfio/%s" vfioDevPath = "/dev/vfio/%s"
pcieRootPortPrefix = "rp" pcieRootPortPrefix = "rp"
vfioAPSysfsDir = "/sys/devices/vfio_ap"
) )
var ( var (
@ -106,6 +107,17 @@ func (device *VFIODevice) Attach(ctx context.Context, devReceiver api.DeviceRece
AllPCIeDevs[deviceBDF] = true AllPCIeDevs[deviceBDF] = true
} }
vfio = vfioPCI vfio = vfioPCI
case config.VFIOAPDeviceMediatedType:
devices, err := GetAPVFIODevices(deviceSysfsDev)
if err != nil {
return err
}
vfio = config.VFIOAPDev{
ID: id,
SysfsDev: deviceSysfsDev,
Type: config.VFIOAPDeviceMediatedType,
APDevices: devices,
}
} }
device.VfioDevs = append(device.VfioDevs, &vfio) device.VfioDevs = append(device.VfioDevs, &vfio)
} }
@ -190,16 +202,7 @@ func (device *VFIODevice) Save() config.DeviceState {
devs := device.VfioDevs devs := device.VfioDevs
for _, dev := range devs { for _, dev := range devs {
if dev != nil { if dev != nil {
bdf := "" ds.VFIODevs = append(ds.VFIODevs, dev)
if pciDev, ok := (*dev).(config.VFIOPCIDev); ok {
bdf = pciDev.BDF
}
ds.VFIODevs = append(ds.VFIODevs, &persistapi.VFIODev{
ID: *(*dev).GetID(),
Type: uint32((*dev).GetType()),
BDF: bdf,
SysfsDev: *(*dev).GetSysfsDev(),
})
} }
} }
return ds return ds
@ -211,20 +214,38 @@ func (device *VFIODevice) Load(ds config.DeviceState) {
device.GenericDevice.Load(ds) device.GenericDevice.Load(ds)
for _, dev := range ds.VFIODevs { for _, dev := range ds.VFIODevs {
var vfioDev config.VFIODev = config.VFIOPCIDev{ var vfio config.VFIODev
ID: dev.ID,
Type: config.VFIODeviceType(dev.Type), if (*device.VfioDevs[0]).GetType() == config.VFIOAPDeviceMediatedType {
BDF: dev.BDF, vfio = config.VFIOAPDev{
SysfsDev: dev.SysfsDev, ID: *(*dev).GetID(),
SysfsDev: *(*dev).GetSysfsDev(),
}
} else {
bdf := ""
if pciDev, ok := (*dev).(config.VFIOPCIDev); ok {
bdf = pciDev.BDF
}
vfio = config.VFIOPCIDev{
ID: *(*dev).GetID(),
Type: config.VFIODeviceType((*dev).GetType()),
BDF: bdf,
SysfsDev: *(*dev).GetSysfsDev(),
}
} }
device.VfioDevs = append(device.VfioDevs, &vfioDev)
device.VfioDevs = append(device.VfioDevs, &vfio)
} }
} }
// It should implement GetAttachCount() and DeviceID() as api.Device implementation // It should implement GetAttachCount() and DeviceID() as api.Device implementation
// here it shares function from *GenericDevice so we don't need duplicate codes // here it shares function from *GenericDevice so we don't need duplicate codes
func getVFIODetails(deviceFileName, iommuDevicesPath string) (deviceBDF, deviceSysfsDev string, vfioDeviceType config.VFIODeviceType, err error) { func getVFIODetails(deviceFileName, iommuDevicesPath string) (deviceBDF, deviceSysfsDev string, vfioDeviceType config.VFIODeviceType, err error) {
vfioDeviceType = GetVFIODeviceType(deviceFileName) sysfsDevStr := filepath.Join(iommuDevicesPath, deviceFileName)
vfioDeviceType, err = GetVFIODeviceType(sysfsDevStr)
if err != nil {
return deviceBDF, deviceSysfsDev, vfioDeviceType, err
}
switch vfioDeviceType { switch vfioDeviceType {
case config.VFIOPCIDeviceNormalType: case config.VFIOPCIDeviceNormalType:
@ -235,8 +256,11 @@ func getVFIODetails(deviceFileName, iommuDevicesPath string) (deviceBDF, deviceS
case config.VFIOPCIDeviceMediatedType: case config.VFIOPCIDeviceMediatedType:
// Get sysfsdev of device eg. /sys/devices/pci0000:00/0000:00:02.0/f79944e4-5a3d-11e8-99ce-479cbab002e4 // Get sysfsdev of device eg. /sys/devices/pci0000:00/0000:00:02.0/f79944e4-5a3d-11e8-99ce-479cbab002e4
sysfsDevStr := filepath.Join(iommuDevicesPath, deviceFileName) sysfsDevStr := filepath.Join(iommuDevicesPath, deviceFileName)
deviceSysfsDev, err = getSysfsDev(sysfsDevStr) deviceSysfsDev, err = GetSysfsDev(sysfsDevStr)
deviceBDF = getBDF(getMediatedBDF(deviceSysfsDev)) deviceBDF = getBDF(getMediatedBDF(deviceSysfsDev))
case config.VFIOAPDeviceMediatedType:
sysfsDevStr := filepath.Join(iommuDevicesPath, deviceFileName)
deviceSysfsDev, err = GetSysfsDev(sysfsDevStr)
default: default:
err = fmt.Errorf("Incorrect tokens found while parsing vfio details: %s", deviceFileName) err = fmt.Errorf("Incorrect tokens found while parsing vfio details: %s", deviceFileName)
} }
@ -264,13 +288,6 @@ func getBDF(deviceSysStr string) string {
return tokens[1] return tokens[1]
} }
// getSysfsDev returns the sysfsdev of mediated device
// Expected input string format is absolute path to the sysfs dev node
// eg. /sys/kernel/iommu_groups/0/devices/f79944e4-5a3d-11e8-99ce-479cbab002e4
func getSysfsDev(sysfsDevStr string) (string, error) {
return filepath.EvalSymlinks(sysfsDevStr)
}
// BindDevicetoVFIO binds the device to vfio driver after unbinding from host. // BindDevicetoVFIO binds the device to vfio driver after unbinding from host.
// Will be called by a network interface or a generic pcie device. // Will be called by a network interface or a generic pcie device.
func BindDevicetoVFIO(bdf, hostDriver, vendorDeviceID string) (string, error) { func BindDevicetoVFIO(bdf, hostDriver, vendorDeviceID string) (string, error) {

View File

@ -34,7 +34,7 @@ func TestGetVFIODetails(t *testing.T) {
switch vfioDeviceType { switch vfioDeviceType {
case config.VFIOPCIDeviceNormalType: case config.VFIOPCIDeviceNormalType:
assert.Equal(t, d.expectedStr, deviceBDF) assert.Equal(t, d.expectedStr, deviceBDF)
case config.VFIOPCIDeviceMediatedType: case config.VFIOPCIDeviceMediatedType, config.VFIOAPDeviceMediatedType:
assert.Equal(t, d.expectedStr, deviceSysfsDev) assert.Equal(t, d.expectedStr, deviceSysfsDev)
default: default:
assert.NotNil(t, err) assert.NotNil(t, err)

View File

@ -867,7 +867,7 @@ func (clh *cloudHypervisor) hotPlugVFIODevice(device *config.VFIODev) error {
if err != nil { if err != nil {
return fmt.Errorf("Failed to hotplug device %+v %s", device, openAPIClientError(err)) return fmt.Errorf("Failed to hotplug device %+v %s", device, openAPIClientError(err))
} }
clh.devicesIds[device.ID] = pciInfo.GetId() clh.devicesIds[*(*device).GetID()] = pciInfo.GetId()
// clh doesn't use bridges, so the PCI path is simply the slot // clh doesn't use bridges, so the PCI path is simply the slot
// number of the device. This will break if clh starts using // number of the device. This will break if clh starts using
@ -884,7 +884,7 @@ func (clh *cloudHypervisor) hotPlugVFIODevice(device *config.VFIODev) error {
return fmt.Errorf("Unexpected PCI address %q from clh hotplug", pciInfo.Bdf) return fmt.Errorf("Unexpected PCI address %q from clh hotplug", pciInfo.Bdf)
} }
guestPciPath, err := vcTypes.PciPathFromString(tokens[0]) guestPciPath, err := types.PciPathFromString(tokens[0])
pciDevice.GuestPciPath = guestPciPath pciDevice.GuestPciPath = guestPciPath
*device = pciDevice *device = pciDevice

View File

@ -77,38 +77,39 @@ const (
) )
var ( var (
checkRequestTimeout = 30 * time.Second checkRequestTimeout = 30 * time.Second
defaultRequestTimeout = 60 * time.Second defaultRequestTimeout = 60 * time.Second
errorMissingOCISpec = errors.New("Missing OCI specification") errorMissingOCISpec = errors.New("Missing OCI specification")
defaultKataHostSharedDir = "/run/kata-containers/shared/sandboxes/" defaultKataHostSharedDir = "/run/kata-containers/shared/sandboxes/"
defaultKataGuestSharedDir = "/run/kata-containers/shared/containers/" defaultKataGuestSharedDir = "/run/kata-containers/shared/containers/"
defaultKataGuestNydusRootDir = "/run/kata-containers/shared/" defaultKataGuestNydusRootDir = "/run/kata-containers/shared/"
mountGuestTag = "kataShared" mountGuestTag = "kataShared"
defaultKataGuestSandboxDir = "/run/kata-containers/sandbox/" defaultKataGuestSandboxDir = "/run/kata-containers/sandbox/"
type9pFs = "9p" type9pFs = "9p"
typeVirtioFS = "virtiofs" typeVirtioFS = "virtiofs"
typeOverlayFS = "overlay" typeOverlayFS = "overlay"
kata9pDevType = "9p" kata9pDevType = "9p"
kataMmioBlkDevType = "mmioblk" kataMmioBlkDevType = "mmioblk"
kataBlkDevType = "blk" kataBlkDevType = "blk"
kataBlkCCWDevType = "blk-ccw" kataBlkCCWDevType = "blk-ccw"
kataSCSIDevType = "scsi" kataSCSIDevType = "scsi"
kataNvdimmDevType = "nvdimm" kataNvdimmDevType = "nvdimm"
kataVirtioFSDevType = "virtio-fs" kataVirtioFSDevType = "virtio-fs"
kataOverlayDevType = "overlayfs" kataOverlayDevType = "overlayfs"
kataWatchableBindDevType = "watchable-bind" kataWatchableBindDevType = "watchable-bind"
kataVfioDevType = "vfio" // VFIO device to used as VFIO in the container kataVfioPciDevType = "vfio-pci" // VFIO PCI device to used as VFIO in the container
kataVfioGuestKernelDevType = "vfio-gk" // VFIO device for consumption by the guest kernel kataVfioPciGuestKernelDevType = "vfio-pci-gk" // VFIO PCI device for consumption by the guest kernel
sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L,cache=mmap", "nodev"} kataVfioApDevType = "vfio-ap"
sharedDirVirtioFSOptions = []string{} sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L,cache=mmap", "nodev"}
sharedDirVirtioFSDaxOptions = "dax" sharedDirVirtioFSOptions = []string{}
shmDir = "shm" sharedDirVirtioFSDaxOptions = "dax"
kataEphemeralDevType = "ephemeral" shmDir = "shm"
defaultEphemeralPath = filepath.Join(defaultKataGuestSandboxDir, kataEphemeralDevType) kataEphemeralDevType = "ephemeral"
grpcMaxDataSize = int64(1024 * 1024) defaultEphemeralPath = filepath.Join(defaultKataGuestSandboxDir, kataEphemeralDevType)
localDirOptions = []string{"mode=0777"} grpcMaxDataSize = int64(1024 * 1024)
maxHostnameLen = 64 localDirOptions = []string{"mode=0777"}
GuestDNSFile = "/etc/resolv.conf" maxHostnameLen = 64
GuestDNSFile = "/etc/resolv.conf"
) )
const ( const (
@ -1126,6 +1127,11 @@ func (k *kataAgent) appendVfioDevice(dev ContainerDevice, device api.Device, c *
// DDDD:BB:DD.F is the device's PCI address on the // DDDD:BB:DD.F is the device's PCI address on the
// *host*. <pcipath> is the device's PCI path in the guest // *host*. <pcipath> is the device's PCI path in the guest
// (see qomGetPciPath() for details). // (see qomGetPciPath() for details).
//
// For VFIO-AP, one VFIO group could include several queue devices. They are
// identified by APQNs (Adjunct Processor Queue Numbers), which do not differ
// between host and guest. They are passed as options so they can be awaited
// by the agent.
kataDevice := &grpc.Device{ kataDevice := &grpc.Device{
ContainerPath: dev.ContainerPath, ContainerPath: dev.ContainerPath,
Type: kataVfioPciDevType, Type: kataVfioPciDevType,
@ -1141,10 +1147,15 @@ func (k *kataAgent) appendVfioDevice(dev ContainerDevice, device api.Device, c *
kataDevice.Type = kataVfioPciGuestKernelDevType kataDevice.Type = kataVfioPciGuestKernelDevType
} }
kataDevice.Options = make([]string, len(devList)) if (*devList[0]).GetType() == config.VFIOAPDeviceMediatedType {
for i, device := range devList { kataDevice.Type = kataVfioApDevType
pciDevice := (*device).(config.VFIOPCIDev) kataDevice.Options = (*devList[0]).(config.VFIOAPDev).APDevices
kataDevice.Options[i] = fmt.Sprintf("0000:%s=%s", pciDevice.BDF, pciDevice.GuestPciPath) } else {
kataDevice.Options = make([]string, len(devList))
for i, device := range devList {
pciDevice := (*device).(config.VFIOPCIDev)
kataDevice.Options[i] = fmt.Sprintf("0000:%s=%s", pciDevice.BDF, pciDevice.GuestPciPath)
}
} }
return kataDevice return kataDevice

View File

@ -1753,6 +1753,8 @@ func (q *qemu) hotplugVFIODevice(ctx context.Context, device *config.VFIODev, op
} else { } else {
err = q.qmpMonitorCh.qmp.ExecutePCIVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, devID, *(*device).GetSysfsDev(), "", pciDevice.Bus, romFile) err = q.qmpMonitorCh.qmp.ExecutePCIVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, devID, *(*device).GetSysfsDev(), "", pciDevice.Bus, romFile)
} }
case config.VFIOAPDeviceMediatedType:
err = q.qmpMonitorCh.qmp.ExecuteAPVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, *(*device).GetSysfsDev())
} }
} else { } else {
addr, bridge, err := q.arch.addDeviceToBridge(ctx, devID, types.PCI) addr, bridge, err := q.arch.addDeviceToBridge(ctx, devID, types.PCI)
@ -1775,6 +1777,8 @@ func (q *qemu) hotplugVFIODevice(ctx context.Context, device *config.VFIODev, op
err = q.qmpMonitorCh.qmp.ExecutePCIVFIODeviceAdd(q.qmpMonitorCh.ctx, devID, pciDevice.BDF, addr, bridge.ID, romFile) err = q.qmpMonitorCh.qmp.ExecutePCIVFIODeviceAdd(q.qmpMonitorCh.ctx, devID, pciDevice.BDF, addr, bridge.ID, romFile)
case config.VFIOPCIDeviceMediatedType: case config.VFIOPCIDeviceMediatedType:
err = q.qmpMonitorCh.qmp.ExecutePCIVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, devID, *(*device).GetSysfsDev(), addr, bridge.ID, romFile) err = q.qmpMonitorCh.qmp.ExecutePCIVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, devID, *(*device).GetSysfsDev(), addr, bridge.ID, romFile)
case config.VFIOAPDeviceMediatedType:
err = q.qmpMonitorCh.qmp.ExecuteAPVFIOMediatedDeviceAdd(q.qmpMonitorCh.ctx, *(*device).GetSysfsDev())
default: default:
return fmt.Errorf("Incorrect VFIO device type found") return fmt.Errorf("Incorrect VFIO device type found")
} }