github.com/vishvananda/netlink@v1.3.1/vdpa_linux.go (about) 1 package netlink 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "syscall" 8 9 "golang.org/x/sys/unix" 10 11 "github.com/vishvananda/netlink/nl" 12 ) 13 14 type vdpaDevID struct { 15 Name string 16 ID uint32 17 } 18 19 // VDPADev contains info about VDPA device 20 type VDPADev struct { 21 vdpaDevID 22 VendorID uint32 23 MaxVQS uint32 24 MaxVQSize uint16 25 MinVQSize uint16 26 } 27 28 // VDPADevConfig contains configuration of the VDPA device 29 type VDPADevConfig struct { 30 vdpaDevID 31 Features uint64 32 NegotiatedFeatures uint64 33 Net VDPADevConfigNet 34 } 35 36 // VDPADevVStats conatins vStats for the VDPA device 37 type VDPADevVStats struct { 38 vdpaDevID 39 QueueIndex uint32 40 Vendor []VDPADevVStatsVendor 41 NegotiatedFeatures uint64 42 } 43 44 // VDPADevVStatsVendor conatins name and value for vendor specific vstat option 45 type VDPADevVStatsVendor struct { 46 Name string 47 Value uint64 48 } 49 50 // VDPADevConfigNet conatins status and net config for the VDPA device 51 type VDPADevConfigNet struct { 52 Status VDPADevConfigNetStatus 53 Cfg VDPADevConfigNetCfg 54 } 55 56 // VDPADevConfigNetStatus contains info about net status 57 type VDPADevConfigNetStatus struct { 58 LinkUp bool 59 Announce bool 60 } 61 62 // VDPADevConfigNetCfg contains net config for the VDPA device 63 type VDPADevConfigNetCfg struct { 64 MACAddr net.HardwareAddr 65 MaxVQP uint16 66 MTU uint16 67 } 68 69 // VDPAMGMTDev conatins info about VDPA management device 70 type VDPAMGMTDev struct { 71 BusName string 72 DevName string 73 SupportedClasses uint64 74 SupportedFeatures uint64 75 MaxVQS uint32 76 } 77 78 // VDPANewDevParams contains parameters for new VDPA device 79 // use SetBits to configure requried features for the device 80 // example: 81 // 82 // VDPANewDevParams{Features: SetBits(0, VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_MAC_ADDR)} 83 type VDPANewDevParams struct { 84 MACAddr net.HardwareAddr 85 MaxVQP uint16 86 MTU uint16 87 Features uint64 88 } 89 90 // SetBits set provided bits in the uint64 input value 91 // usage example: 92 // features := SetBits(0, VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_MAC_ADDR) 93 func SetBits(input uint64, pos ...int) uint64 { 94 for _, p := range pos { 95 input |= 1 << uint64(p) 96 } 97 return input 98 } 99 100 // IsBitSet check if specific bit is set in the uint64 input value 101 // usage example: 102 // hasNetClass := IsBitSet(mgmtDev, VIRTIO_ID_NET) 103 func IsBitSet(input uint64, pos int) bool { 104 val := input & (1 << uint64(pos)) 105 return val > 0 106 } 107 108 // VDPANewDev adds new VDPA device 109 // Equivalent to: `vdpa dev add name <name> mgmtdev <mgmtBus>/mgmtName [params]` 110 func VDPANewDev(name, mgmtBus, mgmtName string, params VDPANewDevParams) error { 111 return pkgHandle.VDPANewDev(name, mgmtBus, mgmtName, params) 112 } 113 114 // VDPADelDev removes VDPA device 115 // Equivalent to: `vdpa dev del <name>` 116 func VDPADelDev(name string) error { 117 return pkgHandle.VDPADelDev(name) 118 } 119 120 // VDPAGetDevList returns list of VDPA devices 121 // Equivalent to: `vdpa dev show` 122 // 123 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 124 // or incomplete. 125 func VDPAGetDevList() ([]*VDPADev, error) { 126 return pkgHandle.VDPAGetDevList() 127 } 128 129 // VDPAGetDevByName returns VDPA device selected by name 130 // Equivalent to: `vdpa dev show <name>` 131 func VDPAGetDevByName(name string) (*VDPADev, error) { 132 return pkgHandle.VDPAGetDevByName(name) 133 } 134 135 // VDPAGetDevConfigList returns list of VDPA devices configurations 136 // Equivalent to: `vdpa dev config show` 137 // 138 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 139 // or incomplete. 140 func VDPAGetDevConfigList() ([]*VDPADevConfig, error) { 141 return pkgHandle.VDPAGetDevConfigList() 142 } 143 144 // VDPAGetDevConfigByName returns VDPA device configuration selected by name 145 // Equivalent to: `vdpa dev config show <name>` 146 func VDPAGetDevConfigByName(name string) (*VDPADevConfig, error) { 147 return pkgHandle.VDPAGetDevConfigByName(name) 148 } 149 150 // VDPAGetDevVStats returns vstats for VDPA device 151 // Equivalent to: `vdpa dev vstats show <name> qidx <queueIndex>` 152 func VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStats, error) { 153 return pkgHandle.VDPAGetDevVStats(name, queueIndex) 154 } 155 156 // VDPAGetMGMTDevList returns list of mgmt devices 157 // Equivalent to: `vdpa mgmtdev show` 158 // 159 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 160 // or incomplete. 161 func VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) { 162 return pkgHandle.VDPAGetMGMTDevList() 163 } 164 165 // VDPAGetMGMTDevByBusAndName returns mgmt devices selected by bus and name 166 // Equivalent to: `vdpa mgmtdev show <bus>/<name>` 167 func VDPAGetMGMTDevByBusAndName(bus, name string) (*VDPAMGMTDev, error) { 168 return pkgHandle.VDPAGetMGMTDevByBusAndName(bus, name) 169 } 170 171 type vdpaNetlinkMessage []syscall.NetlinkRouteAttr 172 173 func (id *vdpaDevID) parseIDAttribute(attr syscall.NetlinkRouteAttr) { 174 switch attr.Attr.Type { 175 case nl.VDPA_ATTR_DEV_NAME: 176 id.Name = nl.BytesToString(attr.Value) 177 case nl.VDPA_ATTR_DEV_ID: 178 id.ID = native.Uint32(attr.Value) 179 } 180 } 181 182 func (netStatus *VDPADevConfigNetStatus) parseStatusAttribute(value []byte) { 183 a := native.Uint16(value) 184 netStatus.Announce = (a & VIRTIO_NET_S_ANNOUNCE) > 0 185 netStatus.LinkUp = (a & VIRTIO_NET_S_LINK_UP) > 0 186 } 187 188 func (d *VDPADev) parseAttributes(attrs vdpaNetlinkMessage) { 189 for _, a := range attrs { 190 d.parseIDAttribute(a) 191 switch a.Attr.Type { 192 case nl.VDPA_ATTR_DEV_VENDOR_ID: 193 d.VendorID = native.Uint32(a.Value) 194 case nl.VDPA_ATTR_DEV_MAX_VQS: 195 d.MaxVQS = native.Uint32(a.Value) 196 case nl.VDPA_ATTR_DEV_MAX_VQ_SIZE: 197 d.MaxVQSize = native.Uint16(a.Value) 198 case nl.VDPA_ATTR_DEV_MIN_VQ_SIZE: 199 d.MinVQSize = native.Uint16(a.Value) 200 } 201 } 202 } 203 204 func (c *VDPADevConfig) parseAttributes(attrs vdpaNetlinkMessage) { 205 for _, a := range attrs { 206 c.parseIDAttribute(a) 207 switch a.Attr.Type { 208 case nl.VDPA_ATTR_DEV_NET_CFG_MACADDR: 209 c.Net.Cfg.MACAddr = a.Value 210 case nl.VDPA_ATTR_DEV_NET_STATUS: 211 c.Net.Status.parseStatusAttribute(a.Value) 212 case nl.VDPA_ATTR_DEV_NET_CFG_MAX_VQP: 213 c.Net.Cfg.MaxVQP = native.Uint16(a.Value) 214 case nl.VDPA_ATTR_DEV_NET_CFG_MTU: 215 c.Net.Cfg.MTU = native.Uint16(a.Value) 216 case nl.VDPA_ATTR_DEV_FEATURES: 217 c.Features = native.Uint64(a.Value) 218 case nl.VDPA_ATTR_DEV_NEGOTIATED_FEATURES: 219 c.NegotiatedFeatures = native.Uint64(a.Value) 220 } 221 } 222 } 223 224 func (s *VDPADevVStats) parseAttributes(attrs vdpaNetlinkMessage) { 225 for _, a := range attrs { 226 s.parseIDAttribute(a) 227 switch a.Attr.Type { 228 case nl.VDPA_ATTR_DEV_QUEUE_INDEX: 229 s.QueueIndex = native.Uint32(a.Value) 230 case nl.VDPA_ATTR_DEV_VENDOR_ATTR_NAME: 231 s.Vendor = append(s.Vendor, VDPADevVStatsVendor{Name: nl.BytesToString(a.Value)}) 232 case nl.VDPA_ATTR_DEV_VENDOR_ATTR_VALUE: 233 if len(s.Vendor) == 0 { 234 break 235 } 236 s.Vendor[len(s.Vendor)-1].Value = native.Uint64(a.Value) 237 case nl.VDPA_ATTR_DEV_NEGOTIATED_FEATURES: 238 s.NegotiatedFeatures = native.Uint64(a.Value) 239 } 240 } 241 } 242 243 func (d *VDPAMGMTDev) parseAttributes(attrs vdpaNetlinkMessage) { 244 for _, a := range attrs { 245 switch a.Attr.Type { 246 case nl.VDPA_ATTR_MGMTDEV_BUS_NAME: 247 d.BusName = nl.BytesToString(a.Value) 248 case nl.VDPA_ATTR_MGMTDEV_DEV_NAME: 249 d.DevName = nl.BytesToString(a.Value) 250 case nl.VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES: 251 d.SupportedClasses = native.Uint64(a.Value) 252 case nl.VDPA_ATTR_DEV_SUPPORTED_FEATURES: 253 d.SupportedFeatures = native.Uint64(a.Value) 254 case nl.VDPA_ATTR_DEV_MGMTDEV_MAX_VQS: 255 d.MaxVQS = native.Uint32(a.Value) 256 } 257 } 258 } 259 260 func (h *Handle) vdpaRequest(command uint8, extraFlags int, attrs []*nl.RtAttr) ([]vdpaNetlinkMessage, error) { 261 f, err := h.GenlFamilyGet(nl.VDPA_GENL_NAME) 262 if err != nil { 263 return nil, err 264 } 265 req := h.newNetlinkRequest(int(f.ID), unix.NLM_F_ACK|extraFlags) 266 req.AddData(&nl.Genlmsg{ 267 Command: command, 268 Version: nl.VDPA_GENL_VERSION, 269 }) 270 for _, a := range attrs { 271 req.AddData(a) 272 } 273 274 resp, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) 275 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 276 return nil, executeErr 277 } 278 messages := make([]vdpaNetlinkMessage, 0, len(resp)) 279 for _, m := range resp { 280 attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) 281 if err != nil { 282 return nil, err 283 } 284 messages = append(messages, attrs) 285 } 286 return messages, executeErr 287 } 288 289 // dump all devices if dev is nil 290 // 291 // If dev is nil and the returned error is [ErrDumpInterrupted], results may be inconsistent 292 // or incomplete. 293 func (h *Handle) vdpaDevGet(dev *string) ([]*VDPADev, error) { 294 var extraFlags int 295 var attrs []*nl.RtAttr 296 if dev != nil { 297 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(*dev))) 298 } else { 299 extraFlags = extraFlags | unix.NLM_F_DUMP 300 } 301 messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_DEV_GET, extraFlags, attrs) 302 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 303 return nil, executeErr 304 } 305 devs := make([]*VDPADev, 0, len(messages)) 306 for _, m := range messages { 307 d := &VDPADev{} 308 d.parseAttributes(m) 309 devs = append(devs, d) 310 } 311 return devs, executeErr 312 } 313 314 // dump all devices if dev is nil 315 // 316 // If dev is nil, and the returned error is [ErrDumpInterrupted], results may be inconsistent 317 // or incomplete. 318 func (h *Handle) vdpaDevConfigGet(dev *string) ([]*VDPADevConfig, error) { 319 var extraFlags int 320 var attrs []*nl.RtAttr 321 if dev != nil { 322 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(*dev))) 323 } else { 324 extraFlags = extraFlags | unix.NLM_F_DUMP 325 } 326 messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_DEV_CONFIG_GET, extraFlags, attrs) 327 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 328 return nil, executeErr 329 } 330 cfgs := make([]*VDPADevConfig, 0, len(messages)) 331 for _, m := range messages { 332 cfg := &VDPADevConfig{} 333 cfg.parseAttributes(m) 334 cfgs = append(cfgs, cfg) 335 } 336 return cfgs, executeErr 337 } 338 339 // dump all devices if dev is nil 340 // 341 // If dev is nil and the returned error is [ErrDumpInterrupted], results may be inconsistent 342 // or incomplete. 343 func (h *Handle) vdpaMGMTDevGet(bus, dev *string) ([]*VDPAMGMTDev, error) { 344 var extraFlags int 345 var attrs []*nl.RtAttr 346 if dev != nil { 347 attrs = append(attrs, 348 nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_DEV_NAME, nl.ZeroTerminated(*dev)), 349 ) 350 if bus != nil { 351 attrs = append(attrs, 352 nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_BUS_NAME, nl.ZeroTerminated(*bus)), 353 ) 354 } 355 } else { 356 extraFlags = extraFlags | unix.NLM_F_DUMP 357 } 358 messages, executeErr := h.vdpaRequest(nl.VDPA_CMD_MGMTDEV_GET, extraFlags, attrs) 359 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 360 return nil, executeErr 361 } 362 cfgs := make([]*VDPAMGMTDev, 0, len(messages)) 363 for _, m := range messages { 364 cfg := &VDPAMGMTDev{} 365 cfg.parseAttributes(m) 366 cfgs = append(cfgs, cfg) 367 } 368 return cfgs, executeErr 369 } 370 371 // VDPANewDev adds new VDPA device 372 // Equivalent to: `vdpa dev add name <name> mgmtdev <mgmtBus>/mgmtName [params]` 373 func (h *Handle) VDPANewDev(name, mgmtBus, mgmtName string, params VDPANewDevParams) error { 374 attrs := []*nl.RtAttr{ 375 nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name)), 376 nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_DEV_NAME, nl.ZeroTerminated(mgmtName)), 377 } 378 if mgmtBus != "" { 379 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_MGMTDEV_BUS_NAME, nl.ZeroTerminated(mgmtBus))) 380 } 381 if len(params.MACAddr) != 0 { 382 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MACADDR, params.MACAddr)) 383 } 384 if params.MaxVQP > 0 { 385 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MAX_VQP, nl.Uint16Attr(params.MaxVQP))) 386 } 387 if params.MTU > 0 { 388 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_NET_CFG_MTU, nl.Uint16Attr(params.MTU))) 389 } 390 if params.Features > 0 { 391 attrs = append(attrs, nl.NewRtAttr(nl.VDPA_ATTR_DEV_FEATURES, nl.Uint64Attr(params.Features))) 392 } 393 _, err := h.vdpaRequest(nl.VDPA_CMD_DEV_NEW, 0, attrs) 394 return err 395 } 396 397 // VDPADelDev removes VDPA device 398 // Equivalent to: `vdpa dev del <name>` 399 func (h *Handle) VDPADelDev(name string) error { 400 _, err := h.vdpaRequest(nl.VDPA_CMD_DEV_DEL, 0, []*nl.RtAttr{ 401 nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name))}) 402 return err 403 } 404 405 // VDPAGetDevList returns list of VDPA devices 406 // Equivalent to: `vdpa dev show` 407 // 408 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 409 // or incomplete. 410 func (h *Handle) VDPAGetDevList() ([]*VDPADev, error) { 411 return h.vdpaDevGet(nil) 412 } 413 414 // VDPAGetDevByName returns VDPA device selected by name 415 // Equivalent to: `vdpa dev show <name>` 416 func (h *Handle) VDPAGetDevByName(name string) (*VDPADev, error) { 417 devs, err := h.vdpaDevGet(&name) 418 if err != nil { 419 return nil, err 420 } 421 if len(devs) == 0 { 422 return nil, fmt.Errorf("device not found") 423 } 424 return devs[0], nil 425 } 426 427 // VDPAGetDevConfigList returns list of VDPA devices configurations 428 // Equivalent to: `vdpa dev config show` 429 // 430 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 431 // or incomplete. 432 func (h *Handle) VDPAGetDevConfigList() ([]*VDPADevConfig, error) { 433 return h.vdpaDevConfigGet(nil) 434 } 435 436 // VDPAGetDevConfigByName returns VDPA device configuration selected by name 437 // Equivalent to: `vdpa dev config show <name>` 438 func (h *Handle) VDPAGetDevConfigByName(name string) (*VDPADevConfig, error) { 439 cfgs, err := h.vdpaDevConfigGet(&name) 440 if err != nil { 441 return nil, err 442 } 443 if len(cfgs) == 0 { 444 return nil, fmt.Errorf("configuration not found") 445 } 446 return cfgs[0], nil 447 } 448 449 // VDPAGetDevVStats returns vstats for VDPA device 450 // Equivalent to: `vdpa dev vstats show <name> qidx <queueIndex>` 451 func (h *Handle) VDPAGetDevVStats(name string, queueIndex uint32) (*VDPADevVStats, error) { 452 messages, err := h.vdpaRequest(nl.VDPA_CMD_DEV_VSTATS_GET, 0, []*nl.RtAttr{ 453 nl.NewRtAttr(nl.VDPA_ATTR_DEV_NAME, nl.ZeroTerminated(name)), 454 nl.NewRtAttr(nl.VDPA_ATTR_DEV_QUEUE_INDEX, nl.Uint32Attr(queueIndex)), 455 }) 456 if err != nil { 457 return nil, err 458 } 459 if len(messages) == 0 { 460 return nil, fmt.Errorf("stats not found") 461 } 462 stats := &VDPADevVStats{} 463 stats.parseAttributes(messages[0]) 464 return stats, nil 465 } 466 467 // VDPAGetMGMTDevList returns list of mgmt devices 468 // Equivalent to: `vdpa mgmtdev show` 469 // 470 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 471 // or incomplete. 472 func (h *Handle) VDPAGetMGMTDevList() ([]*VDPAMGMTDev, error) { 473 return h.vdpaMGMTDevGet(nil, nil) 474 } 475 476 // VDPAGetMGMTDevByBusAndName returns mgmt devices selected by bus and name 477 // Equivalent to: `vdpa mgmtdev show <bus>/<name>` 478 func (h *Handle) VDPAGetMGMTDevByBusAndName(bus, name string) (*VDPAMGMTDev, error) { 479 var busPtr *string 480 if bus != "" { 481 busPtr = &bus 482 } 483 devs, err := h.vdpaMGMTDevGet(busPtr, &name) 484 if err != nil { 485 return nil, err 486 } 487 if len(devs) == 0 { 488 return nil, fmt.Errorf("mgmtdev not found") 489 } 490 return devs[0], nil 491 }