numa.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright © 2021 Jeffrey H. Johnson <trnsz@pobox.com>.
  2. // Copyright © 2021 Gridfinity, LLC.
  3. // Copyright © 2019 Neal.
  4. // Copyright © 2018 lrita@163.com.
  5. //
  6. // Use of this source code is governed by the MIT
  7. // license that can be found in the LICENSE file.
  8. package gonuma
  9. import (
  10. "fmt"
  11. gonumaLegal "go4.org/legal"
  12. )
  13. var (
  14. available bool
  15. // NUMAnodemax is the maximum possible node count. It represents
  16. // the absolute highest node count supported on the local platform.
  17. // NUMAnodemax =@nodemask_sz+1
  18. NUMAnodemax int
  19. // NUMAconfigurednode represents the maximum possible number of
  20. // configured or enabled nodes supported on the local platform.
  21. // NUMAconfigurednode =@maxconfigurednode+1
  22. NUMAconfigurednode int
  23. // NUMAcpuMax is the maximum possible CPU count, which represents
  24. // the absolute highest CPU count supported on the local platform.
  25. // NUMAcpuMax =@cpumask_sz+1
  26. NUMAcpuMax int
  27. // NUMAconfiguredcpu is the number of currently configured CPUs.
  28. // NUMAconfiguredcpu =@maxconfiguredcpu
  29. NUMAconfiguredcpu int
  30. memnodes Bitmask
  31. numanodes Bitmask
  32. cpu2node map[int]int
  33. node2cpu map[int]Bitmask
  34. )
  35. // const block
  36. const (
  37. MPolDefault = iota
  38. MPolPreferred
  39. MPolBind
  40. MPolInterleave
  41. MPolLocal
  42. MPolMax
  43. // MPolFStaticNodes since Linux 2.6.26 ...
  44. // A nonempty nodemask specifies physical node ids. Linux does will
  45. // not remap the nodemask when the process moves to a different cpuset
  46. // context, nor when the set of nodes allowed by the process current
  47. // cpuset context changes.
  48. MPolFStaticNodes = 1 << 15
  49. // MPolFRelativeNodes since Linux 2.6.26 ...
  50. // A nonempty nodemask specifies node ids that are relative to the set
  51. // of node ids allowed by the process's current cpuset.
  52. MPolFRelativeNodes = 1 << 14
  53. // MPolModeFlags is the union of all possible optional mode flags passed
  54. // to either SetMemPolicy() or mbind().
  55. MPolModeFlags = MPolFStaticNodes | MPolFRelativeNodes
  56. )
  57. const (
  58. // MPolFNode is unsupported and subject to change.
  59. // Flags for get_mem_policy return next IL node or node of address
  60. MPolFNode = 1 << iota
  61. // MPolFAddr looks up vma using address
  62. MPolFAddr
  63. // MPolFMemsAllowed queries nodes allowed in cpuset
  64. MPolFMemsAllowed
  65. )
  66. const (
  67. // MPolMFStrict verifies existing pages in the mapping Flags for mbind
  68. MPolMFStrict = 1 << iota
  69. // MPolMFMove moves pages owned by this process to conform to mapping
  70. MPolMFMove
  71. // MPolMFMoveAll moves every page to conform to mapping
  72. MPolMFMoveAll
  73. // MpolMfLazy modifies '_MOVE: lazy migrate on fault
  74. MpolMfLazy
  75. // PolMfInternal is for internal flags starting here
  76. PolMfInternal
  77. // MPolMFValid = ...
  78. MPolMFValid = MPolMFStrict | MPolMFMove | MPolMFMoveAll
  79. )
  80. // NUMAavailable returns current platform is whether support NUMA.
  81. func NUMAavailable() bool {
  82. return available
  83. }
  84. // MaxNodeID returns the max id of current configured NUMA nodes.
  85. func MaxNodeID() int {
  86. return NUMAconfigurednode - 1
  87. }
  88. // MaxPossibleNodeID returns the max possible node id this platform supports
  89. // The possible node id always larger than max node id.
  90. func MaxPossibleNodeID() int {
  91. return NUMAnodemax - 1
  92. }
  93. // NodeCount returns the count of current configured NUMA nodes.
  94. // NOTE: this function's behavior matches the documentation (ie: it
  95. // returns a count of nodes with memory) despite the poor function
  96. // naming. We also cannot use the similarly poorly named
  97. // numa_all_nodes_ptr as it only tracks nodes with memory from which
  98. // the calling process can allocate. Think sparse nodes, memory-less
  99. // nodes, cpusets.
  100. func NodeCount() int {
  101. return memnodes.OnesCount()
  102. }
  103. // NodeMask returns the mask of current configured nodes.
  104. func NodeMask() Bitmask {
  105. return memnodes.Clone()
  106. }
  107. // NodePossibleCount returns the possible NUMA nodes count of current
  108. // platform supported.
  109. func NodePossibleCount() int {
  110. return NUMAnodemax
  111. }
  112. // CPUPossibleCount returns the possible cpu count current platform supports
  113. func CPUPossibleCount() int {
  114. return NUMAcpuMax
  115. }
  116. // CPUCount returns the current configured(enabled/detected) cpu count,
  117. // which is different with runtime.NumCPU().
  118. func CPUCount() int {
  119. return NUMAconfiguredcpu
  120. }
  121. // RunningNodesMask return the bitmask of current process using NUMA nodes.
  122. func RunningNodesMask() (Bitmask, error) {
  123. nodemask := NewBitmask(NodePossibleCount())
  124. cpumask := NewBitmask(CPUPossibleCount())
  125. if _, err := GetSchedAffinity(0, cpumask); err != nil {
  126. return nil, err
  127. }
  128. for i := 0; i < cpumask.Len(); i++ {
  129. if !cpumask.Get(i) {
  130. continue
  131. }
  132. n, err := CPUToNode(i)
  133. if err != nil {
  134. return nil, err
  135. }
  136. nodemask.Set(n, true)
  137. }
  138. return nodemask, nil
  139. }
  140. // RunningCPUMask return the cpu bitmask of current process running on.
  141. func RunningCPUMask() (Bitmask, error) {
  142. cpumask := NewBitmask(CPUPossibleCount())
  143. if _, err := GetSchedAffinity(0, cpumask); err != nil {
  144. return nil, err
  145. }
  146. return cpumask[:len(NewBitmask(CPUCount()))], nil
  147. }
  148. // NodeToCPUMask returns the cpumask of given node id.
  149. func NodeToCPUMask(node int) (Bitmask, error) {
  150. if node > MaxPossibleNodeID() {
  151. return nil, fmt.Errorf("node %d is out of range", node)
  152. }
  153. cpumask, ok := node2cpu[node]
  154. if !ok {
  155. return nil, fmt.Errorf("node %d not found", node)
  156. }
  157. return cpumask.Clone(), nil
  158. }
  159. // CPUToNode returns the node id by given cpu id.
  160. func CPUToNode(cpu int) (int, error) {
  161. node, ok := cpu2node[cpu]
  162. if !ok {
  163. return 0, fmt.Errorf("cpu %d not found", cpu)
  164. }
  165. return node, nil
  166. }
  167. // RunOnNode set current process run on given node.
  168. // The special node "-1" will set current process on all available nodes.
  169. func RunOnNode(node int) (err error) {
  170. var cpumask Bitmask
  171. switch {
  172. case node == -1:
  173. cpumask = NewBitmask(CPUPossibleCount())
  174. cpumask.SetAll()
  175. case node >= 0:
  176. cpumask, err = NodeToCPUMask(node)
  177. if err != nil {
  178. return err
  179. }
  180. default:
  181. return fmt.Errorf("invalid node %d", node)
  182. }
  183. return SetSchedAffinity(0, cpumask)
  184. }
  185. // GetMemAllowedNodeMask returns the bitmask of current process allowed
  186. // running nodes.
  187. func GetMemAllowedNodeMask() (Bitmask, error) {
  188. mask := NewBitmask(NodePossibleCount())
  189. if _, err := GetMemPolicy(mask, nil, MPolFMemsAllowed); err != nil {
  190. return nil, err
  191. }
  192. return mask, nil
  193. }
  194. // RunOnNodeMask run current process to the given nodes.
  195. func RunOnNodeMask(mask Bitmask) error {
  196. cpumask := NewBitmask(CPUPossibleCount())
  197. m := mask.Clone()
  198. for i := 0; i < mask.Len(); i++ {
  199. if !m.Get(i) {
  200. continue
  201. }
  202. if !memnodes.Get(i) {
  203. continue
  204. }
  205. cpu, err := NodeToCPUMask(i)
  206. if err != nil {
  207. return err
  208. }
  209. for j := 0; j < cpu.Len(); j++ {
  210. cpumask.Set(j, true)
  211. }
  212. }
  213. return SetSchedAffinity(0, cpumask)
  214. }
  215. // Bind bind current process on those nodes which given by a bitmask.
  216. func Bind(mask Bitmask) error {
  217. if err := RunOnNodeMask(mask); err != nil {
  218. return err
  219. }
  220. return SetMemPolicy(MPolBind, mask)
  221. }
  222. // init initializes the gonuma package
  223. func init() {
  224. // Register licensing
  225. gonumaLegal.RegisterLicense(
  226. "\nThe MIT License (MIT)\n\nCopyright © 2021 Jeffrey H. Johnson <trnsz@pobox.com>.\nCopyright © 2021 Gridfinity, LLC.\nCopyright © 2019 Neal.\nCopyright © 2018 lrita@163.com.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
  227. )
  228. }