cgroup_manager_linux.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cm
  14. import (
  15. "fmt"
  16. "os"
  17. "path"
  18. "path/filepath"
  19. "strings"
  20. "time"
  21. libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
  22. cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
  23. cgroupsystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
  24. libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
  25. "k8s.io/klog"
  26. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  27. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  28. "k8s.io/apimachinery/pkg/util/sets"
  29. utilfeature "k8s.io/apiserver/pkg/util/feature"
  30. kubefeatures "k8s.io/kubernetes/pkg/features"
  31. "k8s.io/kubernetes/pkg/kubelet/metrics"
  32. )
  33. // libcontainerCgroupManagerType defines how to interface with libcontainer
  34. type libcontainerCgroupManagerType string
  35. const (
  36. // libcontainerCgroupfs means use libcontainer with cgroupfs
  37. libcontainerCgroupfs libcontainerCgroupManagerType = "cgroupfs"
  38. // libcontainerSystemd means use libcontainer with systemd
  39. libcontainerSystemd libcontainerCgroupManagerType = "systemd"
  40. // systemdSuffix is the cgroup name suffix for systemd
  41. systemdSuffix string = ".slice"
  42. )
  43. var RootCgroupName = CgroupName([]string{})
  44. // NewCgroupName composes a new cgroup name.
  45. // Use RootCgroupName as base to start at the root.
  46. // This function does some basic check for invalid characters at the name.
  47. func NewCgroupName(base CgroupName, components ...string) CgroupName {
  48. for _, component := range components {
  49. // Forbit using "_" in internal names. When remapping internal
  50. // names to systemd cgroup driver, we want to remap "-" => "_",
  51. // so we forbid "_" so that we can always reverse the mapping.
  52. if strings.Contains(component, "/") || strings.Contains(component, "_") {
  53. panic(fmt.Errorf("invalid character in component [%q] of CgroupName", component))
  54. }
  55. }
  56. // copy data from the base cgroup to eliminate cases where CgroupNames share underlying slices. See #68416
  57. baseCopy := make([]string, len(base))
  58. copy(baseCopy, base)
  59. return CgroupName(append(baseCopy, components...))
  60. }
  61. func escapeSystemdCgroupName(part string) string {
  62. return strings.Replace(part, "-", "_", -1)
  63. }
  64. func unescapeSystemdCgroupName(part string) string {
  65. return strings.Replace(part, "_", "-", -1)
  66. }
  67. // cgroupName.ToSystemd converts the internal cgroup name to a systemd name.
  68. // For example, the name {"kubepods", "burstable", "pod1234-abcd-5678-efgh"} becomes
  69. // "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1234_abcd_5678_efgh.slice"
  70. // This function always expands the systemd name into the cgroupfs form. If only
  71. // the last part is needed, use path.Base(...) on it to discard the rest.
  72. func (cgroupName CgroupName) ToSystemd() string {
  73. if len(cgroupName) == 0 || (len(cgroupName) == 1 && cgroupName[0] == "") {
  74. return "/"
  75. }
  76. newparts := []string{}
  77. for _, part := range cgroupName {
  78. part = escapeSystemdCgroupName(part)
  79. newparts = append(newparts, part)
  80. }
  81. result, err := cgroupsystemd.ExpandSlice(strings.Join(newparts, "-") + systemdSuffix)
  82. if err != nil {
  83. // Should never happen...
  84. panic(fmt.Errorf("error converting cgroup name [%v] to systemd format: %v", cgroupName, err))
  85. }
  86. return result
  87. }
  88. func ParseSystemdToCgroupName(name string) CgroupName {
  89. driverName := path.Base(name)
  90. driverName = strings.TrimSuffix(driverName, systemdSuffix)
  91. parts := strings.Split(driverName, "-")
  92. result := []string{}
  93. for _, part := range parts {
  94. result = append(result, unescapeSystemdCgroupName(part))
  95. }
  96. return CgroupName(result)
  97. }
  98. func (cgroupName CgroupName) ToCgroupfs() string {
  99. return "/" + path.Join(cgroupName...)
  100. }
  101. func ParseCgroupfsToCgroupName(name string) CgroupName {
  102. components := strings.Split(strings.TrimPrefix(name, "/"), "/")
  103. if len(components) == 1 && components[0] == "" {
  104. components = []string{}
  105. }
  106. return CgroupName(components)
  107. }
  108. func IsSystemdStyleName(name string) bool {
  109. return strings.HasSuffix(name, systemdSuffix)
  110. }
  111. // libcontainerAdapter provides a simplified interface to libcontainer based on libcontainer type.
  112. type libcontainerAdapter struct {
  113. // cgroupManagerType defines how to interface with libcontainer
  114. cgroupManagerType libcontainerCgroupManagerType
  115. }
  116. // newLibcontainerAdapter returns a configured libcontainerAdapter for specified manager.
  117. // it does any initialization required by that manager to function.
  118. func newLibcontainerAdapter(cgroupManagerType libcontainerCgroupManagerType) *libcontainerAdapter {
  119. return &libcontainerAdapter{cgroupManagerType: cgroupManagerType}
  120. }
  121. // newManager returns an implementation of cgroups.Manager
  122. func (l *libcontainerAdapter) newManager(cgroups *libcontainerconfigs.Cgroup, paths map[string]string) (libcontainercgroups.Manager, error) {
  123. switch l.cgroupManagerType {
  124. case libcontainerCgroupfs:
  125. return &cgroupfs.Manager{
  126. Cgroups: cgroups,
  127. Paths: paths,
  128. }, nil
  129. case libcontainerSystemd:
  130. // this means you asked systemd to manage cgroups, but systemd was not on the host, so all you can do is panic...
  131. if !cgroupsystemd.UseSystemd() {
  132. panic("systemd cgroup manager not available")
  133. }
  134. return &cgroupsystemd.LegacyManager{
  135. Cgroups: cgroups,
  136. Paths: paths,
  137. }, nil
  138. }
  139. return nil, fmt.Errorf("invalid cgroup manager configuration")
  140. }
  141. // CgroupSubsystems holds information about the mounted cgroup subsystems
  142. type CgroupSubsystems struct {
  143. // Cgroup subsystem mounts.
  144. // e.g.: "/sys/fs/cgroup/cpu" -> ["cpu", "cpuacct"]
  145. Mounts []libcontainercgroups.Mount
  146. // Cgroup subsystem to their mount location.
  147. // e.g.: "cpu" -> "/sys/fs/cgroup/cpu"
  148. MountPoints map[string]string
  149. }
  150. // cgroupManagerImpl implements the CgroupManager interface.
  151. // Its a stateless object which can be used to
  152. // update,create or delete any number of cgroups
  153. // It uses the Libcontainer raw fs cgroup manager for cgroup management.
  154. type cgroupManagerImpl struct {
  155. // subsystems holds information about all the
  156. // mounted cgroup subsystems on the node
  157. subsystems *CgroupSubsystems
  158. // simplifies interaction with libcontainer and its cgroup managers
  159. adapter *libcontainerAdapter
  160. }
  161. // Make sure that cgroupManagerImpl implements the CgroupManager interface
  162. var _ CgroupManager = &cgroupManagerImpl{}
  163. // NewCgroupManager is a factory method that returns a CgroupManager
  164. func NewCgroupManager(cs *CgroupSubsystems, cgroupDriver string) CgroupManager {
  165. managerType := libcontainerCgroupfs
  166. if cgroupDriver == string(libcontainerSystemd) {
  167. managerType = libcontainerSystemd
  168. }
  169. return &cgroupManagerImpl{
  170. subsystems: cs,
  171. adapter: newLibcontainerAdapter(managerType),
  172. }
  173. }
  174. // Name converts the cgroup to the driver specific value in cgroupfs form.
  175. // This always returns a valid cgroupfs path even when systemd driver is in use!
  176. func (m *cgroupManagerImpl) Name(name CgroupName) string {
  177. if m.adapter.cgroupManagerType == libcontainerSystemd {
  178. return name.ToSystemd()
  179. }
  180. return name.ToCgroupfs()
  181. }
  182. // CgroupName converts the literal cgroupfs name on the host to an internal identifier.
  183. func (m *cgroupManagerImpl) CgroupName(name string) CgroupName {
  184. if m.adapter.cgroupManagerType == libcontainerSystemd {
  185. return ParseSystemdToCgroupName(name)
  186. }
  187. return ParseCgroupfsToCgroupName(name)
  188. }
  189. // buildCgroupPaths builds a path to each cgroup subsystem for the specified name.
  190. func (m *cgroupManagerImpl) buildCgroupPaths(name CgroupName) map[string]string {
  191. cgroupFsAdaptedName := m.Name(name)
  192. cgroupPaths := make(map[string]string, len(m.subsystems.MountPoints))
  193. for key, val := range m.subsystems.MountPoints {
  194. cgroupPaths[key] = path.Join(val, cgroupFsAdaptedName)
  195. }
  196. return cgroupPaths
  197. }
  198. // TODO(filbranden): This logic belongs in libcontainer/cgroup/systemd instead.
  199. // It should take a libcontainerconfigs.Cgroup.Path field (rather than Name and Parent)
  200. // and split it appropriately, using essentially the logic below.
  201. // This was done for cgroupfs in opencontainers/runc#497 but a counterpart
  202. // for systemd was never introduced.
  203. func updateSystemdCgroupInfo(cgroupConfig *libcontainerconfigs.Cgroup, cgroupName CgroupName) {
  204. dir, base := path.Split(cgroupName.ToSystemd())
  205. if dir == "/" {
  206. dir = "-.slice"
  207. } else {
  208. dir = path.Base(dir)
  209. }
  210. cgroupConfig.Parent = dir
  211. cgroupConfig.Name = base
  212. }
  213. // Exists checks if all subsystem cgroups already exist
  214. func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
  215. // Get map of all cgroup paths on the system for the particular cgroup
  216. cgroupPaths := m.buildCgroupPaths(name)
  217. // the presence of alternative control groups not known to runc confuses
  218. // the kubelet existence checks.
  219. // ideally, we would have a mechanism in runc to support Exists() logic
  220. // scoped to the set control groups it understands. this is being discussed
  221. // in https://github.com/opencontainers/runc/issues/1440
  222. // once resolved, we can remove this code.
  223. whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")
  224. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  225. whitelistControllers.Insert("pids")
  226. }
  227. if _, ok := m.subsystems.MountPoints["hugetlb"]; ok {
  228. whitelistControllers.Insert("hugetlb")
  229. }
  230. var missingPaths []string
  231. // If even one cgroup path doesn't exist, then the cgroup doesn't exist.
  232. for controller, path := range cgroupPaths {
  233. // ignore mounts we don't care about
  234. if !whitelistControllers.Has(controller) {
  235. continue
  236. }
  237. if !libcontainercgroups.PathExists(path) {
  238. missingPaths = append(missingPaths, path)
  239. }
  240. }
  241. if len(missingPaths) > 0 {
  242. klog.V(4).Infof("The Cgroup %v has some missing paths: %v", name, missingPaths)
  243. return false
  244. }
  245. return true
  246. }
  247. // Destroy destroys the specified cgroup
  248. func (m *cgroupManagerImpl) Destroy(cgroupConfig *CgroupConfig) error {
  249. start := time.Now()
  250. defer func() {
  251. metrics.CgroupManagerDuration.WithLabelValues("destroy").Observe(metrics.SinceInSeconds(start))
  252. }()
  253. cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
  254. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{}
  255. // libcontainer consumes a different field and expects a different syntax
  256. // depending on the cgroup driver in use, so we need this conditional here.
  257. if m.adapter.cgroupManagerType == libcontainerSystemd {
  258. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  259. } else {
  260. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  261. }
  262. manager, err := m.adapter.newManager(libcontainerCgroupConfig, cgroupPaths)
  263. if err != nil {
  264. return err
  265. }
  266. // Delete cgroups using libcontainers Managers Destroy() method
  267. if err = manager.Destroy(); err != nil {
  268. return fmt.Errorf("unable to destroy cgroup paths for cgroup %v : %v", cgroupConfig.Name, err)
  269. }
  270. return nil
  271. }
  272. type subsystem interface {
  273. // Name returns the name of the subsystem.
  274. Name() string
  275. // Set the cgroup represented by cgroup.
  276. Set(path string, cgroup *libcontainerconfigs.Cgroup) error
  277. // GetStats returns the statistics associated with the cgroup
  278. GetStats(path string, stats *libcontainercgroups.Stats) error
  279. }
  280. // getSupportedSubsystems returns a map of subsystem and if it must be mounted for the kubelet to function.
  281. func getSupportedSubsystems() map[subsystem]bool {
  282. supportedSubsystems := map[subsystem]bool{
  283. &cgroupfs.MemoryGroup{}: true,
  284. &cgroupfs.CpuGroup{}: true,
  285. &cgroupfs.PidsGroup{}: false,
  286. }
  287. // not all hosts support hugetlb cgroup, and in the absent of hugetlb, we will fail silently by reporting no capacity.
  288. supportedSubsystems[&cgroupfs.HugetlbGroup{}] = false
  289. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  290. supportedSubsystems[&cgroupfs.PidsGroup{}] = true
  291. }
  292. return supportedSubsystems
  293. }
  294. // setSupportedSubsystems sets cgroup resource limits only on the supported
  295. // subsystems. ie. cpu and memory. We don't use libcontainer's cgroup/fs/Set()
  296. // method as it doesn't allow us to skip updates on the devices cgroup
  297. // Allowing or denying all devices by writing 'a' to devices.allow or devices.deny is
  298. // not possible once the device cgroups has children. Once the pod level cgroup are
  299. // created under the QOS level cgroup we cannot update the QOS level device cgroup.
  300. // We would like to skip setting any values on the device cgroup in this case
  301. // but this is not possible with libcontainers Set() method
  302. // See https://github.com/opencontainers/runc/issues/932
  303. func setSupportedSubsystems(cgroupConfig *libcontainerconfigs.Cgroup) error {
  304. for sys, required := range getSupportedSubsystems() {
  305. if _, ok := cgroupConfig.Paths[sys.Name()]; !ok {
  306. if required {
  307. return fmt.Errorf("failed to find subsystem mount for required subsystem: %v", sys.Name())
  308. }
  309. // the cgroup is not mounted, but its not required so continue...
  310. klog.V(6).Infof("Unable to find subsystem mount for optional subsystem: %v", sys.Name())
  311. continue
  312. }
  313. if err := sys.Set(cgroupConfig.Paths[sys.Name()], cgroupConfig); err != nil {
  314. return fmt.Errorf("failed to set config for supported subsystems : %v", err)
  315. }
  316. }
  317. return nil
  318. }
  319. func (m *cgroupManagerImpl) toResources(resourceConfig *ResourceConfig) *libcontainerconfigs.Resources {
  320. resources := &libcontainerconfigs.Resources{}
  321. if resourceConfig == nil {
  322. return resources
  323. }
  324. if resourceConfig.Memory != nil {
  325. resources.Memory = *resourceConfig.Memory
  326. }
  327. if resourceConfig.CpuShares != nil {
  328. resources.CpuShares = *resourceConfig.CpuShares
  329. }
  330. if resourceConfig.CpuQuota != nil {
  331. resources.CpuQuota = *resourceConfig.CpuQuota
  332. }
  333. if resourceConfig.CpuPeriod != nil {
  334. resources.CpuPeriod = *resourceConfig.CpuPeriod
  335. }
  336. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  337. if resourceConfig.PidsLimit != nil {
  338. resources.PidsLimit = *resourceConfig.PidsLimit
  339. }
  340. }
  341. // if huge pages are enabled, we set them in libcontainer
  342. // for each page size enumerated, set that value
  343. pageSizes := sets.NewString()
  344. for pageSize, limit := range resourceConfig.HugePageLimit {
  345. sizeString, err := v1helper.HugePageUnitSizeFromByteSize(pageSize)
  346. if err != nil {
  347. klog.Warningf("pageSize is invalid: %v", err)
  348. continue
  349. }
  350. resources.HugetlbLimit = append(resources.HugetlbLimit, &libcontainerconfigs.HugepageLimit{
  351. Pagesize: sizeString,
  352. Limit: uint64(limit),
  353. })
  354. pageSizes.Insert(sizeString)
  355. }
  356. // for each page size omitted, limit to 0
  357. for _, pageSize := range cgroupfs.HugePageSizes {
  358. if pageSizes.Has(pageSize) {
  359. continue
  360. }
  361. resources.HugetlbLimit = append(resources.HugetlbLimit, &libcontainerconfigs.HugepageLimit{
  362. Pagesize: pageSize,
  363. Limit: uint64(0),
  364. })
  365. }
  366. return resources
  367. }
  368. // Update updates the cgroup with the specified Cgroup Configuration
  369. func (m *cgroupManagerImpl) Update(cgroupConfig *CgroupConfig) error {
  370. start := time.Now()
  371. defer func() {
  372. metrics.CgroupManagerDuration.WithLabelValues("update").Observe(metrics.SinceInSeconds(start))
  373. }()
  374. // Extract the cgroup resource parameters
  375. resourceConfig := cgroupConfig.ResourceParameters
  376. resources := m.toResources(resourceConfig)
  377. cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
  378. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
  379. Resources: resources,
  380. Paths: cgroupPaths,
  381. }
  382. // libcontainer consumes a different field and expects a different syntax
  383. // depending on the cgroup driver in use, so we need this conditional here.
  384. if m.adapter.cgroupManagerType == libcontainerSystemd {
  385. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  386. } else {
  387. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  388. }
  389. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
  390. libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
  391. }
  392. if err := setSupportedSubsystems(libcontainerCgroupConfig); err != nil {
  393. return fmt.Errorf("failed to set supported cgroup subsystems for cgroup %v: %v", cgroupConfig.Name, err)
  394. }
  395. return nil
  396. }
  397. // Create creates the specified cgroup
  398. func (m *cgroupManagerImpl) Create(cgroupConfig *CgroupConfig) error {
  399. start := time.Now()
  400. defer func() {
  401. metrics.CgroupManagerDuration.WithLabelValues("create").Observe(metrics.SinceInSeconds(start))
  402. }()
  403. resources := m.toResources(cgroupConfig.ResourceParameters)
  404. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
  405. Resources: resources,
  406. }
  407. // libcontainer consumes a different field and expects a different syntax
  408. // depending on the cgroup driver in use, so we need this conditional here.
  409. if m.adapter.cgroupManagerType == libcontainerSystemd {
  410. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  411. } else {
  412. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  413. }
  414. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
  415. libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
  416. }
  417. // get the manager with the specified cgroup configuration
  418. manager, err := m.adapter.newManager(libcontainerCgroupConfig, nil)
  419. if err != nil {
  420. return err
  421. }
  422. // Apply(-1) is a hack to create the cgroup directories for each resource
  423. // subsystem. The function [cgroups.Manager.apply()] applies cgroup
  424. // configuration to the process with the specified pid.
  425. // It creates cgroup files for each subsystems and writes the pid
  426. // in the tasks file. We use the function to create all the required
  427. // cgroup files but not attach any "real" pid to the cgroup.
  428. if err := manager.Apply(-1); err != nil {
  429. return err
  430. }
  431. // it may confuse why we call set after we do apply, but the issue is that runc
  432. // follows a similar pattern. it's needed to ensure cpu quota is set properly.
  433. if err := m.Update(cgroupConfig); err != nil {
  434. utilruntime.HandleError(fmt.Errorf("cgroup update failed %v", err))
  435. }
  436. return nil
  437. }
  438. // Scans through all subsystems to find pids associated with specified cgroup.
  439. func (m *cgroupManagerImpl) Pids(name CgroupName) []int {
  440. // we need the driver specific name
  441. cgroupFsName := m.Name(name)
  442. // Get a list of processes that we need to kill
  443. pidsToKill := sets.NewInt()
  444. var pids []int
  445. for _, val := range m.subsystems.MountPoints {
  446. dir := path.Join(val, cgroupFsName)
  447. _, err := os.Stat(dir)
  448. if os.IsNotExist(err) {
  449. // The subsystem pod cgroup is already deleted
  450. // do nothing, continue
  451. continue
  452. }
  453. // Get a list of pids that are still charged to the pod's cgroup
  454. pids, err = getCgroupProcs(dir)
  455. if err != nil {
  456. continue
  457. }
  458. pidsToKill.Insert(pids...)
  459. // WalkFunc which is called for each file and directory in the pod cgroup dir
  460. visitor := func(path string, info os.FileInfo, err error) error {
  461. if err != nil {
  462. klog.V(4).Infof("cgroup manager encountered error scanning cgroup path %q: %v", path, err)
  463. return filepath.SkipDir
  464. }
  465. if !info.IsDir() {
  466. return nil
  467. }
  468. pids, err = getCgroupProcs(path)
  469. if err != nil {
  470. klog.V(4).Infof("cgroup manager encountered error getting procs for cgroup path %q: %v", path, err)
  471. return filepath.SkipDir
  472. }
  473. pidsToKill.Insert(pids...)
  474. return nil
  475. }
  476. // Walk through the pod cgroup directory to check if
  477. // container cgroups haven't been GCed yet. Get attached processes to
  478. // all such unwanted containers under the pod cgroup
  479. if err = filepath.Walk(dir, visitor); err != nil {
  480. klog.V(4).Infof("cgroup manager encountered error scanning pids for directory: %q: %v", dir, err)
  481. }
  482. }
  483. return pidsToKill.List()
  484. }
  485. // ReduceCPULimits reduces the cgroup's cpu shares to the lowest possible value
  486. func (m *cgroupManagerImpl) ReduceCPULimits(cgroupName CgroupName) error {
  487. // Set lowest possible CpuShares value for the cgroup
  488. minimumCPUShares := uint64(MinShares)
  489. resources := &ResourceConfig{
  490. CpuShares: &minimumCPUShares,
  491. }
  492. containerConfig := &CgroupConfig{
  493. Name: cgroupName,
  494. ResourceParameters: resources,
  495. }
  496. return m.Update(containerConfig)
  497. }
  498. func getStatsSupportedSubsystems(cgroupPaths map[string]string) (*libcontainercgroups.Stats, error) {
  499. stats := libcontainercgroups.NewStats()
  500. for sys, required := range getSupportedSubsystems() {
  501. if _, ok := cgroupPaths[sys.Name()]; !ok {
  502. if required {
  503. return nil, fmt.Errorf("failed to find subsystem mount for required subsystem: %v", sys.Name())
  504. }
  505. // the cgroup is not mounted, but its not required so continue...
  506. klog.V(6).Infof("Unable to find subsystem mount for optional subsystem: %v", sys.Name())
  507. continue
  508. }
  509. if err := sys.GetStats(cgroupPaths[sys.Name()], stats); err != nil {
  510. return nil, fmt.Errorf("failed to get stats for supported subsystems : %v", err)
  511. }
  512. }
  513. return stats, nil
  514. }
  515. func toResourceStats(stats *libcontainercgroups.Stats) *ResourceStats {
  516. return &ResourceStats{
  517. MemoryStats: &MemoryStats{
  518. Usage: int64(stats.MemoryStats.Usage.Usage),
  519. },
  520. }
  521. }
  522. // Get sets the ResourceParameters of the specified cgroup as read from the cgroup fs
  523. func (m *cgroupManagerImpl) GetResourceStats(name CgroupName) (*ResourceStats, error) {
  524. cgroupPaths := m.buildCgroupPaths(name)
  525. stats, err := getStatsSupportedSubsystems(cgroupPaths)
  526. if err != nil {
  527. return nil, fmt.Errorf("failed to get stats supported cgroup subsystems for cgroup %v: %v", name, err)
  528. }
  529. return toResourceStats(stats), nil
  530. }