github.com/vishvananda/netlink@v1.3.1/rdma_link_linux.go (about) 1 package netlink 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "net" 9 10 "github.com/vishvananda/netlink/nl" 11 "golang.org/x/sys/unix" 12 ) 13 14 // LinkAttrs represents data shared by most link types 15 type RdmaLinkAttrs struct { 16 Index uint32 17 Name string 18 FirmwareVersion string 19 NodeGuid string 20 SysImageGuid string 21 NumPorts uint32 22 } 23 24 // Link represents a rdma device from netlink. 25 type RdmaLink struct { 26 Attrs RdmaLinkAttrs 27 } 28 29 func getProtoField(clientType int, op int) int { 30 return ((clientType << nl.RDMA_NL_GET_CLIENT_SHIFT) | op) 31 } 32 33 func uint64ToGuidString(guid uint64) string { 34 //Convert to byte array 35 sysGuidBytes := new(bytes.Buffer) 36 binary.Write(sysGuidBytes, binary.LittleEndian, guid) 37 38 //Convert to HardwareAddr 39 sysGuidNet := net.HardwareAddr(sysGuidBytes.Bytes()) 40 41 //Get the String 42 return sysGuidNet.String() 43 } 44 45 func executeOneGetRdmaLink(data []byte) (*RdmaLink, error) { 46 47 link := RdmaLink{} 48 49 reader := bytes.NewReader(data) 50 for reader.Len() >= 4 { 51 _, attrType, len, value := parseNfAttrTLV(reader) 52 53 switch attrType { 54 case nl.RDMA_NLDEV_ATTR_DEV_INDEX: 55 var Index uint32 56 r := bytes.NewReader(value) 57 binary.Read(r, nl.NativeEndian(), &Index) 58 link.Attrs.Index = Index 59 case nl.RDMA_NLDEV_ATTR_DEV_NAME: 60 link.Attrs.Name = string(value[0 : len-1]) 61 case nl.RDMA_NLDEV_ATTR_FW_VERSION: 62 link.Attrs.FirmwareVersion = string(value[0 : len-1]) 63 case nl.RDMA_NLDEV_ATTR_NODE_GUID: 64 var guid uint64 65 r := bytes.NewReader(value) 66 binary.Read(r, nl.NativeEndian(), &guid) 67 link.Attrs.NodeGuid = uint64ToGuidString(guid) 68 case nl.RDMA_NLDEV_ATTR_SYS_IMAGE_GUID: 69 var sysGuid uint64 70 r := bytes.NewReader(value) 71 binary.Read(r, nl.NativeEndian(), &sysGuid) 72 link.Attrs.SysImageGuid = uint64ToGuidString(sysGuid) 73 case nl.RDMA_NLDEV_ATTR_PORT_INDEX: 74 var availablePort uint32 75 r := bytes.NewReader(value) 76 binary.Read(r, nl.NativeEndian(), &availablePort) 77 link.Attrs.NumPorts = availablePort 78 } 79 if (len % 4) != 0 { 80 // Skip pad bytes 81 reader.Seek(int64(4-(len%4)), seekCurrent) 82 } 83 } 84 return &link, nil 85 } 86 87 func execRdmaSetLink(req *nl.NetlinkRequest) error { 88 89 _, err := req.Execute(unix.NETLINK_RDMA, 0) 90 return err 91 } 92 93 // RdmaLinkList gets a list of RDMA link devices. 94 // Equivalent to: `rdma dev show` 95 // 96 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 97 // or incomplete. 98 func RdmaLinkList() ([]*RdmaLink, error) { 99 return pkgHandle.RdmaLinkList() 100 } 101 102 // RdmaLinkList gets a list of RDMA link devices. 103 // Equivalent to: `rdma dev show` 104 // 105 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 106 // or incomplete. 107 func (h *Handle) RdmaLinkList() ([]*RdmaLink, error) { 108 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_GET) 109 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) 110 111 msgs, executeErr := req.Execute(unix.NETLINK_RDMA, 0) 112 if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { 113 return nil, executeErr 114 } 115 116 var res []*RdmaLink 117 for _, m := range msgs { 118 link, err := executeOneGetRdmaLink(m) 119 if err != nil { 120 return nil, err 121 } 122 res = append(res, link) 123 } 124 125 return res, executeErr 126 } 127 128 // RdmaLinkByName finds a link by name and returns a pointer to the object if 129 // found and nil error, otherwise returns error code. 130 // 131 // If the returned error is [ErrDumpInterrupted], the result may be missing or 132 // outdated and the caller should retry. 133 func RdmaLinkByName(name string) (*RdmaLink, error) { 134 return pkgHandle.RdmaLinkByName(name) 135 } 136 137 // RdmaLinkByName finds a link by name and returns a pointer to the object if 138 // found and nil error, otherwise returns error code. 139 // 140 // If the returned error is [ErrDumpInterrupted], the result may be missing or 141 // outdated and the caller should retry. 142 func (h *Handle) RdmaLinkByName(name string) (*RdmaLink, error) { 143 links, err := h.RdmaLinkList() 144 if err != nil { 145 return nil, err 146 } 147 for _, link := range links { 148 if link.Attrs.Name == name { 149 return link, nil 150 } 151 } 152 return nil, fmt.Errorf("Rdma device %v not found", name) 153 } 154 155 // RdmaLinkSetName sets the name of the rdma link device. Return nil on success 156 // or error otherwise. 157 // Equivalent to: `rdma dev set $old_devname name $name` 158 func RdmaLinkSetName(link *RdmaLink, name string) error { 159 return pkgHandle.RdmaLinkSetName(link, name) 160 } 161 162 // RdmaLinkSetName sets the name of the rdma link device. Return nil on success 163 // or error otherwise. 164 // Equivalent to: `rdma dev set $old_devname name $name` 165 func (h *Handle) RdmaLinkSetName(link *RdmaLink, name string) error { 166 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 167 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 168 169 b := make([]byte, 4) 170 native.PutUint32(b, uint32(link.Attrs.Index)) 171 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b) 172 req.AddData(data) 173 174 b = make([]byte, len(name)+1) 175 copy(b, name) 176 data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, b) 177 req.AddData(data) 178 179 return execRdmaSetLink(req) 180 } 181 182 func netnsModeToString(mode uint8) string { 183 switch mode { 184 case 0: 185 return "exclusive" 186 case 1: 187 return "shared" 188 default: 189 return "unknown" 190 } 191 } 192 193 func executeOneGetRdmaNetnsMode(data []byte) (string, error) { 194 reader := bytes.NewReader(data) 195 for reader.Len() >= 4 { 196 _, attrType, len, value := parseNfAttrTLV(reader) 197 198 switch attrType { 199 case nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE: 200 var mode uint8 201 r := bytes.NewReader(value) 202 binary.Read(r, nl.NativeEndian(), &mode) 203 return netnsModeToString(mode), nil 204 } 205 if (len % 4) != 0 { 206 // Skip pad bytes 207 reader.Seek(int64(4-(len%4)), seekCurrent) 208 } 209 } 210 return "", fmt.Errorf("Invalid netns mode") 211 } 212 213 // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 214 // Returns mode string and error status as nil on success or returns error 215 // otherwise. 216 // Equivalent to: `rdma system show netns' 217 func RdmaSystemGetNetnsMode() (string, error) { 218 return pkgHandle.RdmaSystemGetNetnsMode() 219 } 220 221 // RdmaSystemGetNetnsMode gets the net namespace mode for RDMA subsystem 222 // Returns mode string and error status as nil on success or returns error 223 // otherwise. 224 // Equivalent to: `rdma system show netns' 225 func (h *Handle) RdmaSystemGetNetnsMode() (string, error) { 226 227 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_GET) 228 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 229 230 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 231 if err != nil { 232 return "", err 233 } 234 if len(msgs) == 0 { 235 return "", fmt.Errorf("No valid response from kernel") 236 } 237 return executeOneGetRdmaNetnsMode(msgs[0]) 238 } 239 240 func netnsModeStringToUint8(mode string) (uint8, error) { 241 switch mode { 242 case "exclusive": 243 return 0, nil 244 case "shared": 245 return 1, nil 246 default: 247 return 0, fmt.Errorf("Invalid mode; %q", mode) 248 } 249 } 250 251 // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 252 // Returns nil on success or appropriate error code. 253 // Equivalent to: `rdma system set netns { shared | exclusive }' 254 func RdmaSystemSetNetnsMode(NewMode string) error { 255 return pkgHandle.RdmaSystemSetNetnsMode(NewMode) 256 } 257 258 // RdmaSystemSetNetnsMode sets the net namespace mode for RDMA subsystem 259 // Returns nil on success or appropriate error code. 260 // Equivalent to: `rdma system set netns { shared | exclusive }' 261 func (h *Handle) RdmaSystemSetNetnsMode(NewMode string) error { 262 value, err := netnsModeStringToUint8(NewMode) 263 if err != nil { 264 return err 265 } 266 267 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SYS_SET) 268 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 269 270 data := nl.NewRtAttr(nl.RDMA_NLDEV_SYS_ATTR_NETNS_MODE, []byte{value}) 271 req.AddData(data) 272 273 _, err = req.Execute(unix.NETLINK_RDMA, 0) 274 return err 275 } 276 277 // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 278 // fd must be an open file descriptor to a network namespace. 279 // Similar to: `rdma dev set $dev netns $ns` 280 func RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 281 return pkgHandle.RdmaLinkSetNsFd(link, fd) 282 } 283 284 // RdmaLinkSetNsFd puts the RDMA device into a new network namespace. The 285 // fd must be an open file descriptor to a network namespace. 286 // Similar to: `rdma dev set $dev netns $ns` 287 func (h *Handle) RdmaLinkSetNsFd(link *RdmaLink, fd uint32) error { 288 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_SET) 289 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 290 291 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, 292 nl.Uint32Attr(link.Attrs.Index)) 293 req.AddData(data) 294 295 data = nl.NewRtAttr(nl.RDMA_NLDEV_NET_NS_FD, nl.Uint32Attr(fd)) 296 req.AddData(data) 297 298 return execRdmaSetLink(req) 299 } 300 301 // RdmaLinkDel deletes an rdma link 302 // 303 // Similar to: rdma link delete NAME 304 // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html 305 func RdmaLinkDel(name string) error { 306 return pkgHandle.RdmaLinkDel(name) 307 } 308 309 // RdmaLinkDel deletes an rdma link. 310 // 311 // If the returned error is [ErrDumpInterrupted], the caller should retry. 312 func (h *Handle) RdmaLinkDel(name string) error { 313 link, err := h.RdmaLinkByName(name) 314 if err != nil { 315 return err 316 } 317 318 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_DELLINK) 319 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 320 321 b := make([]byte, 4) 322 native.PutUint32(b, link.Attrs.Index) 323 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b)) 324 325 _, err = req.Execute(unix.NETLINK_RDMA, 0) 326 return err 327 } 328 329 // RdmaLinkAdd adds an rdma link for the specified type to the network device. 330 // Similar to: rdma link add NAME type TYPE netdev NETDEV 331 // 332 // NAME - specifies the new name of the rdma link to add 333 // TYPE - specifies which rdma type to use. Link types: 334 // rxe - Soft RoCE driver 335 // siw - Soft iWARP driver 336 // NETDEV - specifies the network device to which the link is bound 337 // 338 // REF: https://man7.org/linux/man-pages/man8/rdma-link.8.html 339 func RdmaLinkAdd(linkName, linkType, netdev string) error { 340 return pkgHandle.RdmaLinkAdd(linkName, linkType, netdev) 341 } 342 343 // RdmaLinkAdd adds an rdma link for the specified type to the network device. 344 func (h *Handle) RdmaLinkAdd(linkName string, linkType string, netdev string) error { 345 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_NEWLINK) 346 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK) 347 348 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_NAME, nl.ZeroTerminated(linkName))) 349 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_LINK_TYPE, nl.ZeroTerminated(linkType))) 350 req.AddData(nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_NDEV_NAME, nl.ZeroTerminated(netdev))) 351 _, err := req.Execute(unix.NETLINK_RDMA, 0) 352 return err 353 } 354 355 // RdmaResource represents a rdma device resource tracking summaries 356 type RdmaResource struct { 357 Index uint32 358 Name string 359 RdmaResourceSummaryEntries map[string]uint64 360 } 361 362 // RdmaResourceList list rdma resource tracking information 363 // Returns all rdma devices resource tracking summary on success or returns error 364 // otherwise. 365 // Equivalent to: `rdma resource' 366 func RdmaResourceList() ([]*RdmaResource, error) { 367 return pkgHandle.RdmaResourceList() 368 } 369 370 // RdmaResourceList list rdma resource tracking information 371 // Returns all rdma devices resource tracking summary on success or returns error 372 // otherwise. 373 // Equivalent to: `rdma resource' 374 func (h *Handle) RdmaResourceList() ([]*RdmaResource, error) { 375 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_RES_GET) 376 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_DUMP) 377 378 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 379 if err != nil { 380 return nil, err 381 } 382 if len(msgs) == 0 { 383 return nil, fmt.Errorf("No valid response from kernel") 384 } 385 var rdmaResources []*RdmaResource 386 for _, msg := range msgs { 387 res, err := executeOneGetRdmaResourceList(msg) 388 if err != nil { 389 return nil, err 390 } 391 rdmaResources = append(rdmaResources, res) 392 } 393 return rdmaResources, nil 394 } 395 396 func parseRdmaCounters(counterType uint16, data []byte) (map[string]uint64, error) { 397 var counterKeyType, counterValueType uint16 398 switch counterType { 399 case nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY: 400 counterKeyType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME 401 counterValueType = nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR 402 case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY: 403 counterKeyType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME 404 counterValueType = nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE 405 default: 406 return nil, fmt.Errorf("Invalid counter type: %d", counterType) 407 } 408 counters := make(map[string]uint64) 409 reader := bytes.NewReader(data) 410 411 for reader.Len() >= 4 { 412 _, attrType, _, value := parseNfAttrTLV(reader) 413 if attrType != counterType { 414 return nil, fmt.Errorf("Invalid resource summary entry type; %d", attrType) 415 } 416 417 summaryReader := bytes.NewReader(value) 418 for summaryReader.Len() >= 4 { 419 _, attrType, len, value := parseNfAttrTLV(summaryReader) 420 if attrType != counterKeyType { 421 return nil, fmt.Errorf("Invalid resource summary entry name type; %d", attrType) 422 } 423 name := string(value[0 : len-1]) 424 // Skip pad bytes 425 if (len % 4) != 0 { 426 summaryReader.Seek(int64(4-(len%4)), seekCurrent) 427 } 428 _, attrType, len, value = parseNfAttrTLV(summaryReader) 429 if attrType != counterValueType { 430 return nil, fmt.Errorf("Invalid resource summary entry value type; %d", attrType) 431 } 432 counters[name] = native.Uint64(value) 433 } 434 } 435 return counters, nil 436 } 437 438 func executeOneGetRdmaResourceList(data []byte) (*RdmaResource, error) { 439 var res RdmaResource 440 reader := bytes.NewReader(data) 441 for reader.Len() >= 4 { 442 _, attrType, len, value := parseNfAttrTLV(reader) 443 444 switch attrType { 445 case nl.RDMA_NLDEV_ATTR_DEV_INDEX: 446 var Index uint32 447 r := bytes.NewReader(value) 448 binary.Read(r, nl.NativeEndian(), &Index) 449 res.Index = Index 450 case nl.RDMA_NLDEV_ATTR_DEV_NAME: 451 res.Name = string(value[0 : len-1]) 452 case nl.RDMA_NLDEV_ATTR_RES_SUMMARY: 453 var err error 454 res.RdmaResourceSummaryEntries, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY, value) 455 if err != nil { 456 return nil, err 457 } 458 } 459 if (len % 4) != 0 { 460 // Skip pad bytes 461 reader.Seek(int64(4-(len%4)), seekCurrent) 462 } 463 } 464 return &res, nil 465 } 466 467 // RdmaPortStatistic represents a rdma port statistic counter 468 type RdmaPortStatistic struct { 469 PortIndex uint32 470 Statistics map[string]uint64 471 } 472 473 // RdmaDeviceStatistic represents a rdma device statistic counter 474 type RdmaDeviceStatistic struct { 475 RdmaPortStatistics []*RdmaPortStatistic 476 } 477 478 // RdmaStatistic get rdma device statistic counters 479 // Returns rdma device statistic counters on success or returns error 480 // otherwise. 481 // Equivalent to: `rdma statistic show link [DEV]' 482 func RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) { 483 return pkgHandle.RdmaStatistic(link) 484 } 485 486 // RdmaStatistic get rdma device statistic counters 487 // Returns rdma device statistic counters on success or returns error 488 // otherwise. 489 // Equivalent to: `rdma statistic show link [DEV]' 490 func (h *Handle) RdmaStatistic(link *RdmaLink) (*RdmaDeviceStatistic, error) { 491 rdmaLinkStatistic := make([]*RdmaPortStatistic, 0) 492 for portIndex := uint32(1); portIndex <= link.Attrs.NumPorts; portIndex++ { 493 portStatistic, err := h.RdmaPortStatisticList(link, portIndex) 494 if err != nil { 495 return nil, err 496 } 497 rdmaLinkStatistic = append(rdmaLinkStatistic, portStatistic) 498 } 499 return &RdmaDeviceStatistic{RdmaPortStatistics: rdmaLinkStatistic}, nil 500 } 501 502 // RdmaPortStatisticList get rdma device port statistic counters 503 // Returns rdma device port statistic counters on success or returns error 504 // otherwise. 505 // Equivalent to: `rdma statistic show link [DEV/PORT]' 506 func RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) { 507 return pkgHandle.RdmaPortStatisticList(link, port) 508 } 509 510 // RdmaPortStatisticList get rdma device port statistic counters 511 // Returns rdma device port statistic counters on success or returns error 512 // otherwise. 513 // Equivalent to: `rdma statistic show link [DEV/PORT]' 514 func (h *Handle) RdmaPortStatisticList(link *RdmaLink, port uint32) (*RdmaPortStatistic, error) { 515 proto := getProtoField(nl.RDMA_NL_NLDEV, nl.RDMA_NLDEV_CMD_STAT_GET) 516 req := h.newNetlinkRequest(proto, unix.NLM_F_ACK|unix.NLM_F_REQUEST) 517 b := make([]byte, 4) 518 native.PutUint32(b, link.Attrs.Index) 519 data := nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_DEV_INDEX, b) 520 req.AddData(data) 521 522 b = make([]byte, 4) 523 native.PutUint32(b, port) 524 data = nl.NewRtAttr(nl.RDMA_NLDEV_ATTR_PORT_INDEX, b) 525 req.AddData(data) 526 527 msgs, err := req.Execute(unix.NETLINK_RDMA, 0) 528 if err != nil { 529 return nil, err 530 } 531 if len(msgs) != 1 { 532 return nil, fmt.Errorf("No valid response from kernel") 533 } 534 return executeOneGetRdmaPortStatistics(msgs[0]) 535 } 536 537 func executeOneGetRdmaPortStatistics(data []byte) (*RdmaPortStatistic, error) { 538 var stat RdmaPortStatistic 539 reader := bytes.NewReader(data) 540 for reader.Len() >= 4 { 541 _, attrType, len, value := parseNfAttrTLV(reader) 542 543 switch attrType { 544 case nl.RDMA_NLDEV_ATTR_PORT_INDEX: 545 var Index uint32 546 r := bytes.NewReader(value) 547 binary.Read(r, nl.NativeEndian(), &Index) 548 stat.PortIndex = Index 549 case nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTERS: 550 var err error 551 stat.Statistics, err = parseRdmaCounters(nl.RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY, value) 552 if err != nil { 553 return nil, err 554 } 555 } 556 if (len % 4) != 0 { 557 // Skip pad bytes 558 reader.Seek(int64(4-(len%4)), seekCurrent) 559 } 560 } 561 return &stat, nil 562 }