cgrouputils.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package cgroupv1
  15. import (
  16. "bufio"
  17. "fmt"
  18. "io/ioutil"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "regexp"
  23. "strings"
  24. "yunion.io/x/log"
  25. "yunion.io/x/onecloud/pkg/util/cgrouputils/cgroup"
  26. "yunion.io/x/onecloud/pkg/util/fileutils2"
  27. "yunion.io/x/onecloud/pkg/util/procutils"
  28. )
  29. const (
  30. CGROUP_TASKS = "tasks"
  31. maxWeight = 16
  32. normalizeBase = 1024
  33. )
  34. type CGroupTask struct {
  35. pid string
  36. threadIds []string
  37. name string
  38. weight float64
  39. hand cgroup.ICGroupTask
  40. }
  41. func NewCGroupTask(pid, name string, cpuShares int, threadIds []string) *CGroupTask {
  42. return &CGroupTask{
  43. pid: pid,
  44. name: name,
  45. weight: float64(cpuShares) / normalizeBase,
  46. threadIds: threadIds,
  47. }
  48. }
  49. func (*CGroupTask) Init() bool {
  50. if !manager.CgroupIsMounted() {
  51. if !fileutils2.Exists(manager.GetCgroupPath()) {
  52. if err := procutils.NewCommand("mkdir", "-p", manager.GetCgroupPath()).Run(); err != nil {
  53. log.Errorf("mkdir -p %s error: %v", manager.GetCgroupPath(), err)
  54. }
  55. }
  56. if err := procutils.NewCommand("mount", "-t", "tmpfs", "-o", "uid=0,gid=0,mode=0755",
  57. "cgroup", manager.GetCgroupPath()).Run(); err != nil {
  58. log.Errorf("mount cgroups path %s, error: %v", manager.GetCgroupPath(), err)
  59. return false
  60. }
  61. }
  62. file, err := os.Open("/proc/cgroups")
  63. if err != nil {
  64. log.Errorln(err)
  65. return false
  66. }
  67. defer file.Close()
  68. re := regexp.MustCompile(`\s+`)
  69. scanner := bufio.NewScanner(file)
  70. for scanner.Scan() {
  71. // #subsys_name hierarchy num_cgroups enabled
  72. line := scanner.Text()
  73. if line[0] != '#' {
  74. parts := re.Split(line, -1)
  75. module := parts[0]
  76. if parts[3] == "0" { // disabled
  77. continue
  78. }
  79. if !manager.ModuleIsMounted(module) {
  80. moduleDir := path.Join(manager.GetCgroupPath(), module)
  81. if !fileutils2.Exists(moduleDir) {
  82. if output, err := procutils.NewCommand("mkdir", moduleDir).Output(); err != nil {
  83. log.Errorf("mkdir %s failed: %s, %s", moduleDir, err, output)
  84. return false
  85. }
  86. }
  87. if err := procutils.NewCommand("mount", "-t", "cgroup", "-o",
  88. module, module, moduleDir).Run(); err != nil {
  89. log.Errorf("mount cgroup module %s to %s error: %v", module, moduleDir, err)
  90. return false
  91. }
  92. }
  93. }
  94. }
  95. if err := scanner.Err(); err != nil {
  96. log.Errorf("scan file %s error: %v", file.Name(), err)
  97. return false
  98. }
  99. return true
  100. }
  101. func (c *CGroupTask) CustomConfig(key, value string) bool {
  102. configPath := GetTaskParamPath(c.hand.Module(), key, c.GroupName())
  103. if !fileutils2.Exists(configPath) {
  104. return true
  105. }
  106. return SetRootParam(c.hand.Module(), key, value, c.GroupName())
  107. }
  108. func (c *CGroupTask) SetWeight(coreNum int) {
  109. c.weight = float64(coreNum) / normalizeBase
  110. }
  111. func (c *CGroupTask) SetHand(hand cgroup.ICGroupTask) {
  112. c.hand = hand
  113. }
  114. func (c *CGroupTask) SetPid(pid string) {
  115. c.pid = pid
  116. }
  117. func (c *CGroupTask) SetName(name string) {
  118. c.name = name
  119. }
  120. func (c *CGroupTask) GroupName() string {
  121. if len(c.name) > 0 {
  122. return c.name
  123. }
  124. return c.pid
  125. }
  126. func (c *CGroupTask) InitTask(hand cgroup.ICGroupTask, coreNum int, pid, name string) {
  127. c.SetHand(hand)
  128. c.SetWeight(coreNum)
  129. c.SetPid(pid)
  130. c.SetName(name)
  131. }
  132. func (c *CGroupTask) Module() string {
  133. return ""
  134. }
  135. func (c *CGroupTask) GetWeight() float64 {
  136. if c.weight < maxWeight {
  137. return c.weight
  138. } else {
  139. return maxWeight
  140. }
  141. }
  142. func (c *CGroupTask) GetTaskIds() []string {
  143. if len(c.pid) == 0 {
  144. return nil
  145. }
  146. if len(c.threadIds) > 0 {
  147. return c.threadIds
  148. }
  149. files, err := ioutil.ReadDir(fmt.Sprintf("/proc/%s/task", c.pid))
  150. if err != nil {
  151. log.Errorf("GetTaskIds failed: %s", err)
  152. return nil
  153. }
  154. ids := []string{}
  155. for _, file := range files {
  156. ids = append(ids, file.Name())
  157. }
  158. return ids
  159. }
  160. func (c *CGroupTask) TaskPath() string {
  161. return path.Join(RootTaskPath(c.hand.Module()), c.GroupName())
  162. }
  163. func (c *CGroupTask) TaskIsExist() bool {
  164. return fileutils2.Exists(c.TaskPath())
  165. }
  166. func (c *CGroupTask) createTask() bool {
  167. if err := os.Mkdir(c.TaskPath(), os.ModePerm); err != nil {
  168. log.Errorln(err)
  169. return false
  170. }
  171. return true
  172. }
  173. func (c *CGroupTask) GetParam(name string) string {
  174. return GetRootParam(c.hand.Module(), name, c.GroupName())
  175. }
  176. func (c *CGroupTask) MoveTasksToRoot() {
  177. procs := c.GetParam(CGROUP_TASKS)
  178. if len(procs) > 0 {
  179. for _, proc := range strings.Split(procs, "\n") {
  180. proc = strings.TrimSpace(proc)
  181. if len(proc) > 0 {
  182. c.PushPid(proc, true)
  183. }
  184. }
  185. }
  186. }
  187. func (c *CGroupTask) RemoveTask() bool {
  188. if c.TaskIsExist() {
  189. c.MoveTasksToRoot()
  190. log.Infof("Remove task path %s %s", c.TaskPath(), c.name)
  191. if err := os.Remove(c.TaskPath()); err != nil {
  192. log.Errorf("Remove task path failed %s", err)
  193. return false
  194. }
  195. }
  196. return true
  197. }
  198. func (c *CGroupTask) GetStaticConfig() map[string]string {
  199. return nil
  200. }
  201. func (c *CGroupTask) GetConfig() map[string]string {
  202. return nil
  203. }
  204. func (c *CGroupTask) SetParam(name, value string) bool {
  205. return SetRootParam(c.hand.Module(), name, value, c.GroupName())
  206. }
  207. func (c *CGroupTask) SetParams(conf map[string]string) bool {
  208. for k, v := range conf {
  209. if !c.SetParam(k, v) {
  210. log.Errorf("Fail to set %s/%s=%s for %s", c.hand.Module(), k, v, c.GroupName())
  211. return false
  212. }
  213. }
  214. return true
  215. }
  216. func (c *CGroupTask) Configure() bool {
  217. if !c.TaskIsExist() {
  218. if !c.createTask() {
  219. return false
  220. }
  221. }
  222. conf := c.hand.GetStaticConfig()
  223. if !c.SetParams(conf) {
  224. return false
  225. }
  226. conf = c.hand.GetConfig()
  227. return c.SetParams(conf)
  228. }
  229. func (c *CGroupTask) SetTask() bool {
  230. if !c.TaskIsExist() {
  231. if !c.createTask() {
  232. return false
  233. }
  234. }
  235. conf := c.hand.GetStaticConfig()
  236. if !c.SetParams(conf) {
  237. return false
  238. }
  239. conf = c.hand.GetConfig()
  240. if c.SetParams(conf) {
  241. pids := c.GetTaskIds()
  242. if len(pids) > 0 {
  243. for _, pid := range pids {
  244. c.PushPid(pid, false)
  245. }
  246. return true
  247. }
  248. }
  249. return false
  250. }
  251. func (c *CGroupTask) PushPid(tid string, isRoot bool) {
  252. if c.pid == "" {
  253. return
  254. }
  255. subdir := fmt.Sprintf("/proc/%s/task/%s", c.pid, tid)
  256. if fi, err := os.Stat(subdir); err != nil {
  257. log.Errorf("Fail to put pid in task %s", err)
  258. return
  259. } else if fi.Mode().IsDir() {
  260. stat, err := fileutils2.FileGetContents(path.Join(subdir, "stat"))
  261. if err != nil {
  262. log.Errorf("Fail to put pid in task %s", err)
  263. return
  264. }
  265. re := regexp.MustCompile(`\s+`)
  266. data := re.Split(stat, -1)
  267. if data[2] != "Z" {
  268. if isRoot {
  269. SetRootParam(c.hand.Module(), CGROUP_TASKS, tid, "")
  270. } else {
  271. c.SetParam(CGROUP_TASKS, tid)
  272. }
  273. }
  274. }
  275. }
  276. /**
  277. * CGroupCPUTask
  278. */
  279. type CGroupCPUTask struct {
  280. *CGroupTask
  281. }
  282. const (
  283. CgroupsSharesWeight = 1024
  284. CPU_SHARES = "cpu.shares"
  285. )
  286. func (c *CGroupCPUTask) Module() string {
  287. return "cpu"
  288. }
  289. func (c *CGroupCPUTask) GetConfig() map[string]string {
  290. wt := int(CgroupsSharesWeight * c.GetWeight())
  291. return map[string]string{CPU_SHARES: fmt.Sprintf("%d", wt)}
  292. }
  293. func (c *CGroupCPUTask) Init() bool {
  294. return SetRootParam(c.Module(), CPU_SHARES,
  295. fmt.Sprintf("%d", CgroupsSharesWeight), "")
  296. }
  297. func (m *cgroupManager) NewCGroupCPUTask(pid, name string, cpuShares int) cgroup.ICGroupTask {
  298. t := &CGroupCPUTask{NewCGroupTask(pid, name, cpuShares, nil)}
  299. t.SetHand(t)
  300. return t
  301. }
  302. /**
  303. * CGroupIOTask
  304. */
  305. type CGroupIOTask struct {
  306. *CGroupTask
  307. }
  308. const (
  309. IoWeightBase = 100
  310. IoWeightMax = 1000
  311. IoWeightMin = 100
  312. BLOCK_IO_WEIGHT = "blkio.weight"
  313. BLOCK_IO_BFQ_WEIGHT = "blkio.bfq.weight"
  314. IOSCHED_CFQ = "cfq"
  315. IOSCHED_BFQ = "bfq"
  316. )
  317. func (c *CGroupIOTask) Module() string {
  318. return "blkio"
  319. }
  320. func (c *CGroupIOTask) GetConfig() map[string]string {
  321. wt := int(c.GetWeight() * IoWeightBase)
  322. if wt > IoWeightMax {
  323. wt = IoWeightMax
  324. } else if wt < IoWeightMin {
  325. wt = IoWeightMin
  326. }
  327. switch manager.GetIoScheduler() {
  328. case IOSCHED_CFQ:
  329. return map[string]string{BLOCK_IO_WEIGHT: fmt.Sprintf("%d", wt)}
  330. case IOSCHED_BFQ:
  331. return map[string]string{BLOCK_IO_BFQ_WEIGHT: fmt.Sprintf("%d", wt)}
  332. default:
  333. return nil
  334. }
  335. }
  336. func (c *CGroupIOTask) Init() bool {
  337. switch manager.GetIoScheduler() {
  338. case IOSCHED_CFQ:
  339. return SetRootParam(c.Module(), BLOCK_IO_WEIGHT, fmt.Sprintf("%d", IoWeightMax), "")
  340. default:
  341. return true
  342. }
  343. }
  344. func (m *cgroupManager) NewCGroupIOTask(pid, name string, cpuShares int) cgroup.ICGroupTask {
  345. task := &CGroupIOTask{NewCGroupTask(pid, name, cpuShares, nil)}
  346. task.SetHand(task)
  347. return task
  348. }
  349. /**
  350. * CGroupIOHardlimitTask
  351. */
  352. type CGroupIOHardlimitTask struct {
  353. *CGroupIOTask
  354. cpuNum int
  355. params map[string]int
  356. devId string
  357. }
  358. func (c *CGroupIOHardlimitTask) GetConfig() map[string]string {
  359. config := make(map[string]string, 0)
  360. for k, v := range c.params {
  361. if v != 0 {
  362. config[k] = fmt.Sprintf("%s %d", c.devId, v*c.cpuNum)
  363. }
  364. }
  365. return config
  366. }
  367. func (m *cgroupManager) NewCGroupIOHardlimitTask(pid, name string, coreNum int, params map[string]int, devId string) cgroup.ICGroupTask {
  368. task := &CGroupIOHardlimitTask{
  369. CGroupIOTask: m.NewCGroupIOTask(pid, name, 0).(*CGroupIOTask),
  370. cpuNum: coreNum,
  371. params: params,
  372. devId: devId,
  373. }
  374. task.SetHand(task)
  375. return task
  376. }
  377. /**
  378. * CGroupMemoryTask
  379. */
  380. type CGroupMemoryTask struct {
  381. *CGroupTask
  382. }
  383. const (
  384. root_swappiness = 60
  385. vm_swappiness = 0
  386. MEMORY_SWAPPINESS = "memory.swappiness"
  387. )
  388. func (c *CGroupMemoryTask) Module() string {
  389. return "memory"
  390. }
  391. func (c *CGroupMemoryTask) GetConfig() map[string]string {
  392. return map[string]string{MEMORY_SWAPPINESS: fmt.Sprintf("%d", vm_swappiness)}
  393. }
  394. func (m *cgroupManager) NewCGroupMemoryTask(pid, name string, coreNum int) cgroup.ICGroupTask {
  395. task := &CGroupMemoryTask{
  396. CGroupTask: NewCGroupTask(pid, name, coreNum, nil),
  397. }
  398. task.SetHand(task)
  399. return task
  400. }
  401. /**
  402. * CGroupCPUSetTask
  403. */
  404. type CGroupCPUSetTask struct {
  405. *CGroupTask
  406. cpuset string
  407. mems string
  408. }
  409. const (
  410. CPUSET_CPUS = "cpuset.cpus"
  411. CPUSET_MEMS = "cpuset.mems"
  412. )
  413. func (c *CGroupCPUSetTask) Module() string {
  414. return "cpuset"
  415. }
  416. func (c *CGroupCPUSetTask) GetConfig() map[string]string {
  417. if c.cpuset == "" {
  418. parentPath := filepath.Dir(c.GroupName())
  419. c.cpuset = GetRootParam(c.Module(), CPUSET_CPUS, parentPath)
  420. }
  421. if c.mems == "" {
  422. parentPath := filepath.Dir(c.GroupName())
  423. c.mems = GetRootParam(c.Module(), CPUSET_MEMS, parentPath)
  424. }
  425. return map[string]string{CPUSET_CPUS: c.cpuset, CPUSET_MEMS: c.mems}
  426. }
  427. func (m *cgroupManager) NewCGroupCPUSetTask(pid, name, cpuset, mems string) cgroup.ICGroupTask {
  428. task := &CGroupCPUSetTask{
  429. CGroupTask: NewCGroupTask(pid, name, 0, nil),
  430. cpuset: cpuset,
  431. mems: mems,
  432. }
  433. task.SetHand(task)
  434. return task
  435. }
  436. func (m *cgroupManager) NewCGroupSubCPUSetTask(pid, name string, cpuset string, threadIds []string) cgroup.ICGroupTask {
  437. task := &CGroupCPUSetTask{
  438. CGroupTask: NewCGroupTask(pid, name, 0, threadIds),
  439. cpuset: cpuset,
  440. }
  441. task.SetHand(task)
  442. return task
  443. }