cilium/cilium · error

device is not a vf

Error message

device is not a vf

What it means

errNotAVF ("device is not a vf") is returned by the SR-IOV PciDevice Setup and Free methods (pkg/networkdriver/sriov/sriov.go:120-152) when d.PFName is empty, i.e. the device being set up or freed has no Physical Function parent recorded. parseDevice also yields this condition when a discovered PCI device cannot be attributed to a PF, meaning it is not usable as an SR-IOV virtual function.

Source

Thrown at pkg/networkdriver/sriov/sriov.go:35

	"strings"

	"github.com/vishvananda/netlink"
	resourceapi "k8s.io/api/resource/v1"
	"k8s.io/utils/ptr"

	"github.com/cilium/cilium/pkg/datapath/linux/safenetlink"
	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/networkdriver/types"
)

const (
	defaultSysfsPath = "/host/sys"
	pciDevicesPath   = "bus/pci/devices"
)

var (
	errNotAVF            = errors.New("device is not a vf")
	errTooManyVFs        = errors.New("too many vfs")
	errInterfaceNotFound = errors.New("interface not found")
	errVFIDNotFound      = errors.New("could not find VF ID")
)

type PCIAddr string
type KernelIfName string

// netlinkOps abstracts the netlink calls used by SRIOVManager and PciDevice.
// The real implementation delegates to safenetlink/netlink; tests inject a fake.
type netlinkOps interface {
	// LinkList returns all netlink links on the system.
	LinkList() ([]netlink.Link, error)
	// LinkByName returns the link with the given interface name.
	LinkByName(name string) (netlink.Link, error)
	// LinkSetVfVlan sets the VLAN for a VF on a PF link.
	LinkSetVfVlan(link netlink.Link, vf, vlan int) error
}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Check the pool config filters so only actual VFs match — add explicit pciAddrs or ifNames rather than broad driver/vendor filters.
  2. Verify SR-IOV is enabled and VFs are created on the PF: cat /sys/bus/pci/devices/<PF>/sriov_numvfs.
  3. Confirm /host/sys is mounted into the container (defaultSysfsPath is /host/sys) so PF-VF relationships are visible.
  4. If the device is a PF intentionally, remove it from the pool and select its VFs instead.

Example fix

// before: broad filter matches the PF itself
// filter: {drivers: ["mlx5_core"]}
// after: restrict to the VF pci addresses
// filter: {pciAddrs: ["0000:3b:02.5", "0000:3b:02.6"]}
Defensive patterns

Strategy: validation

Validate before calling

func isSRIOVVF(pciAddr string) (bool, error) {
	path := filepath.Join("/host/sys/bus/pci/devices", pciAddr)
	physFn, err := os.Readlink(filepath.Join(path, "physfn"))
	if os.IsNotExist(err) {
		return false, nil // no PF parent: not a VF
	}
	return err == nil, err
}

Type guard

func IsNotAVF(err error) bool {
	return errors.Is(err, sriov.ErrNotAVF)
}

Try / catch

if err := dev.Setup(cfg); err != nil {
	if errors.Is(err, errNotAVF) {
		// skip non-VF device and mark it ineligible instead of failing the allocation
		return skipDevice(dev, err)
	}
	return err
}

Prevention

When it happens

Trigger: Setup(config) or Free(config) is called on a PciDevice whose PFName field is empty — the wrapped error includes the device's pci-style ifname and kernel ifname. This happens when parseDevice discovered a PCI network device that is not an SR-IOV VF (no PF link in sysfs).

Common situations: Pool filters (driver/vendor/device-id) accidentally match physical function or non-SR-IOV NICs, so a non-VF device enters the pool; sysfs PF symlinks (virtfnN) missing because SR-IOV is disabled on the PF; bonding/team devices or unusual NIC firmware that breaks PF-VF attribution.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/2fb1c0cfb0f4b77e. Report an issue: GitHub.