rdma.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package fscommon
  2. import (
  3. "bufio"
  4. "errors"
  5. "math"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "github.com/opencontainers/runc/libcontainer/cgroups"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. "golang.org/x/sys/unix"
  12. )
  13. // parseRdmaKV parses raw string to RdmaEntry.
  14. func parseRdmaKV(raw string, entry *cgroups.RdmaEntry) error {
  15. var value uint32
  16. parts := strings.SplitN(raw, "=", 3)
  17. if len(parts) != 2 {
  18. return errors.New("Unable to parse RDMA entry")
  19. }
  20. k, v := parts[0], parts[1]
  21. if v == "max" {
  22. value = math.MaxUint32
  23. } else {
  24. val64, err := strconv.ParseUint(v, 10, 32)
  25. if err != nil {
  26. return err
  27. }
  28. value = uint32(val64)
  29. }
  30. if k == "hca_handle" {
  31. entry.HcaHandles = value
  32. } else if k == "hca_object" {
  33. entry.HcaObjects = value
  34. }
  35. return nil
  36. }
  37. // readRdmaEntries reads and converts array of rawstrings to RdmaEntries from file.
  38. // example entry: mlx4_0 hca_handle=2 hca_object=2000
  39. func readRdmaEntries(dir, file string) ([]cgroups.RdmaEntry, error) {
  40. rdmaEntries := make([]cgroups.RdmaEntry, 0)
  41. fd, err := cgroups.OpenFile(dir, file, unix.O_RDONLY)
  42. if err != nil {
  43. return nil, err
  44. }
  45. defer fd.Close() //nolint:errorlint
  46. scanner := bufio.NewScanner(fd)
  47. for scanner.Scan() {
  48. parts := strings.SplitN(scanner.Text(), " ", 4)
  49. if len(parts) == 3 {
  50. entry := new(cgroups.RdmaEntry)
  51. entry.Device = parts[0]
  52. err = parseRdmaKV(parts[1], entry)
  53. if err != nil {
  54. continue
  55. }
  56. err = parseRdmaKV(parts[2], entry)
  57. if err != nil {
  58. continue
  59. }
  60. rdmaEntries = append(rdmaEntries, *entry)
  61. }
  62. }
  63. return rdmaEntries, scanner.Err()
  64. }
  65. // RdmaGetStats returns rdma stats such as totalLimit and current entries.
  66. func RdmaGetStats(path string, stats *cgroups.Stats) error {
  67. currentEntries, err := readRdmaEntries(path, "rdma.current")
  68. if err != nil {
  69. if errors.Is(err, os.ErrNotExist) {
  70. err = nil
  71. }
  72. return err
  73. }
  74. maxEntries, err := readRdmaEntries(path, "rdma.max")
  75. if err != nil {
  76. return err
  77. }
  78. // If device got removed between reading two files, ignore returning stats.
  79. if len(currentEntries) != len(maxEntries) {
  80. return nil
  81. }
  82. stats.RdmaStats = cgroups.RdmaStats{
  83. RdmaLimit: maxEntries,
  84. RdmaCurrent: currentEntries,
  85. }
  86. return nil
  87. }
  88. func createCmdString(device string, limits configs.LinuxRdma) string {
  89. cmdString := device
  90. if limits.HcaHandles != nil {
  91. cmdString += " hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10)
  92. }
  93. if limits.HcaObjects != nil {
  94. cmdString += " hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10)
  95. }
  96. return cmdString
  97. }
  98. // RdmaSet sets RDMA resources.
  99. func RdmaSet(path string, r *configs.Resources) error {
  100. for device, limits := range r.Rdma {
  101. if err := cgroups.WriteFile(path, "rdma.max", createCmdString(device, limits)); err != nil {
  102. return err
  103. }
  104. }
  105. return nil
  106. }