rdma_link_linux.go 17 KB

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