cgroup/fs: rework Apply()

In manager.Apply() method, a path to each subsystem is obtained by
calling d.path(sys.Name()), and the sys.Apply() is called that does
the same call to d.path() again.

d.path() is an expensive call, so rather than to call it twice, let's
reuse the result.

This results the number of times we parse mountinfo during container
start from 62 to 34 on my setup.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin 2020-05-31 16:02:34 -07:00
parent 819fcc687e
commit c1adc99a20
15 changed files with 44 additions and 101 deletions

View File

@ -22,12 +22,8 @@ func (s *BlkioGroup) Name() string {
return "blkio" return "blkio"
} }
func (s *BlkioGroup) Apply(d *cgroupData) error { func (s *BlkioGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("blkio") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -21,17 +21,7 @@ func (s *CpuGroup) Name() string {
return "cpu" return "cpu"
} }
func (s *CpuGroup) Apply(d *cgroupData) error { func (s *CpuGroup) Apply(path string, d *cgroupData) error {
// We always want to join the cpu group, to allow fair cpu scheduling
// on a container basis
path, err := d.path("cpu")
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return s.ApplyDir(path, d.config, d.pid)
}
func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error {
// This might happen if we have no cpu cgroup mounted. // This might happen if we have no cpu cgroup mounted.
// Just do nothing and don't fail. // Just do nothing and don't fail.
if path == "" { if path == "" {
@ -43,12 +33,12 @@ func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error
// We should set the real-Time group scheduling settings before moving // We should set the real-Time group scheduling settings before moving
// in the process because if the process is already in SCHED_RR mode // in the process because if the process is already in SCHED_RR mode
// and no RT bandwidth is set, adding it will fail. // and no RT bandwidth is set, adding it will fail.
if err := s.SetRtSched(path, cgroup); err != nil { if err := s.SetRtSched(path, d.config); err != nil {
return err return err
} }
// because we are not using d.join we need to place the pid into the procs file // Since we are not using join(), we need to place the pid
// unlike the other subsystems // into the procs file unlike other subsystems.
return cgroups.WriteCgroupProc(path, pid) return cgroups.WriteCgroupProc(path, d.pid)
} }
func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error { func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {

View File

@ -182,7 +182,9 @@ func TestCpuSetRtSchedAtApply(t *testing.T) {
helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter
helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter
cpu := &CpuGroup{} cpu := &CpuGroup{}
if err := cpu.ApplyDir(helper.CgroupPath, helper.CgroupData.config, 1234); err != nil {
helper.CgroupData.pid = 1234
if err := cpu.Apply(helper.CgroupPath, helper.CgroupData); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -40,13 +40,8 @@ func (s *CpuacctGroup) Name() string {
return "cpuacct" return "cpuacct"
} }
func (s *CpuacctGroup) Apply(d *cgroupData) error { func (s *CpuacctGroup) Apply(path string, d *cgroupData) error {
// we just want to join this group even though we don't set anything return join(path, d.pid)
if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -23,12 +23,8 @@ func (s *CpusetGroup) Name() string {
return "cpuset" return "cpuset"
} }
func (s *CpusetGroup) Apply(d *cgroupData) error { func (s *CpusetGroup) Apply(path string, d *cgroupData) error {
dir, err := d.path("cpuset") return s.ApplyDir(path, d.config, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return s.ApplyDir(dir, d.config, d.pid)
} }
func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -22,17 +22,16 @@ func (s *DevicesGroup) Name() string {
return "devices" return "devices"
} }
func (s *DevicesGroup) Apply(d *cgroupData) error { func (s *DevicesGroup) Apply(path string, d *cgroupData) error {
if d.config.SkipDevices { if d.config.SkipDevices {
return nil return nil
} }
_, err := d.join("devices") if path == "" {
if err != nil { // Return error here, since devices cgroup
// We will return error even it's `not found` error, devices // is a hard requirement for container's security.
// cgroup is hard requirement for container's security. return errSubsystemDoesNotExist
return err
} }
return nil return join(path, d.pid)
} }
func loadEmulator(path string) (*devices.Emulator, error) { func loadEmulator(path string) (*devices.Emulator, error) {

View File

@ -22,12 +22,8 @@ func (s *FreezerGroup) Name() string {
return "freezer" return "freezer"
} }
func (s *FreezerGroup) Apply(d *cgroupData) error { func (s *FreezerGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("freezer") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -57,7 +57,7 @@ type subsystem interface {
// Removes the cgroup represented by 'cgroupData'. // Removes the cgroup represented by 'cgroupData'.
Remove(*cgroupData) error Remove(*cgroupData) error
// Creates and joins the cgroup represented by 'cgroupData'. // Creates and joins the cgroup represented by 'cgroupData'.
Apply(*cgroupData) error Apply(path string, c *cgroupData) error
// Set the cgroup represented by cgroup. // Set the cgroup represented by cgroup.
Set(path string, cgroup *configs.Cgroup) error Set(path string, cgroup *configs.Cgroup) error
} }
@ -211,7 +211,7 @@ func (m *manager) Apply(pid int) (err error) {
} }
m.paths[sys.Name()] = p m.paths[sys.Name()] = p
if err := sys.Apply(d); err != nil { if err := sys.Apply(p, d); err != nil {
// In the case of rootless (including euid=0 in userns), where an // In the case of rootless (including euid=0 in userns), where an
// explicit cgroup path hasn't been set, we don't bail on error in // explicit cgroup path hasn't been set, we don't bail on error in
// case of permission problems. Cases where limits have been set // case of permission problems. Cases where limits have been set
@ -374,18 +374,14 @@ func (raw *cgroupData) path(subsystem string) (string, error) {
return filepath.Join(parentPath, raw.innerPath), nil return filepath.Join(parentPath, raw.innerPath), nil
} }
func (raw *cgroupData) join(subsystem string) (string, error) { func join(path string, pid int) error {
path, err := raw.path(subsystem) if path == "" {
if err != nil { return nil
return "", err
} }
if err := os.MkdirAll(path, 0755); err != nil { if err := os.MkdirAll(path, 0755); err != nil {
return "", err return err
} }
if err := cgroups.WriteCgroupProc(path, raw.pid); err != nil { return cgroups.WriteCgroupProc(path, pid)
return "", err
}
return path, nil
} }
func removePath(p string, err error) error { func removePath(p string, err error) error {

View File

@ -19,12 +19,8 @@ func (s *HugetlbGroup) Name() string {
return "hugetlb" return "hugetlb"
} }
func (s *HugetlbGroup) Apply(d *cgroupData) error { func (s *HugetlbGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("hugetlb") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -37,11 +37,8 @@ func (s *MemoryGroup) Name() string {
return "memory" return "memory"
} }
func (s *MemoryGroup) Apply(d *cgroupData) (err error) { func (s *MemoryGroup) Apply(path string, d *cgroupData) (err error) {
path, err := d.path("memory") if path == "" {
if err != nil && !cgroups.IsNotFound(err) {
return err
} else if path == "" {
return nil return nil
} }
if memoryAssigned(d.config) { if memoryAssigned(d.config) {
@ -66,11 +63,7 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
// We need to join memory cgroup after set memory limits, because // We need to join memory cgroup after set memory limits, because
// kmem.limit_in_bytes can only be set when the cgroup is empty. // kmem.limit_in_bytes can only be set when the cgroup is empty.
_, err = d.join("memory") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error { func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {

View File

@ -16,10 +16,10 @@ func (s *NameGroup) Name() string {
return s.GroupName return s.GroupName
} }
func (s *NameGroup) Apply(d *cgroupData) error { func (s *NameGroup) Apply(path string, d *cgroupData) error {
if s.Join { if s.Join {
// ignore errors if the named cgroup does not exist // ignore errors if the named cgroup does not exist
d.join(s.GroupName) join(path, d.pid)
} }
return nil return nil
} }

View File

@ -17,12 +17,8 @@ func (s *NetClsGroup) Name() string {
return "net_cls" return "net_cls"
} }
func (s *NetClsGroup) Apply(d *cgroupData) error { func (s *NetClsGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("net_cls") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *NetClsGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *NetClsGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -15,12 +15,8 @@ func (s *NetPrioGroup) Name() string {
return "net_prio" return "net_prio"
} }
func (s *NetPrioGroup) Apply(d *cgroupData) error { func (s *NetPrioGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("net_prio") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *NetPrioGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *NetPrioGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -14,12 +14,8 @@ func (s *PerfEventGroup) Name() string {
return "perf_event" return "perf_event"
} }
func (s *PerfEventGroup) Apply(d *cgroupData) error { func (s *PerfEventGroup) Apply(path string, d *cgroupData) error {
// we just want to join this group even though we don't set anything return join(path, d.pid)
if _, err := d.join("perf_event"); err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error {

View File

@ -19,12 +19,8 @@ func (s *PidsGroup) Name() string {
return "pids" return "pids"
} }
func (s *PidsGroup) Apply(d *cgroupData) error { func (s *PidsGroup) Apply(path string, d *cgroupData) error {
_, err := d.join("pids") return join(path, d.pid)
if err != nil && !cgroups.IsNotFound(err) {
return err
}
return nil
} }
func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error { func (s *PidsGroup) Set(path string, cgroup *configs.Cgroup) error {