cirrus: fix off-by-one in cirrus_bitblt_rop_bkwd_transp_*_16
[qemu.git] / tests / qemu-iotests / 093
1 #!/usr/bin/env python
2 #
3 # Tests for IO throttling
4 #
5 # Copyright (C) 2015 Red Hat, Inc.
6 # Copyright (C) 2015-2016 Igalia, S.L.
7 #
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.
12 #
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.
17 #
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/>.
20 #
21
22 import iotests
23
24 nsec_per_sec = 1000000000
25
26 class ThrottleTestCase(iotests.QMPTestCase):
27     test_img = "null-aio://"
28     max_drives = 3
29
30     def blockstats(self, device):
31         result = self.vm.qmp("query-blockstats")
32         for r in result['return']:
33             if r['device'] == device:
34                 stat = r['stats']
35                 return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
36         raise Exception("Device not found for blockstats: %s" % device)
37
38     def setUp(self):
39         self.vm = iotests.VM()
40         for i in range(0, self.max_drives):
41             self.vm.add_drive(self.test_img)
42         self.vm.launch()
43
44     def tearDown(self):
45         self.vm.shutdown()
46
47     def configure_throttle(self, ndrives, params):
48         params['group'] = 'test'
49
50         # Set the I/O throttling parameters to all drives
51         for i in range(0, ndrives):
52             params['device'] = 'drive%d' % i
53             result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
54             self.assert_qmp(result, 'return', {})
55
56     def do_test_throttle(self, ndrives, seconds, params, first_drive = 0):
57         def check_limit(limit, num):
58             # IO throttling algorithm is discrete, allow 10% error so the test
59             # is more robust
60             return limit == 0 or \
61                    (num < seconds * limit * 1.1 / ndrives
62                    and num > seconds * limit * 0.9 / ndrives)
63
64         # Set vm clock to a known value
65         ns = seconds * nsec_per_sec
66         self.vm.qtest("clock_step %d" % ns)
67
68         # Submit enough requests so the throttling mechanism kicks
69         # in. The throttled requests won't be executed until we
70         # advance the virtual clock.
71         rq_size = 512
72         rd_nr = max(params['bps'] / rq_size / 2,
73                     params['bps_rd'] / rq_size,
74                     params['iops'] / 2,
75                     params['iops_rd'])
76         rd_nr *= seconds * 2
77         rd_nr /= ndrives
78         wr_nr = max(params['bps'] / rq_size / 2,
79                     params['bps_wr'] / rq_size,
80                     params['iops'] / 2,
81                     params['iops_wr'])
82         wr_nr *= seconds * 2
83         wr_nr /= ndrives
84
85         # Send I/O requests to all drives
86         for i in range(rd_nr):
87             for drive in range(0, ndrives):
88                 idx = first_drive + drive
89                 self.vm.hmp_qemu_io("drive%d" % idx, "aio_read %d %d" %
90                                     (i * rq_size, rq_size))
91
92         for i in range(wr_nr):
93             for drive in range(0, ndrives):
94                 idx = first_drive + drive
95                 self.vm.hmp_qemu_io("drive%d" % idx, "aio_write %d %d" %
96                                     (i * rq_size, rq_size))
97
98         # We'll store the I/O stats for each drive in these arrays
99         start_rd_bytes = [0] * ndrives
100         start_rd_iops  = [0] * ndrives
101         start_wr_bytes = [0] * ndrives
102         start_wr_iops  = [0] * ndrives
103         end_rd_bytes   = [0] * ndrives
104         end_rd_iops    = [0] * ndrives
105         end_wr_bytes   = [0] * ndrives
106         end_wr_iops    = [0] * ndrives
107
108         # Read the stats before advancing the clock
109         for i in range(0, ndrives):
110             idx = first_drive + i
111             start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
112                 start_wr_iops[i] = self.blockstats('drive%d' % idx)
113
114         self.vm.qtest("clock_step %d" % ns)
115
116         # Read the stats after advancing the clock
117         for i in range(0, ndrives):
118             idx = first_drive + i
119             end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
120                 end_wr_iops[i] = self.blockstats('drive%d' % idx)
121
122         # Check that the I/O is within the limits and evenly distributed
123         for i in range(0, ndrives):
124             rd_bytes = end_rd_bytes[i] - start_rd_bytes[i]
125             rd_iops = end_rd_iops[i] - start_rd_iops[i]
126             wr_bytes = end_wr_bytes[i] - start_wr_bytes[i]
127             wr_iops = end_wr_iops[i] - start_wr_iops[i]
128
129             self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
130             self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
131             self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
132             self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
133             self.assertTrue(check_limit(params['iops_rd'], rd_iops))
134             self.assertTrue(check_limit(params['iops_wr'], wr_iops))
135
136     # Connect N drives to a VM and test I/O in all of them
137     def test_all(self):
138         params = {"bps": 4096,
139                   "bps_rd": 4096,
140                   "bps_wr": 4096,
141                   "iops": 10,
142                   "iops_rd": 10,
143                   "iops_wr": 10,
144                  }
145         # Repeat the test with different numbers of drives
146         for ndrives in range(1, self.max_drives + 1):
147             # Pick each out of all possible params and test
148             for tk in params:
149                 limits = dict([(k, 0) for k in params])
150                 limits[tk] = params[tk] * ndrives
151                 self.configure_throttle(ndrives, limits)
152                 self.do_test_throttle(ndrives, 5, limits)
153
154     # Connect N drives to a VM and test I/O in just one of them a time
155     def test_one(self):
156         params = {"bps": 4096,
157                   "bps_rd": 4096,
158                   "bps_wr": 4096,
159                   "iops": 10,
160                   "iops_rd": 10,
161                   "iops_wr": 10,
162                  }
163         # Repeat the test for each one of the drives
164         for drive in range(0, self.max_drives):
165             # Pick each out of all possible params and test
166             for tk in params:
167                 limits = dict([(k, 0) for k in params])
168                 limits[tk] = params[tk] * self.max_drives
169                 self.configure_throttle(self.max_drives, limits)
170                 self.do_test_throttle(1, 5, limits, drive)
171
172     def test_burst(self):
173         params = {"bps": 4096,
174                   "bps_rd": 4096,
175                   "bps_wr": 4096,
176                   "iops": 10,
177                   "iops_rd": 10,
178                   "iops_wr": 10,
179                  }
180         ndrives = 1
181         # Pick each out of all possible params and test
182         for tk in params:
183             rate = params[tk] * ndrives
184             burst_rate = rate * 7
185             burst_length = 4
186
187             # Configure the throttling settings
188             settings = dict([(k, 0) for k in params])
189             settings[tk] = rate
190             settings['%s_max' % tk] = burst_rate
191             settings['%s_max_length' % tk] = burst_length
192             self.configure_throttle(ndrives, settings)
193
194             # Wait for the bucket to empty so we can do bursts
195             wait_ns = nsec_per_sec * burst_length * burst_rate / rate
196             self.vm.qtest("clock_step %d" % wait_ns)
197
198             # Test I/O at the max burst rate
199             limits = dict([(k, 0) for k in params])
200             limits[tk] = burst_rate
201             self.do_test_throttle(ndrives, burst_length, limits)
202
203             # Now test I/O at the normal rate
204             limits[tk] = rate
205             self.do_test_throttle(ndrives, 5, limits)
206
207 class ThrottleTestCoroutine(ThrottleTestCase):
208     test_img = "null-co://"
209
210 class ThrottleTestGroupNames(iotests.QMPTestCase):
211     test_img = "null-aio://"
212     max_drives = 3
213
214     def setUp(self):
215         self.vm = iotests.VM()
216         for i in range(0, self.max_drives):
217             self.vm.add_drive(self.test_img, "throttling.iops-total=100")
218         self.vm.launch()
219
220     def tearDown(self):
221         self.vm.shutdown()
222
223     def set_io_throttle(self, device, params):
224         params["device"] = device
225         result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
226         self.assert_qmp(result, 'return', {})
227
228     def verify_name(self, device, name):
229         result = self.vm.qmp("query-block")
230         for r in result["return"]:
231             if r["device"] == device:
232                 info = r["inserted"]
233                 if name:
234                     self.assertEqual(info["group"], name)
235                 else:
236                     self.assertFalse(info.has_key('group'))
237                 return
238
239         raise Exception("No group information found for '%s'" % device)
240
241     def test_group_naming(self):
242         params = {"bps": 0,
243                   "bps_rd": 0,
244                   "bps_wr": 0,
245                   "iops": 0,
246                   "iops_rd": 0,
247                   "iops_wr": 0}
248
249         # Check the drives added using the command line.
250         # The default throttling group name is the device name.
251         for i in range(self.max_drives):
252             devname = "drive%d" % i
253             self.verify_name(devname, devname)
254
255         # Clear throttling settings => the group name is gone.
256         for i in range(self.max_drives):
257             devname = "drive%d" % i
258             self.set_io_throttle(devname, params)
259             self.verify_name(devname, None)
260
261         # Set throttling settings using block_set_io_throttle and
262         # check the default group names.
263         params["iops"] = 10
264         for i in range(self.max_drives):
265             devname = "drive%d" % i
266             self.set_io_throttle(devname, params)
267             self.verify_name(devname, devname)
268
269         # Set a custom group name for each device
270         for i in range(3):
271             devname = "drive%d" % i
272             groupname = "group%d" % i
273             params['group'] = groupname
274             self.set_io_throttle(devname, params)
275             self.verify_name(devname, groupname)
276
277         # Put drive0 in group1 and check that all other devices remain
278         # unchanged
279         params['group'] = 'group1'
280         self.set_io_throttle('drive0', params)
281         self.verify_name('drive0', 'group1')
282         for i in range(1, self.max_drives):
283             devname = "drive%d" % i
284             groupname = "group%d" % i
285             self.verify_name(devname, groupname)
286
287         # Put drive0 in group2 and check that all other devices remain
288         # unchanged
289         params['group'] = 'group2'
290         self.set_io_throttle('drive0', params)
291         self.verify_name('drive0', 'group2')
292         for i in range(1, self.max_drives):
293             devname = "drive%d" % i
294             groupname = "group%d" % i
295             self.verify_name(devname, groupname)
296
297         # Clear throttling settings from drive0 check that all other
298         # devices remain unchanged
299         params["iops"] = 0
300         self.set_io_throttle('drive0', params)
301         self.verify_name('drive0', None)
302         for i in range(1, self.max_drives):
303             devname = "drive%d" % i
304             groupname = "group%d" % i
305             self.verify_name(devname, groupname)
306
307
308 if __name__ == '__main__':
309     iotests.main(supported_fmts=["raw"])