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 <ykulazhenkov@nvidia.com>
This commit is contained in:
Yury Kulazhenkov 2023-12-27 17:45:01 +02:00 committed by Alessandro Boch
parent 06219cde3e
commit 857968af11
5 changed files with 929 additions and 16 deletions

View File

@ -31,6 +31,28 @@ func skipUnlessRoot(t *testing.T) {
} }
} }
func skipUnlessKModuleLoaded(t *testing.T, module ...string) {
t.Helper()
file, err := ioutil.ReadFile("/proc/modules")
if err != nil {
t.Fatal("Failed to open /proc/modules", err)
}
for _, mod := range module {
found := false
for _, line := range strings.Split(string(file), "\n") {
n := strings.Split(line, " ")[0]
if n == mod {
found = true
break
}
}
if !found {
t.Skipf("Test requires kmodule %q.", mod)
}
}
}
func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest { func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
skipUnlessRoot(t) skipUnlessRoot(t)
@ -159,22 +181,7 @@ func setUpSEG6NetlinkTest(t *testing.T) tearDownNetlinkTest {
} }
func setUpNetlinkTestWithKModule(t *testing.T, name string) tearDownNetlinkTest { func setUpNetlinkTestWithKModule(t *testing.T, name string) tearDownNetlinkTest {
file, err := ioutil.ReadFile("/proc/modules") skipUnlessKModuleLoaded(t, name)
if err != nil {
t.Fatal("Failed to open /proc/modules", err)
}
found := false
for _, line := range strings.Split(string(file), "\n") {
n := strings.Split(line, " ")[0]
if n == name {
found = true
break
}
}
if !found {
t.Skipf("Test requires kmodule %q.", name)
}
return setUpNetlinkTest(t) return setUpNetlinkTest(t)
} }

41
nl/vdpa_linux.go Normal file
View File

@ -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
)

463
vdpa_linux.go Normal file
View File

@ -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 <name> mgmtdev <mgmtBus>/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 <name>`
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 <name>`
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 <name>`
func VDPAGetDevConfigByName(name string) (*VDPADevConfig, error) {
return pkgHandle.VDPAGetDevConfigByName(name)
}
// VDPAGetDevVStats returns vstats for VDPA device
// Equivalent to: `vdpa dev vstats show <name> qidx <queueIndex>`
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 <bus>/<name>`
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 <name> mgmtdev <mgmtBus>/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 <name>`
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 <name>`
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 <name>`
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 <name> qidx <queueIndex>`
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 <bus>/<name>`
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
}

270
vdpa_linux_test.go Normal file
View File

@ -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")
}
}

132
virtio.go Normal file
View File

@ -0,0 +1,132 @@
package netlink
// features for virtio net
const (
VIRTIO_NET_F_CSUM = 0 // Host handles pkts w/ partial csum
VIRTIO_NET_F_GUEST_CSUM = 1 // Guest handles pkts w/ partial csum
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS = 2 // Dynamic offload configuration.
VIRTIO_NET_F_MTU = 3 // Initial MTU advice
VIRTIO_NET_F_MAC = 5 // Host has given MAC address.
VIRTIO_NET_F_GUEST_TSO4 = 7 // Guest can handle TSOv4 in.
VIRTIO_NET_F_GUEST_TSO6 = 8 // Guest can handle TSOv6 in.
VIRTIO_NET_F_GUEST_ECN = 9 // Guest can handle TSO[6] w/ ECN in.
VIRTIO_NET_F_GUEST_UFO = 10 // Guest can handle UFO in.
VIRTIO_NET_F_HOST_TSO4 = 11 // Host can handle TSOv4 in.
VIRTIO_NET_F_HOST_TSO6 = 12 // Host can handle TSOv6 in.
VIRTIO_NET_F_HOST_ECN = 13 // Host can handle TSO[6] w/ ECN in.
VIRTIO_NET_F_HOST_UFO = 14 // Host can handle UFO in.
VIRTIO_NET_F_MRG_RXBUF = 15 // Host can merge receive buffers.
VIRTIO_NET_F_STATUS = 16 // virtio_net_config.status available
VIRTIO_NET_F_CTRL_VQ = 17 // Control channel available
VIRTIO_NET_F_CTRL_RX = 18 // Control channel RX mode support
VIRTIO_NET_F_CTRL_VLAN = 19 // Control channel VLAN filtering
VIRTIO_NET_F_CTRL_RX_EXTRA = 20 // Extra RX mode control support
VIRTIO_NET_F_GUEST_ANNOUNCE = 21 // Guest can announce device on the* network
VIRTIO_NET_F_MQ = 22 // Device supports Receive Flow Steering
VIRTIO_NET_F_CTRL_MAC_ADDR = 23 // Set MAC address
VIRTIO_NET_F_VQ_NOTF_COAL = 52 // Device supports virtqueue notification coalescing
VIRTIO_NET_F_NOTF_COAL = 53 // Device supports notifications coalescing
VIRTIO_NET_F_GUEST_USO4 = 54 // Guest can handle USOv4 in.
VIRTIO_NET_F_GUEST_USO6 = 55 // Guest can handle USOv6 in.
VIRTIO_NET_F_HOST_USO = 56 // Host can handle USO in.
VIRTIO_NET_F_HASH_REPORT = 57 // Supports hash report
VIRTIO_NET_F_GUEST_HDRLEN = 59 // Guest provides the exact hdr_len value.
VIRTIO_NET_F_RSS = 60 // Supports RSS RX steering
VIRTIO_NET_F_RSC_EXT = 61 // extended coalescing info
VIRTIO_NET_F_STANDBY = 62 // Act as standby for another device with the same MAC.
VIRTIO_NET_F_SPEED_DUPLEX = 63 // Device set linkspeed and duplex
VIRTIO_NET_F_GSO = 6 // Host handles pkts any GSO type
)
// virtio net status
const (
VIRTIO_NET_S_LINK_UP = 1 // Link is up
VIRTIO_NET_S_ANNOUNCE = 2 // Announcement is needed
)
// virtio config
const (
// Do we get callbacks when the ring is completely used, even if we've
// suppressed them?
VIRTIO_F_NOTIFY_ON_EMPTY = 24
// Can the device handle any descriptor layout?
VIRTIO_F_ANY_LAYOUT = 27
// v1.0 compliant
VIRTIO_F_VERSION_1 = 32
// If clear - device has the platform DMA (e.g. IOMMU) bypass quirk feature.
// If set - use platform DMA tools to access the memory.
// Note the reverse polarity (compared to most other features),
// this is for compatibility with legacy systems.
VIRTIO_F_ACCESS_PLATFORM = 33
// Legacy name for VIRTIO_F_ACCESS_PLATFORM (for compatibility with old userspace)
VIRTIO_F_IOMMU_PLATFORM = VIRTIO_F_ACCESS_PLATFORM
// This feature indicates support for the packed virtqueue layout.
VIRTIO_F_RING_PACKED = 34
// Inorder feature indicates that all buffers are used by the device
// in the same order in which they have been made available.
VIRTIO_F_IN_ORDER = 35
// This feature indicates that memory accesses by the driver and the
// device are ordered in a way described by the platform.
VIRTIO_F_ORDER_PLATFORM = 36
// Does the device support Single Root I/O Virtualization?
VIRTIO_F_SR_IOV = 37
// This feature indicates that the driver passes extra data (besides
// identifying the virtqueue) in its device notifications.
VIRTIO_F_NOTIFICATION_DATA = 38
// This feature indicates that the driver uses the data provided by the device
// as a virtqueue identifier in available buffer notifications.
VIRTIO_F_NOTIF_CONFIG_DATA = 39
// This feature indicates that the driver can reset a queue individually.
VIRTIO_F_RING_RESET = 40
)
// virtio device ids
const (
VIRTIO_ID_NET = 1 // virtio net
VIRTIO_ID_BLOCK = 2 // virtio block
VIRTIO_ID_CONSOLE = 3 // virtio console
VIRTIO_ID_RNG = 4 // virtio rng
VIRTIO_ID_BALLOON = 5 // virtio balloon
VIRTIO_ID_IOMEM = 6 // virtio ioMemory
VIRTIO_ID_RPMSG = 7 // virtio remote processor messaging
VIRTIO_ID_SCSI = 8 // virtio scsi
VIRTIO_ID_9P = 9 // 9p virtio console
VIRTIO_ID_MAC80211_WLAN = 10 // virtio WLAN MAC
VIRTIO_ID_RPROC_SERIAL = 11 // virtio remoteproc serial link
VIRTIO_ID_CAIF = 12 // Virtio caif
VIRTIO_ID_MEMORY_BALLOON = 13 // virtio memory balloon
VIRTIO_ID_GPU = 16 // virtio GPU
VIRTIO_ID_CLOCK = 17 // virtio clock/timer
VIRTIO_ID_INPUT = 18 // virtio input
VIRTIO_ID_VSOCK = 19 // virtio vsock transport
VIRTIO_ID_CRYPTO = 20 // virtio crypto
VIRTIO_ID_SIGNAL_DIST = 21 // virtio signal distribution device
VIRTIO_ID_PSTORE = 22 // virtio pstore device
VIRTIO_ID_IOMMU = 23 // virtio IOMMU
VIRTIO_ID_MEM = 24 // virtio mem
VIRTIO_ID_SOUND = 25 // virtio sound
VIRTIO_ID_FS = 26 // virtio filesystem
VIRTIO_ID_PMEM = 27 // virtio pmem
VIRTIO_ID_RPMB = 28 // virtio rpmb
VIRTIO_ID_MAC80211_HWSIM = 29 // virtio mac80211-hwsim
VIRTIO_ID_VIDEO_ENCODER = 30 // virtio video encoder
VIRTIO_ID_VIDEO_DECODER = 31 // virtio video decoder
VIRTIO_ID_SCMI = 32 // virtio SCMI
VIRTIO_ID_NITRO_SEC_MOD = 33 // virtio nitro secure module
VIRTIO_ID_I2C_ADAPTER = 34 // virtio i2c adapter
VIRTIO_ID_WATCHDOG = 35 // virtio watchdog
VIRTIO_ID_CAN = 36 // virtio can
VIRTIO_ID_DMABUF = 37 // virtio dmabuf
VIRTIO_ID_PARAM_SERV = 38 // virtio parameter server
VIRTIO_ID_AUDIO_POLICY = 39 // virtio audio policy
VIRTIO_ID_BT = 40 // virtio bluetooth
VIRTIO_ID_GPIO = 41 // virtio gpio
// Virtio Transitional IDs
VIRTIO_TRANS_ID_NET = 0x1000 // transitional virtio net
VIRTIO_TRANS_ID_BLOCK = 0x1001 // transitional virtio block
VIRTIO_TRANS_ID_BALLOON = 0x1002 // transitional virtio balloon
VIRTIO_TRANS_ID_CONSOLE = 0x1003 // transitional virtio console
VIRTIO_TRANS_ID_SCSI = 0x1004 // transitional virtio SCSI
VIRTIO_TRANS_ID_RNG = 0x1005 // transitional virtio rng
VIRTIO_TRANS_ID_9P = 0x1009 // transitional virtio 9p console
)