3 # Tests for IO throttling
5 # Copyright (C) 2015 Red Hat, Inc.
6 # Copyright (C) 2015 Igalia, S.L.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 class ThrottleTestCase(iotests
.QMPTestCase
):
25 test_img
= "null-aio://"
28 def blockstats(self
, device
):
29 result
= self
.vm
.qmp("query-blockstats")
30 for r
in result
['return']:
31 if r
['device'] == device
:
33 return stat
['rd_bytes'], stat
['rd_operations'], stat
['wr_bytes'], stat
['wr_operations']
34 raise Exception("Device not found for blockstats: %s" % device
)
37 self
.vm
= iotests
.VM()
38 for i
in range(0, self
.max_drives
):
39 self
.vm
.add_drive(self
.test_img
)
45 def do_test_throttle(self
, ndrives
, seconds
, params
):
46 def check_limit(limit
, num
):
47 # IO throttling algorithm is discrete, allow 10% error so the test
49 return limit
== 0 or \
50 (num
< seconds
* limit
* 1.1 / ndrives
51 and num
> seconds
* limit
* 0.9 / ndrives
)
53 nsec_per_sec
= 1000000000
55 params
['group'] = 'test'
57 # Set the I/O throttling parameters to all drives
58 for i
in range(0, ndrives
):
59 params
['device'] = 'drive%d' % i
60 result
= self
.vm
.qmp("block_set_io_throttle", conv_keys
=False, **params
)
61 self
.assert_qmp(result
, 'return', {})
63 # Set vm clock to a known value
64 ns
= seconds
* nsec_per_sec
65 self
.vm
.qtest("clock_step %d" % ns
)
67 # Submit enough requests. They will drain bps_max and iops_max, but the
68 # rest requests won't get executed until we advance the virtual clock
69 # with qtest interface
71 rd_nr
= max(params
['bps'] / rq_size
/ 2,
72 params
['bps_rd'] / rq_size
,
77 wr_nr
= max(params
['bps'] / rq_size
/ 2,
78 params
['bps_wr'] / rq_size
,
84 # Send I/O requests to all drives
85 for i
in range(rd_nr
):
86 for drive
in range(0, ndrives
):
87 self
.vm
.hmp_qemu_io("drive%d" % drive
, "aio_read %d %d" %
88 (i
* rq_size
, rq_size
))
90 for i
in range(wr_nr
):
91 for drive
in range(0, ndrives
):
92 self
.vm
.hmp_qemu_io("drive%d" % drive
, "aio_write %d %d" %
93 (i
* rq_size
, rq_size
))
95 # We'll store the I/O stats for each drive in these arrays
96 start_rd_bytes
= [0] * ndrives
97 start_rd_iops
= [0] * ndrives
98 start_wr_bytes
= [0] * ndrives
99 start_wr_iops
= [0] * ndrives
100 end_rd_bytes
= [0] * ndrives
101 end_rd_iops
= [0] * ndrives
102 end_wr_bytes
= [0] * ndrives
103 end_wr_iops
= [0] * ndrives
105 # Read the stats before advancing the clock
106 for i
in range(0, ndrives
):
107 start_rd_bytes
[i
], start_rd_iops
[i
], start_wr_bytes
[i
], \
108 start_wr_iops
[i
] = self
.blockstats('drive%d' % i
)
110 self
.vm
.qtest("clock_step %d" % ns
)
112 # Read the stats after advancing the clock
113 for i
in range(0, ndrives
):
114 end_rd_bytes
[i
], end_rd_iops
[i
], end_wr_bytes
[i
], \
115 end_wr_iops
[i
] = self
.blockstats('drive%d' % i
)
117 # Check that the I/O is within the limits and evenly distributed
118 for i
in range(0, ndrives
):
119 rd_bytes
= end_rd_bytes
[i
] - start_rd_bytes
[i
]
120 rd_iops
= end_rd_iops
[i
] - start_rd_iops
[i
]
121 wr_bytes
= end_wr_bytes
[i
] - start_wr_bytes
[i
]
122 wr_iops
= end_wr_iops
[i
] - start_wr_iops
[i
]
124 self
.assertTrue(check_limit(params
['bps'], rd_bytes
+ wr_bytes
))
125 self
.assertTrue(check_limit(params
['bps_rd'], rd_bytes
))
126 self
.assertTrue(check_limit(params
['bps_wr'], wr_bytes
))
127 self
.assertTrue(check_limit(params
['iops'], rd_iops
+ wr_iops
))
128 self
.assertTrue(check_limit(params
['iops_rd'], rd_iops
))
129 self
.assertTrue(check_limit(params
['iops_wr'], wr_iops
))
132 params
= {"bps": 4096,
139 # Repeat the test with different numbers of drives
140 for ndrives
in range(1, self
.max_drives
+ 1):
141 # Pick each out of all possible params and test
143 limits
= dict([(k
, 0) for k
in params
])
144 limits
[tk
] = params
[tk
] * ndrives
145 self
.do_test_throttle(ndrives
, 5, limits
)
147 class ThrottleTestCoroutine(ThrottleTestCase
):
148 test_img
= "null-co://"
150 if __name__
== '__main__':
151 iotests
.main(supported_fmts
=["raw"])