mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			464 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			464 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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
 | |
| }
 |