Browse code

Add support for blkio read/write bps device

Signed-off-by: Ma Shimiao <mashimiao.fnst@cn.fujitsu.com>

Ma Shimiao authored on 2015/07/08 20:06:48
Showing 20 changed files
... ...
@@ -1372,6 +1372,8 @@ _docker_run() {
1372 1372
 		--cpuset-mems
1373 1373
 		--cpu-shares
1374 1374
 		--device
1375
+		--device-read-bps
1376
+		--device-write-bps
1375 1377
 		--dns
1376 1378
 		--dns-opt
1377 1379
 		--dns-search
... ...
@@ -468,6 +468,8 @@ __docker_subcommand() {
468 468
         "($help)*--cap-drop=[Drop Linux capabilities]:capability: "
469 469
         "($help)--cidfile=[Write the container ID to the file]:CID file:_files"
470 470
         "($help)*--device=[Add a host device to the container]:device:_files"
471
+        "($help)*--device-read-bps=[Limit the read rate (bytes per second) from a device]:device:IO rate: "
472
+        "($help)*--device-write-bps=[Limit the write rate (bytes per second) to a device]:device:IO rate: "
471 473
         "($help)*--dns=[Set custom DNS servers]:DNS server: "
472 474
         "($help)*--dns-opt=[Set custom DNS options]:DNS option: "
473 475
         "($help)*--dns-search=[Set custom DNS search domains]:DNS domains: "
... ...
@@ -163,6 +163,16 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
163 163
 		return err
164 164
 	}
165 165
 
166
+	readBpsDevice, err := getBlkioReadBpsDevices(c.HostConfig)
167
+	if err != nil {
168
+		return err
169
+	}
170
+
171
+	writeBpsDevice, err := getBlkioWriteBpsDevices(c.HostConfig)
172
+	if err != nil {
173
+		return err
174
+	}
175
+
166 176
 	for _, limit := range ulimits {
167 177
 		rl, err := limit.GetRlimit()
168 178
 		if err != nil {
... ...
@@ -178,16 +188,18 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
178 178
 			CPUShares:         c.HostConfig.CPUShares,
179 179
 			BlkioWeight:       c.HostConfig.BlkioWeight,
180 180
 		},
181
-		MemorySwap:        c.HostConfig.MemorySwap,
182
-		KernelMemory:      c.HostConfig.KernelMemory,
183
-		CpusetCpus:        c.HostConfig.CpusetCpus,
184
-		CpusetMems:        c.HostConfig.CpusetMems,
185
-		CPUPeriod:         c.HostConfig.CPUPeriod,
186
-		CPUQuota:          c.HostConfig.CPUQuota,
187
-		Rlimits:           rlimits,
188
-		BlkioWeightDevice: weightDevices,
189
-		OomKillDisable:    c.HostConfig.OomKillDisable,
190
-		MemorySwappiness:  *c.HostConfig.MemorySwappiness,
181
+		MemorySwap:                  c.HostConfig.MemorySwap,
182
+		KernelMemory:                c.HostConfig.KernelMemory,
183
+		CpusetCpus:                  c.HostConfig.CpusetCpus,
184
+		CpusetMems:                  c.HostConfig.CpusetMems,
185
+		CPUPeriod:                   c.HostConfig.CPUPeriod,
186
+		CPUQuota:                    c.HostConfig.CPUQuota,
187
+		Rlimits:                     rlimits,
188
+		BlkioWeightDevice:           weightDevices,
189
+		BlkioThrottleReadBpsDevice:  readBpsDevice,
190
+		BlkioThrottleWriteBpsDevice: writeBpsDevice,
191
+		OomKillDisable:              c.HostConfig.OomKillDisable,
192
+		MemorySwappiness:            *c.HostConfig.MemorySwappiness,
191 193
 	}
192 194
 
193 195
 	processConfig := execdriver.ProcessConfig{
... ...
@@ -83,6 +83,36 @@ func parseSecurityOpt(container *container.Container, config *runconfig.HostConf
83 83
 	return err
84 84
 }
85 85
 
86
+func getBlkioReadBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) {
87
+	var BlkioReadBpsDevice []*blkiodev.ThrottleDevice
88
+	var stat syscall.Stat_t
89
+
90
+	for _, bpsDevice := range config.BlkioDeviceReadBps {
91
+		if err := syscall.Stat(bpsDevice.Path, &stat); err != nil {
92
+			return nil, err
93
+		}
94
+		ReadBpsDevice := blkiodev.NewThrottleDevice(int64(stat.Rdev/256), int64(stat.Rdev%256), bpsDevice.Rate)
95
+		BlkioReadBpsDevice = append(BlkioReadBpsDevice, ReadBpsDevice)
96
+	}
97
+
98
+	return BlkioReadBpsDevice, nil
99
+}
100
+
101
+func getBlkioWriteBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) {
102
+	var BlkioWriteBpsDevice []*blkiodev.ThrottleDevice
103
+	var stat syscall.Stat_t
104
+
105
+	for _, bpsDevice := range config.BlkioDeviceWriteBps {
106
+		if err := syscall.Stat(bpsDevice.Path, &stat); err != nil {
107
+			return nil, err
108
+		}
109
+		WriteBpsDevice := blkiodev.NewThrottleDevice(int64(stat.Rdev/256), int64(stat.Rdev%256), bpsDevice.Rate)
110
+		BlkioWriteBpsDevice = append(BlkioWriteBpsDevice, WriteBpsDevice)
111
+	}
112
+
113
+	return BlkioWriteBpsDevice, nil
114
+}
115
+
86 116
 func checkKernelVersion(k, major, minor int) bool {
87 117
 	if v, err := kernel.GetKernelVersion(); err != nil {
88 118
 		logrus.Warnf("%s", err)
... ...
@@ -259,6 +289,16 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *runconfig.HostC
259 259
 		logrus.Warnf("Your kernel does not support Block I/O weight_device. Weight-device discarded.")
260 260
 		hostConfig.BlkioWeightDevice = []*pblkiodev.WeightDevice{}
261 261
 	}
262
+	if len(hostConfig.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice {
263
+		warnings = append(warnings, "Your kernel does not support Block read limit in bytes per second.")
264
+		logrus.Warnf("Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded.")
265
+		hostConfig.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{}
266
+	}
267
+	if len(hostConfig.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice {
268
+		warnings = append(warnings, "Your kernel does not support Block write limit in bytes per second.")
269
+		logrus.Warnf("Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded.")
270
+		hostConfig.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{}
271
+	}
262 272
 	if hostConfig.OomKillDisable && !sysInfo.OomKillDisable {
263 273
 		hostConfig.OomKillDisable = false
264 274
 		return warnings, fmt.Errorf("Your kernel does not support oom kill disable.")
... ...
@@ -39,6 +39,14 @@ func parseSecurityOpt(container *container.Container, config *runconfig.HostConf
39 39
 	return nil
40 40
 }
41 41
 
42
+func getBlkioReadBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) {
43
+	return nil, nil
44
+}
45
+
46
+func getBlkioWriteBpsDevices(config *runconfig.HostConfig) ([]*blkiodev.ThrottleDevice, error) {
47
+	return nil, nil
48
+}
49
+
42 50
 func setupInitLayer(initLayer string, rootUID, rootGID int) error {
43 51
 	return nil
44 52
 }
... ...
@@ -38,16 +38,18 @@ type Resources struct {
38 38
 
39 39
 	// Fields below here are platform specific
40 40
 
41
-	BlkioWeightDevice []*blkiodev.WeightDevice `json:"blkio_weight_device"`
42
-	MemorySwap        int64                    `json:"memory_swap"`
43
-	KernelMemory      int64                    `json:"kernel_memory"`
44
-	CPUQuota          int64                    `json:"cpu_quota"`
45
-	CpusetCpus        string                   `json:"cpuset_cpus"`
46
-	CpusetMems        string                   `json:"cpuset_mems"`
47
-	CPUPeriod         int64                    `json:"cpu_period"`
48
-	Rlimits           []*ulimit.Rlimit         `json:"rlimits"`
49
-	OomKillDisable    bool                     `json:"oom_kill_disable"`
50
-	MemorySwappiness  int64                    `json:"memory_swappiness"`
41
+	BlkioWeightDevice           []*blkiodev.WeightDevice   `json:"blkio_weight_device"`
42
+	BlkioThrottleReadBpsDevice  []*blkiodev.ThrottleDevice `json:"blkio_throttle_read_bps_device"`
43
+	BlkioThrottleWriteBpsDevice []*blkiodev.ThrottleDevice `json:"blkio_throttle_write_bps_device"`
44
+	MemorySwap                  int64                      `json:"memory_swap"`
45
+	KernelMemory                int64                      `json:"kernel_memory"`
46
+	CPUQuota                    int64                      `json:"cpu_quota"`
47
+	CpusetCpus                  string                     `json:"cpuset_cpus"`
48
+	CpusetMems                  string                     `json:"cpuset_mems"`
49
+	CPUPeriod                   int64                      `json:"cpu_period"`
50
+	Rlimits                     []*ulimit.Rlimit           `json:"rlimits"`
51
+	OomKillDisable              bool                       `json:"oom_kill_disable"`
52
+	MemorySwappiness            int64                      `json:"memory_swappiness"`
51 53
 }
52 54
 
53 55
 // ProcessConfig is the platform specific structure that describes a process
... ...
@@ -170,6 +172,8 @@ func SetupCgroups(container *configs.Config, c *Command) error {
170 170
 		container.Cgroups.CpuQuota = c.Resources.CPUQuota
171 171
 		container.Cgroups.BlkioWeight = c.Resources.BlkioWeight
172 172
 		container.Cgroups.BlkioWeightDevice = c.Resources.BlkioWeightDevice
173
+		container.Cgroups.BlkioThrottleReadBpsDevice = c.Resources.BlkioThrottleReadBpsDevice
174
+		container.Cgroups.BlkioThrottleWriteBpsDevice = c.Resources.BlkioThrottleWriteBpsDevice
173 175
 		container.Cgroups.OomKillDisable = c.Resources.OomKillDisable
174 176
 		container.Cgroups.MemorySwappiness = c.Resources.MemorySwappiness
175 177
 	}
... ...
@@ -188,6 +188,8 @@ Create a container
188 188
              "CpusetMems": "0,1",
189 189
              "BlkioWeight": 300,
190 190
              "BlkioWeightDevice": [{}],
191
+             "BlkioDeviceReadBps": [{}],
192
+             "BlkioDeviceWriteBps": [{}],
191 193
              "MemorySwappiness": 60,
192 194
              "OomKillDisable": false,
193 195
              "OomScoreAdj": 500,
... ...
@@ -245,6 +247,10 @@ Json Parameters:
245 245
 -   **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
246 246
 -   **BlkioWeight** - Block IO weight (relative weight) accepts a weight value between 10 and 1000.
247 247
 -   **BlkioWeightDevice** - Block IO weight (relative device weight) in the form of:        `"BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]`
248
+-   **BlkioDeviceReadBps** - Limit read rate from a device in form of:	`"BlkioDeviceReadBps": [{"Path": "device_path", "Rate": rate}]`, for example:
249
+	`"BlkioDeviceReadBps": [{"Path": "/dev/sda", "Rate": "1024"}]"`
250
+-   **BlkioDeviceWriteBps** - Limit write rate to a device in the form of:	`"BlkioDeviceWriteBps": [{"Path": "deivce_path", "Rate": rate}]`, for example:
251
+	`"BlkioDeviceWriteBps": [{"Path": "/dev/sda", "Rate": "1024"}]"`
248 252
 -   **MemorySwappiness** - Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
249 253
 -   **OomKillDisable** - Boolean value, whether to disable OOM Killer for the container or not.
250 254
 -   **OomScoreAdj** - An integer value containing the score given to the container in order to tune OOM killer preferences.
... ...
@@ -398,6 +404,8 @@ Return low-level information on the container `id`
398 398
 			"Binds": null,
399 399
 			"BlkioWeight": 0,
400 400
 			"BlkioWeightDevice": [{}],
401
+			"BlkioDeviceReadBps": [{}],
402
+			"BlkioDeviceWriteBps": [{}],
401 403
 			"CapAdd": null,
402 404
 			"CapDrop": null,
403 405
 			"ContainerIDFile": "",
... ...
@@ -30,6 +30,8 @@ Creates a new container.
30 30
       --cpuset-cpus=""              CPUs in which to allow execution (0-3, 0,1)
31 31
       --cpuset-mems=""              Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
32 32
       --device=[]                   Add a host device to the container
33
+      --device-read-bps=[]          Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb)
34
+      --device-write-bps=[]         Limit write rate (bytes per second) to a device (e.g., --device-write-bps=/dev/sda:1mb)
33 35
       --disable-content-trust=true  Skip image verification
34 36
       --dns=[]                      Set custom DNS servers
35 37
       --dns-opt=[]                  Set custom DNS options
... ...
@@ -29,6 +29,8 @@ parent = "smn_cli"
29 29
       --cpuset-mems=""              Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
30 30
       -d, --detach=false            Run container in background and print container ID
31 31
       --device=[]                   Add a host device to the container
32
+      --device-read-bps=[]          Limit read rate (bytes per second) from a device (e.g., --device-read-bps=/dev/sda:1mb)
33
+      --device-write-bps=[]         Limit write rate (bytes per second) to a device (e.g., --device-write-bps=/dev/sda:1mb)
32 34
       --disable-content-trust=true  Skip image verification
33 35
       --dns=[]                      Set custom DNS servers
34 36
       --dns-opt=[]                  Set custom DNS options
... ...
@@ -624,6 +624,10 @@ container:
624 624
 | `--cpu-quota=0`            | Limit the CPU CFS (Completely Fair Scheduler) quota                                         |
625 625
 | `--blkio-weight=0`         | Block IO weight (relative weight) accepts a weight value between 10 and 1000.               |
626 626
 | `--blkio-weight-device=""` | Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)                                                |
627
+| `--device-read-bps="" `    | Limit read rate from a device (format: `<device-path>:<number>[<unit>]`.                    |
628
+|                            | Number is a positive integer. Unit can be one of kb, mb, or gb.                             |
629
+| `--device-write-bps="" `   | Limit write rate to a device (format: `<device-path>:<number>[<unit>]`.                     |
630
+|                            | Number is a positive integer. Unit can be one of kb, mb, or gb.                             |
627 631
 | `--oom-kill-disable=false` | Whether to disable OOM Killer for the container or not.                                     |
628 632
 | `--memory-swappiness=""  ` | Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.        |
629 633
 | `--shm-size=""  `          | Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.      |
... ...
@@ -978,6 +982,18 @@ $ docker run -it \
978 978
     --blkio-weight-device "/dev/sda:200" \
979 979
     ubuntu
980 980
 
981
+The `--device-read-bps` flag can limit read rate from a device.
982
+For example, the command creates a container and limits theread rate to `1mb` per second from `/dev/sda`:
983
+
984
+    $ docker run -ti --device-read-bps /dev/sda:1mb ubuntu
985
+
986
+The `--device-write-bps` flag can limit write rate to a device.
987
+For example, the command creates a container and limits write rate to `1mb` per second to `/dev/sda`:
988
+
989
+    $ docker run -ti --device-write-bps /dev/sda:1mb ubuntu
990
+
991
+Both flags take limits in the `<device-path>:<limit>[unit]` format. Both read and write rates must be a positive integer. You can specify the rate in `kb` (kilobytes), `mb` (megabytes), or `gb` (gigabytes).
992
+
981 993
 ## Additional groups
982 994
     --group-add: Add Linux capabilities
983 995
 
... ...
@@ -246,6 +246,18 @@ func (s *DockerSuite) TestRunWithBlkioInvalidWeightDevice(c *check.C) {
246 246
 	c.Assert(err, check.NotNil, check.Commentf(out))
247 247
 }
248 248
 
249
+func (s *DockerSuite) TestRunWithBlkioInvalidDeivceReadBps(c *check.C) {
250
+	testRequires(c, blkioWeight)
251
+	out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sda:500", "busybox", "true")
252
+	c.Assert(err, check.NotNil, check.Commentf(out))
253
+}
254
+
255
+func (s *DockerSuite) TestRunWithBlkioInvalidDeviceWriteBps(c *check.C) {
256
+	testRequires(c, blkioWeight)
257
+	out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sda:500", "busybox", "true")
258
+	c.Assert(err, check.NotNil, check.Commentf(out))
259
+}
260
+
249 261
 func (s *DockerSuite) TestRunOOMExitCode(c *check.C) {
250 262
 	testRequires(c, oomControl)
251 263
 	errChan := make(chan error)
... ...
@@ -20,6 +20,8 @@ docker-create - Create a new container
20 20
 [**--cpuset-cpus**[=*CPUSET-CPUS*]]
21 21
 [**--cpuset-mems**[=*CPUSET-MEMS*]]
22 22
 [**--device**[=*[]*]]
23
+[**--device-read-bps**[=*[]*]]
24
+[**--device-write-bps**[=*[]*]]
23 25
 [**--dns**[=*[]*]]
24 26
 [**--dns-search**[=*[]*]]
25 27
 [**--dns-opt**[=*[]*]]
... ...
@@ -125,6 +127,12 @@ two memory nodes.
125 125
 **--device**=[]
126 126
    Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
127 127
 
128
+**--device-read-bps**=[]
129
+    Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
130
+
131
+**--device-write-bps**=[]
132
+    Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
133
+
128 134
 **--dns**=[]
129 135
    Set custom DNS servers
130 136
 
... ...
@@ -21,6 +21,8 @@ docker-run - Run a command in a new container
21 21
 [**--cpuset-mems**[=*CPUSET-MEMS*]]
22 22
 [**-d**|**--detach**[=*false*]]
23 23
 [**--device**[=*[]*]]
24
+[**--device-read-bps**[=*[]*]]
25
+[**--device-write-bps**[=*[]*]]
24 26
 [**--dns**[=*[]*]]
25 27
 [**--dns-opt**[=*[]*]]
26 28
 [**--dns-search**[=*[]*]]
... ...
@@ -192,6 +194,12 @@ stopping the process by pressing the keys CTRL-P CTRL-Q.
192 192
 **--device**=[]
193 193
    Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
194 194
 
195
+**--device-read-bps**=[]
196
+   Limit read rate from a device (e.g. --device-read-bps=/dev/sda:1mb)
197
+
198
+**--device-write-bps**=[]
199
+   Limit write rate to a device (e.g. --device-write-bps=/dev/sda:1mb)
200
+
195 201
 **--dns-search**=[]
196 202
    Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
197 203
 
... ...
@@ -11,6 +11,7 @@ import (
11 11
 
12 12
 	"github.com/docker/docker/pkg/blkiodev"
13 13
 	"github.com/docker/docker/pkg/parsers"
14
+	"github.com/docker/docker/pkg/units"
14 15
 )
15 16
 
16 17
 var (
... ...
@@ -173,6 +174,9 @@ type ValidatorFctType func(val string) (string, error)
173 173
 // ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error.
174 174
 type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error)
175 175
 
176
+// ValidatorThrottleFctType defines a validator function that returns a validated struct and/or an error.
177
+type ValidatorThrottleFctType func(val string) (*blkiodev.ThrottleDevice, error)
178
+
176 179
 // ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
177 180
 type ValidatorFctListType func(val string) ([]string, error)
178 181
 
... ...
@@ -210,6 +214,29 @@ func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) {
210 210
 	}, nil
211 211
 }
212 212
 
213
+// ValidateThrottleBpsDevice validates that the specified string has a valid device-rate format.
214
+func ValidateThrottleBpsDevice(val string) (*blkiodev.ThrottleDevice, error) {
215
+	split := strings.SplitN(val, ":", 2)
216
+	if len(split) != 2 {
217
+		return nil, fmt.Errorf("bad format: %s", val)
218
+	}
219
+	if !strings.HasPrefix(split[0], "/dev/") {
220
+		return nil, fmt.Errorf("bad format for device path: %s", val)
221
+	}
222
+	rate, err := units.RAMInBytes(split[1])
223
+	if err != nil {
224
+		return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
225
+	}
226
+	if rate < 0 {
227
+		return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
228
+	}
229
+
230
+	return &blkiodev.ThrottleDevice{
231
+		Path: split[0],
232
+		Rate: uint64(rate),
233
+	}, nil
234
+}
235
+
213 236
 // ValidateLink validates that the specified string has a valid link format (containerName:alias).
214 237
 func ValidateLink(val string) (string, error) {
215 238
 	if _, _, err := parsers.ParseLink(val); err != nil {
216 239
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package opts
1
+
2
+import (
3
+	"fmt"
4
+
5
+	"github.com/docker/docker/pkg/blkiodev"
6
+)
7
+
8
+// ThrottledeviceOpt defines a map of ThrottleDevices
9
+type ThrottledeviceOpt struct {
10
+	values    []*blkiodev.ThrottleDevice
11
+	validator ValidatorThrottleFctType
12
+}
13
+
14
+// NewThrottledeviceOpt creates a new ThrottledeviceOpt
15
+func NewThrottledeviceOpt(validator ValidatorThrottleFctType) ThrottledeviceOpt {
16
+	values := []*blkiodev.ThrottleDevice{}
17
+	return ThrottledeviceOpt{
18
+		values:    values,
19
+		validator: validator,
20
+	}
21
+}
22
+
23
+// Set validates a ThrottleDevice and sets its name as a key in ThrottledeviceOpt
24
+func (opt *ThrottledeviceOpt) Set(val string) error {
25
+	var value *blkiodev.ThrottleDevice
26
+	if opt.validator != nil {
27
+		v, err := opt.validator(val)
28
+		if err != nil {
29
+			return err
30
+		}
31
+		value = v
32
+	}
33
+	(opt.values) = append((opt.values), value)
34
+	return nil
35
+}
36
+
37
+// String returns ThrottledeviceOpt values as a string.
38
+func (opt *ThrottledeviceOpt) String() string {
39
+	var out []string
40
+	for _, v := range opt.values {
41
+		out = append(out, v.String())
42
+	}
43
+
44
+	return fmt.Sprintf("%v", out)
45
+}
46
+
47
+// GetList returns a slice of pointers to ThrottleDevices.
48
+func (opt *ThrottledeviceOpt) GetList() []*blkiodev.ThrottleDevice {
49
+	var throttledevice []*blkiodev.ThrottleDevice
50
+	for _, v := range opt.values {
51
+		throttledevice = append(throttledevice, v)
52
+	}
53
+
54
+	return throttledevice
55
+}
... ...
@@ -13,3 +13,13 @@ type WeightDevice struct {
13 13
 func (w *WeightDevice) String() string {
14 14
 	return fmt.Sprintf("%s:%d", w.Path, w.Weight)
15 15
 }
16
+
17
+// ThrottleDevice is a structure that hold device:rate_per_second pair
18
+type ThrottleDevice struct {
19
+	Path string
20
+	Rate uint64
21
+}
22
+
23
+func (t *ThrottleDevice) String() string {
24
+	return fmt.Sprintf("%s:%d", t.Path, t.Rate)
25
+}
... ...
@@ -63,6 +63,12 @@ type cgroupBlkioInfo struct {
63 63
 
64 64
 	// Whether Block IO weight_device is supported or not
65 65
 	BlkioWeightDevice bool
66
+
67
+	// Whether Block IO read limit in bytes per second is supported or not
68
+	BlkioReadBpsDevice bool
69
+
70
+	// Whether Block IO write limit in bytes per second is supported or not
71
+	BlkioWriteBpsDevice bool
66 72
 }
67 73
 
68 74
 type cgroupCpusetInfo struct {
... ...
@@ -126,9 +126,21 @@ func checkCgroupBlkioInfo(quiet bool) cgroupBlkioInfo {
126 126
 	if !quiet && !weightDevice {
127 127
 		logrus.Warn("Your kernel does not support cgroup blkio weight_device")
128 128
 	}
129
+
130
+	readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
131
+	if !quiet && !readBpsDevice {
132
+		logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
133
+	}
134
+
135
+	writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
136
+	if !quiet && !writeBpsDevice {
137
+		logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
138
+	}
129 139
 	return cgroupBlkioInfo{
130
-		BlkioWeight:       weight,
131
-		BlkioWeightDevice: weightDevice,
140
+		BlkioWeight:         weight,
141
+		BlkioWeightDevice:   weightDevice,
142
+		BlkioReadBpsDevice:  readBpsDevice,
143
+		BlkioWriteBpsDevice: writeBpsDevice,
132 144
 	}
133 145
 }
134 146
 
... ...
@@ -171,20 +171,22 @@ type Resources struct {
171 171
 	CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
172 172
 
173 173
 	// Applicable to UNIX platforms
174
-	CgroupParent      string // Parent cgroup.
175
-	BlkioWeight       uint16 // Block IO weight (relative weight vs. other containers)
176
-	BlkioWeightDevice []*blkiodev.WeightDevice
177
-	CPUPeriod         int64            `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
178
-	CPUQuota          int64            `json:"CpuQuota"`  // CPU CFS (Completely Fair Scheduler) quota
179
-	CpusetCpus        string           // CpusetCpus 0-2, 0,1
180
-	CpusetMems        string           // CpusetMems 0-2, 0,1
181
-	Devices           []DeviceMapping  // List of devices to map inside the container
182
-	KernelMemory      int64            // Kernel memory limit (in bytes)
183
-	Memory            int64            // Memory limit (in bytes)
184
-	MemoryReservation int64            // Memory soft limit (in bytes)
185
-	MemorySwap        int64            // Total memory usage (memory + swap); set `-1` to disable swap
186
-	MemorySwappiness  *int64           // Tuning container memory swappiness behaviour
187
-	Ulimits           []*ulimit.Ulimit // List of ulimits to be set in the container
174
+	CgroupParent        string // Parent cgroup.
175
+	BlkioWeight         uint16 // Block IO weight (relative weight vs. other containers)
176
+	BlkioWeightDevice   []*blkiodev.WeightDevice
177
+	BlkioDeviceReadBps  []*blkiodev.ThrottleDevice
178
+	BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
179
+	CPUPeriod           int64            `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
180
+	CPUQuota            int64            `json:"CpuQuota"`  // CPU CFS (Completely Fair Scheduler) quota
181
+	CpusetCpus          string           // CpusetCpus 0-2, 0,1
182
+	CpusetMems          string           // CpusetMems 0-2, 0,1
183
+	Devices             []DeviceMapping  // List of devices to map inside the container
184
+	KernelMemory        int64            // Kernel memory limit (in bytes)
185
+	Memory              int64            // Memory limit (in bytes)
186
+	MemoryReservation   int64            // Memory soft limit (in bytes)
187
+	MemorySwap          int64            // Total memory usage (memory + swap); set `-1` to disable swap
188
+	MemorySwappiness    *int64           // Tuning container memory swappiness behaviour
189
+	Ulimits             []*ulimit.Ulimit // List of ulimits to be set in the container
188 190
 }
189 191
 
190 192
 // HostConfig the non-portable Config structure of a container.
... ...
@@ -53,6 +53,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
53 53
 		flVolumes           = opts.NewListOpts(nil)
54 54
 		flTmpfs             = opts.NewListOpts(nil)
55 55
 		flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice)
56
+		flDeviceReadBps     = opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice)
57
+		flDeviceWriteBps    = opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice)
56 58
 		flLinks             = opts.NewListOpts(opts.ValidateLink)
57 59
 		flEnv               = opts.NewListOpts(opts.ValidateEnv)
58 60
 		flLabels            = opts.NewListOpts(opts.ValidateEnv)
... ...
@@ -113,6 +115,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
113 113
 
114 114
 	cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
115 115
 	cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
116
+	cmd.Var(&flDeviceReadBps, []string{"-device-read-bps"}, "Limit read rate (bytes per second) from a device")
117
+	cmd.Var(&flDeviceWriteBps, []string{"-device-write-bps"}, "Limit write rate (bytes per second) to a device")
116 118
 	cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
117 119
 	cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory")
118 120
 	cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
... ...
@@ -338,21 +342,23 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
338 338
 	}
339 339
 
340 340
 	resources := Resources{
341
-		CgroupParent:      *flCgroupParent,
342
-		Memory:            flMemory,
343
-		MemoryReservation: MemoryReservation,
344
-		MemorySwap:        memorySwap,
345
-		MemorySwappiness:  flSwappiness,
346
-		KernelMemory:      KernelMemory,
347
-		CPUShares:         *flCPUShares,
348
-		CPUPeriod:         *flCPUPeriod,
349
-		CpusetCpus:        *flCpusetCpus,
350
-		CpusetMems:        *flCpusetMems,
351
-		CPUQuota:          *flCPUQuota,
352
-		BlkioWeight:       *flBlkioWeight,
353
-		BlkioWeightDevice: flBlkioWeightDevice.GetList(),
354
-		Ulimits:           flUlimits.GetList(),
355
-		Devices:           deviceMappings,
341
+		CgroupParent:        *flCgroupParent,
342
+		Memory:              flMemory,
343
+		MemoryReservation:   MemoryReservation,
344
+		MemorySwap:          memorySwap,
345
+		MemorySwappiness:    flSwappiness,
346
+		KernelMemory:        KernelMemory,
347
+		CPUShares:           *flCPUShares,
348
+		CPUPeriod:           *flCPUPeriod,
349
+		CpusetCpus:          *flCpusetCpus,
350
+		CpusetMems:          *flCpusetMems,
351
+		CPUQuota:            *flCPUQuota,
352
+		BlkioWeight:         *flBlkioWeight,
353
+		BlkioWeightDevice:   flBlkioWeightDevice.GetList(),
354
+		BlkioDeviceReadBps:  flDeviceReadBps.GetList(),
355
+		BlkioDeviceWriteBps: flDeviceWriteBps.GetList(),
356
+		Ulimits:             flUlimits.GetList(),
357
+		Devices:             deviceMappings,
356 358
 	}
357 359
 
358 360
 	config := &Config{