From 4064b4d360991fd37365c489dab97dc805713bd6 Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Wed, 27 Dec 2023 17:58:10 +0200 Subject: [PATCH] Add support for VDPA devices management Current implementation support following functions: - VDPANewDev - VDPADelDev - VDPAGetDevList - VDPAGetDevByName - VDPAGetDevConfigList - VDPAGetDevConfigByName - VDPAGetDevVStats - VDPAGetMGMTDevList - VDPAGetMGMTDevByBusAndName Signed-off-by: Yury Kulazhenkov --- nl/vdpa_linux.go | 41 ++++ vdpa_linux.go | 463 +++++++++++++++++++++++++++++++++++++++++++++ vdpa_linux_test.go | 270 ++++++++++++++++++++++++++ 3 files changed, 774 insertions(+) create mode 100644 nl/vdpa_linux.go create mode 100644 vdpa_linux.go create mode 100644 vdpa_linux_test.go diff --git a/nl/vdpa_linux.go b/nl/vdpa_linux.go new file mode 100644 index 00000000..f209125d --- /dev/null +++ b/nl/vdpa_linux.go @@ -0,0 +1,41 @@ +package nl + +const ( + VDPA_GENL_NAME = "vdpa" + VDPA_GENL_VERSION = 0x1 +) + +const ( + VDPA_CMD_UNSPEC = iota + VDPA_CMD_MGMTDEV_NEW + VDPA_CMD_MGMTDEV_GET /* can dump */ + VDPA_CMD_DEV_NEW + VDPA_CMD_DEV_DEL + VDPA_CMD_DEV_GET /* can dump */ + VDPA_CMD_DEV_CONFIG_GET /* can dump */ + VDPA_CMD_DEV_VSTATS_GET +) + +const ( + VDPA_ATTR_UNSPEC = iota + VDPA_ATTR_MGMTDEV_BUS_NAME + VDPA_ATTR_MGMTDEV_DEV_NAME + VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES + VDPA_ATTR_DEV_NAME + VDPA_ATTR_DEV_ID + VDPA_ATTR_DEV_VENDOR_ID + VDPA_ATTR_DEV_MAX_VQS + VDPA_ATTR_DEV_MAX_VQ_SIZE + VDPA_ATTR_DEV_MIN_VQ_SIZE + VDPA_ATTR_DEV_NET_CFG_MACADDR + VDPA_ATTR_DEV_NET_STATUS + VDPA_ATTR_DEV_NET_CFG_MAX_VQP + VDPA_ATTR_DEV_NET_CFG_MTU + VDPA_ATTR_DEV_NEGOTIATED_FEATURES + VDPA_ATTR_DEV_MGMTDEV_MAX_VQS + VDPA_ATTR_DEV_SUPPORTED_FEATURES + VDPA_ATTR_DEV_QUEUE_INDEX + VDPA_ATTR_DEV_VENDOR_ATTR_NAME + VDPA_ATTR_DEV_VENDOR_ATTR_VALUE + VDPA_ATTR_DEV_FEATURES +) diff --git a/vdpa_linux.go b/vdpa_linux.go new file mode 100644 index 00000000..7c15986d --- /dev/null +++ b/vdpa_linux.go @@ -0,0 +1,463 @@ +package netlink + +import ( + "fmt" + "net" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/vishvananda/netlink/nl" +) + +type vdpaDevID struct { + Name string + ID uint32 +} + +// VDPADev contains info about VDPA device +type VDPADev struct { + vdpaDevID + VendorID uint32 + MaxVQS uint32 + MaxVQSize uint16 + MinVQSize uint16 +} + +// VDPADevConfig contains configuration of the VDPA device +type VDPADevConfig struct { + vdpaDevID + Features uint64 + NegotiatedFeatures uint64 + Net VDPADevConfigNet +} + +// VDPADevVStats conatins vStats for the VDPA device +type VDPADevVStats struct { + vdpaDevID + QueueIndex uint32 + Vendor []VDPADevVStatsVendor + NegotiatedFeatures uint64 +} + +// VDPADevVStatsVendor conatins name and value for vendor specific vstat option +type VDPADevVStatsVendor struct { + Name string + Value uint64 +} + +// VDPADevConfigNet conatins status and net config for the VDPA device +type VDPADevConfigNet struct { + Status VDPADevConfigNetStatus + Cfg VDPADevConfigNetCfg +} + +// VDPADevConfigNetStatus contains info about net status +type VDPADevConfigNetStatus struct { + LinkUp bool + Announce bool +} + +// VDPADevConfigNetCfg contains net config for the VDPA device +type VDPADevConfigNetCfg struct { + MACAddr net.HardwareAddr + MaxVQP uint16 + MTU uint16 +} + +// VDPAMGMTDev conatins info about VDPA management device +type VDPAMGMTDev struct { + BusName string + DevName string + SupportedClasses uint64 + SupportedFeatures uint64 + MaxVQS uint32 +} + +// VDPANewDevParams contains parameters for new VDPA device +// use SetBits to configure requried features for the device +// example: +// +// VDPANewDevParams{Features: SetBits(0, VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_MAC_ADDR)} +type VDPANewDevParams struct { + MACAddr net.HardwareAddr + MaxVQP uint16 + MTU uint16 + Features uint64 +} + +// SetBits set provided bits in the uint64 input value +// usage example: +// features := SetBits(0, VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_MAC_ADDR) +func SetBits(input uint64, pos ...int) uint64 { + for _, p := range pos { + input |= 1 << uint64(p) + } + return input +} + +// IsBitSet check if specific bit is set in the uint64 input value +// usage example: +// hasNetClass := IsBitSet(mgmtDev, VIRTIO_ID_NET) +func IsBitSet(input uint64, pos int) bool { + val := input & (1 << uint64(pos)) + return val > 0 +} + +// VDPANewDev adds new VDPA device +// Equivalent to: `vdpa dev add name mgmtdev /mgmtName [params]` +func VDPANewDev(name, mgmtBus, mgmtName string, params VDPANewDevParams) error { + return pkgHandle.VDPANewDev(name, mgmtBus, mgmtName, params) +} + +// VDPADelDev removes VDPA device +// Equivalent to: `vdpa dev del ` +func VDPADelDev(name string) error { + return pkgHandle.VDPADelDev(name) +} + +// VDPAGetDevList returns list of VDPA devices +// Equivalent to: `vdpa dev show` +func VDPAGetDevList() ([]*VDPADev, error) { + return pkgHandle.VDPAGetDevList() +} + +// VDPAGetDevByName returns VDPA device selected by name +// Equivalent to: `vdpa dev show ` +func VDPAGetDevByName(name string) (*VDPADev, error) { + return pkgHandle.VDPAGetDevByName(name) +} + +// VDPAGetDevConfigList returns list of VDPA devices configurations +// Equivalent to: `vdpa dev config show` +func VDPAGetDevConfigList() ([]*VDPADevConfig, error) { + return pkgHandle.VDPAGetDevConfigList() +} + +// VDPAGetDevConfigByName returns VDPA device configuration selected by name +// Equivalent to: `vdpa dev config show ` +func VDPAGetDevConfigByName(name string) (*VDPADevConfig, error) { + return pkgHandle.VDPAGetDevConfigByName(name) +} + +// VDPAGetDevVStats returns vstats for VDPA device +// Equivalent to: `vdpa dev vstats show qidx ` +func VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStats, error) { + return pkgHandle.VDPAGetDevVStats(name, queueIndex) +} + +// VDPAGetMGMTDevList returns list of mgmt devices +// Equivalent to: `vdpa mgmtdev show` +func VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) { + return pkgHandle.VDPAGetMGMTDevList() +} + +// VDPAGetMGMTDevByBusAndName returns mgmt devices selected by bus and name +// Equivalent to: `vdpa mgmtdev show /` +func VDPAGetMGMTDevByBusAndName(bus, name string) (*VDPAMGMTDev, error) { + return pkgHandle.VDPAGetMGMTDevByBusAndName(bus, name) +} + +type vdpaNetlinkMessage []syscall.NetlinkRouteAttr + +func (id *vdpaDevID) parseIDAttribute(attr syscall.NetlinkRouteAttr) { + switch attr.Attr.Type { + case nl.VDPA_ATTR_DEV_NAME: + id.Name = nl.BytesToString(attr.Value) + case nl.VDPA_ATTR_DEV_ID: + id.ID = native.Uint32(attr.Value) + } +} + +func (netStatus *VDPADevConfigNetStatus) parseStatusAttribute(value []byte) { + a := native.Uint16(value) + netStatus.Announce = (a & VIRTIO_NET_S_ANNOUNCE) > 0 + netStatus.LinkUp = (a & VIRTIO_NET_S_LINK_UP) > 0 +} + +func (d *VDPADev) parseAttributes(attrs vdpaNetlinkMessage) { + for _, a := range attrs { + d.parseIDAttribute(a) + switch a.Attr.Type { + case nl.VDPA_ATTR_DEV_VENDOR_ID: + d.VendorID = native.Uint32(a.Value) + case nl.VDPA_ATTR_DEV_MAX_VQS: + d.MaxVQS = native.Uint32(a.Value) + case nl.VDPA_ATTR_DEV_MAX_VQ_SIZE: + d.MaxVQSize = native.Uint16(a.Value) + case nl.VDPA_ATTR_DEV_MIN_VQ_SIZE: + d.MinVQSize = native.Uint16(a.Value) + } + } +} + +func (c *VDPADevConfig) parseAttributes(attrs vdpaNetlinkMessage) { + for _, a := range attrs { + c.parseIDAttribute(a) + switch a.Attr.Type { + case nl.VDPA_ATTR_DEV_NET_CFG_MACADDR: + c.Net.Cfg.MACAddr = a.Value + case nl.VDPA_ATTR_DEV_NET_STATUS: + c.Net.Status.parseStatusAttribute(a.Value) + case nl.VDPA_ATTR_DEV_NET_CFG_MAX_VQP: + c.Net.Cfg.MaxVQP = native.Uint16(a.Value) + case nl.VDPA_ATTR_DEV_NET_CFG_MTU: + c.Net.Cfg.MTU = native.Uint16(a.Value) + case nl.VDPA_ATTR_DEV_FEATURES: + c.Features = native.Uint64(a.Value) + case nl.VDPA_ATTR_DEV_NEGOTIATED_FEATURES: + c.NegotiatedFeatures = native.Uint64(a.Value) + } + } +} + +func (s *VDPADevVStats) parseAttributes(attrs vdpaNetlinkMessage) { + for _, a := range attrs { + s.parseIDAttribute(a) + switch a.Attr.Type { + case nl.VDPA_ATTR_DEV_QUEUE_INDEX: + s.QueueIndex = native.Uint32(a.Value) + case nl.VDPA_ATTR_DEV_VENDOR_ATTR_NAME: + s.Vendor = append(s.Vendor, VDPADevVStatsVendor{Name: nl.BytesToString(a.Value)}) + case nl.VDPA_ATTR_DEV_VENDOR_ATTR_VALUE: + if len(s.Vendor) == 0 { + break + } + s.Vendor[len(s.Vendor)-1].Value = native.Uint64(a.Value) + case nl.VDPA_ATTR_DEV_NEGOTIATED_FEATURES: + s.NegotiatedFeatures = native.Uint64(a.Value) + } + } +} + +func (d *VDPAMGMTDev) parseAttributes(attrs vdpaNetlinkMessage) { + for _, a := range attrs { + switch a.Attr.Type { + case nl.VDPA_ATTR_MGMTDEV_BUS_NAME: + d.BusName = nl.BytesToString(a.Value) + case nl.VDPA_ATTR_MGMTDEV_DEV_NAME: + d.DevName = nl.BytesToString(a.Value) + case nl.VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES: + d.SupportedClasses = native.Uint64(a.Value) + case nl.VDPA_ATTR_DEV_SUPPORTED_FEATURES: + d.SupportedFeatures = native.Uint64(a.Value) + case nl.VDPA_ATTR_DEV_MGMTDEV_MAX_VQS: + d.MaxVQS = native.Uint32(a.Value) + } + } +} + +func (h *Handle) vdpaRequest(command uint8, extraFlags int, attrs []*nl.RtAttr) ([]vdpaNetlinkMessage, error) { + f, err := h.GenlFamilyGet(nl.VDPA_GENL_NAME) + if err != nil { + return nil, err + } + req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_ACK|extraFlags) + req.AddData(&nl.Genlmsg{ + Command: command, + Version: nl.VDPA_GENL_VERSION, + }) + for _, a := range attrs { + req.AddData(a) + } + + resp, err := req.Execute(unix.NETLINK_GENERIC, 0) + if err != nil { + return nil, err + } + messages := make([]vdpaNetlinkMessage, 0, len(resp)) + for _, m := range resp { + attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) + if err != nil { + return nil, err + } + messages = append(messages, attrs) + } + return messages, nil +} + +// dump all devices if dev is nil +func (h *Handle) vdpaDevGet(dev *string) ([]*VDPADev, error) { + var extraFlags int + var attrs []*nl.RtAttr + if dev != nil { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(*dev))) + } else { + extraFlags = extraFlags | unix.NLM_F_DUMP + } + messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_GET, extraFlags, attrs) + if err != nil { + return nil, err + } + devs := make([]*VDPADev, 0, len(messages)) + for _, m := range messages { + d := &VDPADev{} + d.parseAttributes(m) + devs = append(devs, d) + } + return devs, nil +} + +// dump all devices if dev is nil +func (h *Handle) vdpaDevConfigGet(dev *string) ([]*VDPADevConfig, error) { + var extraFlags int + var attrs []*nl.RtAttr + if dev != nil { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(*dev))) + } else { + extraFlags = extraFlags | unix.NLM_F_DUMP + } + messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_CONFIG_GET, extraFlags, attrs) + if err != nil { + return nil, err + } + cfgs := make([]*VDPADevConfig, 0, len(messages)) + for _, m := range messages { + cfg := &VDPADevConfig{} + cfg.parseAttributes(m) + cfgs = append(cfgs, cfg) + } + return cfgs, nil +} + +// dump all devices if dev is nil +func (h *Handle) vdpaMGMTDevGet(bus, dev *string) ([]*VDPAMGMTDev, error) { + var extraFlags int + var attrs []*nl.RtAttr + if dev != nil { + attrs = append(attrs, + nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_DEV_NAME, nl.ZeroTerminated(*dev)), + ) + if bus != nil { + attrs = append(attrs, + nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_BUS_NAME, nl.ZeroTerminated(*bus)), + ) + } + } else { + extraFlags = extraFlags | unix.NLM_F_DUMP + } + messages, err := h.vdpaRequest(nl.VDPA_CMD_MGMTDEV_GET, extraFlags, attrs) + if err != nil { + return nil, err + } + cfgs := make([]*VDPAMGMTDev, 0, len(messages)) + for _, m := range messages { + cfg := &VDPAMGMTDev{} + cfg.parseAttributes(m) + cfgs = append(cfgs, cfg) + } + return cfgs, nil +} + +// VDPANewDev adds new VDPA device +// Equivalent to: `vdpa dev add name mgmtdev /mgmtName [params]` +func (h *Handle) VDPANewDev(name, mgmtBus, mgmtName string, params VDPANewDevParams) error { + attrs := []*nl.RtAttr{ + nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name)), + nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_DEV_NAME, nl.ZeroTerminated(mgmtName)), + } + if mgmtBus != "" { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_BUS_NAME, nl.ZeroTerminated(mgmtBus))) + } + if len(params.MACAddr) != 0 { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MACADDR, params.MACAddr)) + } + if params.MaxVQP > 0 { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MAX_VQP, nl.Uint16Attr(params.MaxVQP))) + } + if params.MTU > 0 { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MTU, nl.Uint16Attr(params.MTU))) + } + if params.Features > 0 { + attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_FEATURES, nl.Uint64Attr(params.Features))) + } + _, err := h.vdpaRequest(nl.VDPA_CMD_DEV_NEW, 0, attrs) + return err +} + +// VDPADelDev removes VDPA device +// Equivalent to: `vdpa dev del ` +func (h *Handle) VDPADelDev(name string) error { + _, err := h.vdpaRequest(nl.VDPA_CMD_DEV_DEL, 0, []*nl.RtAttr{ + nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name))}) + return err +} + +// VDPAGetDevList returns list of VDPA devices +// Equivalent to: `vdpa dev show` +func (h *Handle) VDPAGetDevList() ([]*VDPADev, error) { + return h.vdpaDevGet(nil) +} + +// VDPAGetDevByName returns VDPA device selected by name +// Equivalent to: `vdpa dev show ` +func (h *Handle) VDPAGetDevByName(name string) (*VDPADev, error) { + devs, err := h.vdpaDevGet(&name) + if err != nil { + return nil, err + } + if len(devs) == 0 { + return nil, fmt.Errorf("device not found") + } + return devs[0], nil +} + +// VDPAGetDevConfigList returns list of VDPA devices configurations +// Equivalent to: `vdpa dev config show` +func (h *Handle) VDPAGetDevConfigList() ([]*VDPADevConfig, error) { + return h.vdpaDevConfigGet(nil) +} + +// VDPAGetDevConfigByName returns VDPA device configuration selected by name +// Equivalent to: `vdpa dev config show ` +func (h *Handle) VDPAGetDevConfigByName(name string) (*VDPADevConfig, error) { + cfgs, err := h.vdpaDevConfigGet(&name) + if err != nil { + return nil, err + } + if len(cfgs) == 0 { + return nil, fmt.Errorf("configuration not found") + } + return cfgs[0], nil +} + +// VDPAGetDevVStats returns vstats for VDPA device +// Equivalent to: `vdpa dev vstats show qidx ` +func (h *Handle) VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStats, error) { + messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_VSTATS_GET, 0, []*nl.RtAttr{ + nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name)), + nl.NewRtAttr(nl.VDPA_ATTR_DEV_QUEUE_INDEX, nl.Uint32Attr(queueIndex)), + }) + if err != nil { + return nil, err + } + if len(messages) == 0 { + return nil, fmt.Errorf("stats not found") + } + stats := &VDPADevVStats{} + stats.parseAttributes(messages[0]) + return stats, nil +} + +// VDPAGetMGMTDevList returns list of mgmt devices +// Equivalent to: `vdpa mgmtdev show` +func (h *Handle) VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) { + return h.vdpaMGMTDevGet(nil, nil) +} + +// VDPAGetMGMTDevByBusAndName returns mgmt devices selected by bus and name +// Equivalent to: `vdpa mgmtdev show /` +func (h *Handle) VDPAGetMGMTDevByBusAndName(bus, name string) (*VDPAMGMTDev, error) { + var busPtr *string + if bus != "" { + busPtr = &bus + } + devs, err := h.vdpaMGMTDevGet(busPtr, &name) + if err != nil { + return nil, err + } + if len(devs) == 0 { + return nil, fmt.Errorf("mgmtdev not found") + } + return devs[0], nil +} diff --git a/vdpa_linux_test.go b/vdpa_linux_test.go new file mode 100644 index 00000000..91a488e4 --- /dev/null +++ b/vdpa_linux_test.go @@ -0,0 +1,270 @@ +package netlink + +import ( + "errors" + "syscall" + "testing" + + "github.com/vishvananda/netlink/nl" +) + +// tests in this package require following modules: vdpa, vdpa_sim, vdpa_sim_net +// The vpda_sim_net module creates virtual VDPA mgmt device with name vdpasim_net. + +const ( + vdpaSimMGMTDev = "vdpasim_net" + vdpaTestDeviceName = "__nl_test_dev" +) + +var ( + vdapTestReqModules = []string{"vdpa", "vdpa_sim", "vdpa_sim_net"} +) + +func setupVDPATest(t *testing.T, reqCommands ...int) func() { + t.Helper() + skipUnlessRoot(t) + skipUnlessKModuleLoaded(t, vdapTestReqModules...) + gFam, err := GenlFamilyGet(nl.VDPA_GENL_NAME) + if err != nil { + t.Skip("can't check for supported VDPA commands") + } + for _, c := range reqCommands { + found := false + for _, supportedOpt := range gFam.Ops { + if supportedOpt.ID == uint32(c) { + found = true + } + } + if !found { + t.Skip("host doesn't support required VDPA command for the test") + } + } + return func() { + _ = VDPADelDev(vdpaTestDeviceName) + } +} + +func TestVDPAGetMGMTDevList(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_MGMTDEV_GET)() + mgmtDevs, err := VDPAGetMGMTDevList() + if err != nil { + t.Fatalf("Failed to list VDPA mgmt devs: %v", err) + } + simMGMTFound := false + for _, d := range mgmtDevs { + if d.DevName != vdpaSimMGMTDev || d.BusName != "" { + continue + } + simMGMTFound = true + checkVDPAMGMTDev(t, d) + } + if !simMGMTFound { + t.Fatal("VDPA vdpasim_net MGMT device not found") + } +} + +func TestVDPAGetMGMTDevByBusAndName(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_MGMTDEV_GET)() + mgmtDev, err := VDPAGetMGMTDevByBusAndName("", vdpaSimMGMTDev) + if err != nil { + t.Fatalf("Failed to get VDPA sim mgmt dev: %v", err) + } + checkVDPAMGMTDev(t, mgmtDev) + if mgmtDev.DevName != vdpaSimMGMTDev || mgmtDev.BusName != "" { + t.Fatalf("Invalid device received for Get call, expected: %s, actual: %s", vdpaSimMGMTDev, mgmtDev.DevName) + } +} + +func TestVDPAGetMGMTDevByBusAndName_Unknown_Device(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_MGMTDEV_GET)() + _, err := VDPAGetMGMTDevByBusAndName("pci", "__should_not_exist") + if !errors.Is(err, syscall.ENODEV) { + t.Fatal("VDPAGetMGMTDevByBusAndName returns unexpected error for unknown device") + } +} + +func TestVDPANewDev(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_NEW)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + _, err := VDPAGetDevByName(vdpaTestDeviceName) + if err != nil { + t.Fatalf("failed to get created VDPA devvice: %v", err) + } +} + +func TestVDPANewDev_Already_Exist(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_NEW)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + err := createVDPATestDev() + if !errors.Is(err, syscall.EEXIST) { + t.Fatal("VDPANewDev returns unexpected error for device which is already exist") + } +} + +func TestVDPANewDev_Unknown_MGMT_DEV(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_NEW)() + err := VDPANewDev(vdpaTestDeviceName, "", "__should_not_exist", VDPANewDevParams{}) + if !errors.Is(err, syscall.ENODEV) { + t.Fatal("VDPANewDev returns unexpected error for unknown mgmt device") + } +} + +func TestVDPADelDev(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_DEL, nl.VDPA_CMD_DEV_NEW)() + defer setupVDPATest(t)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + if err := VDPADelDev(vdpaTestDeviceName); err != nil { + t.Fatalf("VDPADelDev failed: %v", err) + } +} + +func TestVDPADelDev_Unknown_Device(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_DEL)() + err := VDPADelDev("__should_not_exist") + if !errors.Is(err, syscall.ENODEV) { + t.Fatal("VDPADelDev returns unexpected error for unknown device") + } +} + +func TestVDPAGetDevList(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_NEW)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + devs, err := VDPAGetDevList() + if err != nil { + t.Fatalf("VDPAGetDevList failed: %v", err) + } + testDevFound := false + for _, d := range devs { + if d.Name != vdpaTestDeviceName { + continue + } + testDevFound = true + checkVDPADev(t, d) + } + if !testDevFound { + t.Fatal("VDPA test device not found") + } +} + +func TestVDPAGetDevByName(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_NEW)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + dev, err := VDPAGetDevByName(vdpaTestDeviceName) + if err != nil { + t.Fatalf("VDPAGetDevByName failed: %v", err) + } + checkVDPADev(t, dev) + if dev.Name != vdpaTestDeviceName { + t.Fatalf("Invalid device received for Get call, expected: %s, actual: %s", vdpaTestDeviceName, dev.Name) + } +} + +func TestVDPAGetDevByName_Unknown(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET)() + _, err := VDPAGetDevByName("__should_not_exist") + if !errors.Is(err, syscall.ENODEV) { + t.Fatal("VDPAGetDevByName returns unexpected error for unknown device") + } +} + +func TestVDPAGetDevConfigList(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_CONFIG_GET)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + devConfs, err := VDPAGetDevConfigList() + if err != nil { + t.Fatalf("VDPAGetDevConfigList failed: %v", err) + } + testDevConfFound := false + for _, d := range devConfs { + if d.Name != vdpaTestDeviceName { + continue + } + testDevConfFound = true + checkVDPADevConf(t, d) + } + if !testDevConfFound { + t.Fatal("VDPA test device config not found") + } +} + +func TestVDPAGetDevConfigByName(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_CONFIG_GET)() + if err := createVDPATestDev(); err != nil { + t.Fatalf("failed to create VDPA device: %v", err) + } + dev, err := VDPAGetDevConfigByName(vdpaTestDeviceName) + if err != nil { + t.Fatalf("VDPAGetDevConfigByName failed: %v", err) + } + checkVDPADevConf(t, dev) + if dev.Name != vdpaTestDeviceName { + t.Fatalf("Invalid device received for Get call, expected: %s, actual: %s", vdpaTestDeviceName, dev.Name) + } +} + +func TestVDPAGetDevConfigByName_Unknowm(t *testing.T) { + defer setupVDPATest(t, nl.VDPA_CMD_DEV_GET, nl.VDPA_CMD_DEV_CONFIG_GET)() + _, err := VDPAGetDevConfigByName("__should_not_exist") + if !errors.Is(err, syscall.ENODEV) { + t.Fatal("VDPAGetDevConfigByName returns unexpected error for unknown device") + } +} + +func TestSetGetBits(t *testing.T) { + features := SetBits(0, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_MQ) + if !IsBitSet(features, VIRTIO_NET_F_CSUM) || !IsBitSet(features, VIRTIO_NET_F_MQ) { + t.Fatal("BitSet test failed") + } + if IsBitSet(features, VIRTIO_NET_F_STATUS) { + t.Fatal("unexpected bit is set") + } +} + +func createVDPATestDev() error { + return VDPANewDev(vdpaTestDeviceName, "", vdpaSimMGMTDev, VDPANewDevParams{}) +} + +func checkVDPAMGMTDev(t *testing.T, d *VDPAMGMTDev) { + if d == nil { + t.Fatal("VDPA MGMT dev is nil") + } + if d.DevName == "" { + t.Fatal("VDPA MGMT dev name is not set") + } +} + +func checkVDPADev(t *testing.T, d *VDPADev) { + if d == nil { + t.Fatal("VDPA dev is nil") + } + if d.Name == "" { + t.Fatal("VDPA dev name is not set") + } + if d.ID == 0 { + t.Fatal("VDPA dev ID is not set") + } +} + +func checkVDPADevConf(t *testing.T, d *VDPADevConfig) { + if d == nil { + t.Fatal("VDPA dev config is nil") + } + if d.Name == "" { + t.Fatal("VDPA dev name is not set") + } + if d.ID == 0 { + t.Fatal("VDPA dev ID is not set") + } +}