Browse code

Separating cgroup Memory and MemoryReservation.

This will allow for these to be set independently. Keep the current Docker behavior where Memory and MemoryReservation are set to the value of Memory.

Docker-DCO-1.1-Signed-off-by: Victor Marmol <vmarmol@google.com> (github: vmarmol)

Victor Marmol authored on 2014/04/24 14:11:43
Showing 6 changed files
... ...
@@ -21,10 +21,11 @@ var actions = map[string]Action{
21 21
 
22 22
 	"net.join": joinNetNamespace, // join another containers net namespace
23 23
 
24
-	"cgroups.cpu_shares":  cpuShares,  // set the cpu shares
25
-	"cgroups.memory":      memory,     // set the memory limit
26
-	"cgroups.memory_swap": memorySwap, // set the memory swap limit
27
-	"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
24
+	"cgroups.cpu_shares":         cpuShares,         // set the cpu shares
25
+	"cgroups.memory":             memory,            // set the memory limit
26
+	"cgroups.memory_reservation": memoryReservation, // set the memory reservation
27
+	"cgroups.memory_swap":        memorySwap,        // set the memory swap limit
28
+	"cgroups.cpuset.cpus":        cpusetCpus,        // set the cpus used
28 29
 
29 30
 	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
30 31
 
... ...
@@ -70,6 +71,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
70 70
 	return nil
71 71
 }
72 72
 
73
+func memoryReservation(container *libcontainer.Container, context interface{}, value string) error {
74
+	if container.Cgroups == nil {
75
+		return fmt.Errorf("cannot set cgroups when they are disabled")
76
+	}
77
+
78
+	v, err := utils.RAMInBytes(value)
79
+	if err != nil {
80
+		return err
81
+	}
82
+	container.Cgroups.MemoryReservation = v
83
+	return nil
84
+}
85
+
73 86
 func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
74 87
 	if container.Cgroups == nil {
75 88
 		return fmt.Errorf("cannot set cgroups when they are disabled")
... ...
@@ -93,7 +93,7 @@ func TestCpuShares(t *testing.T) {
93 93
 	}
94 94
 }
95 95
 
96
-func TestCgroupMemory(t *testing.T) {
96
+func TestMemory(t *testing.T) {
97 97
 	var (
98 98
 		container = template.New()
99 99
 		opts      = []string{
... ...
@@ -109,6 +109,22 @@ func TestCgroupMemory(t *testing.T) {
109 109
 	}
110 110
 }
111 111
 
112
+func TestMemoryReservation(t *testing.T) {
113
+	var (
114
+		container = template.New()
115
+		opts      = []string{
116
+			"cgroups.memory_reservation=500m",
117
+		}
118
+	)
119
+	if err := ParseConfiguration(container, nil, opts); err != nil {
120
+		t.Fatal(err)
121
+	}
122
+
123
+	if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
124
+		t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
125
+	}
126
+}
127
+
112 128
 func TestAddCap(t *testing.T) {
113 129
 	var (
114 130
 		container = template.New()
... ...
@@ -91,6 +91,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
91 91
 	if c.Resources != nil {
92 92
 		container.Cgroups.CpuShares = c.Resources.CpuShares
93 93
 		container.Cgroups.Memory = c.Resources.Memory
94
+		container.Cgroups.MemoryReservation = c.Resources.Memory
94 95
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
95 96
 	}
96 97
 	return nil
... ...
@@ -12,13 +12,14 @@ type Cgroup struct {
12 12
 	Name   string `json:"name,omitempty"`
13 13
 	Parent string `json:"parent,omitempty"`
14 14
 
15
-	DeviceAccess bool   `json:"device_access,omitempty"` // name of parent cgroup or slice
16
-	Memory       int64  `json:"memory,omitempty"`        // Memory limit (in bytes)
17
-	MemorySwap   int64  `json:"memory_swap,omitempty"`   // Total memory usage (memory + swap); set `-1' to disable swap
18
-	CpuShares    int64  `json:"cpu_shares,omitempty"`    // CPU shares (relative weight vs. other containers)
19
-	CpuQuota     int64  `json:"cpu_quota,omitempty"`     // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
20
-	CpuPeriod    int64  `json:"cpu_period,omitempty"`    // CPU period to be used for hardcapping (in usecs). 0 to use system default.
21
-	CpusetCpus   string `json:"cpuset_cpus,omitempty"`   // CPU to use
15
+	DeviceAccess      bool   `json:"device_access,omitempty"`      // name of parent cgroup or slice
16
+	Memory            int64  `json:"memory,omitempty"`             // Memory limit (in bytes)
17
+	MemoryReservation int64  `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
18
+	MemorySwap        int64  `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
19
+	CpuShares         int64  `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
20
+	CpuQuota          int64  `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
21
+	CpuPeriod         int64  `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
22
+	CpusetCpus        string `json:"cpuset_cpus,omitempty"`        // CPU to use
22 23
 
23 24
 	UnitProperties [][2]string `json:"unit_properties,omitempty"` // systemd unit properties
24 25
 }
... ...
@@ -13,7 +13,7 @@ type memoryGroup struct {
13 13
 func (s *memoryGroup) Set(d *data) error {
14 14
 	dir, err := d.join("memory")
15 15
 	// only return an error for memory if it was not specified
16
-	if err != nil && (d.c.Memory != 0 || d.c.MemorySwap != 0) {
16
+	if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
17 17
 		return err
18 18
 	}
19 19
 	defer func() {
... ...
@@ -22,12 +22,15 @@ func (s *memoryGroup) Set(d *data) error {
22 22
 		}
23 23
 	}()
24 24
 
25
-	if d.c.Memory != 0 || d.c.MemorySwap != 0 {
25
+	// Only set values if some config was specified.
26
+	if d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0 {
26 27
 		if d.c.Memory != 0 {
27 28
 			if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
28 29
 				return err
29 30
 			}
30
-			if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
31
+		}
32
+		if d.c.MemoryReservation != 0 {
33
+			if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.MemoryReservation, 10)); err != nil {
31 34
 				return err
32 35
 			}
33 36
 		}
... ...
@@ -121,6 +121,10 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
121 121
 		properties = append(properties,
122 122
 			systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
123 123
 	}
124
+	if c.MemoryReservation != 0 {
125
+		properties = append(properties,
126
+			systemd1.Property{"MemorySoftLimit", dbus.MakeVariant(uint64(c.MemoryReservation))})
127
+	}
124 128
 	// TODO: MemorySwap not available in systemd
125 129
 
126 130
 	if c.CpuShares != 0 {