cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

219 (9243B)


      1#!/usr/bin/env python3
      2# group: rw
      3#
      4# Copyright (C) 2018 Red Hat, Inc.
      5#
      6# This program is free software; you can redistribute it and/or modify
      7# it under the terms of the GNU General Public License as published by
      8# the Free Software Foundation; either version 2 of the License, or
      9# (at your option) any later version.
     10#
     11# This program is distributed in the hope that it will be useful,
     12# but WITHOUT ANY WARRANTY; without even the implied warranty of
     13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14# GNU General Public License for more details.
     15#
     16# You should have received a copy of the GNU General Public License
     17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18#
     19# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
     20#
     21# Check using the job-* QMP commands with block jobs
     22
     23import iotests
     24
     25iotests.script_initialize(supported_fmts=['qcow2'])
     26
     27img_size = 4 * 1024 * 1024
     28
     29def pause_wait(vm, job_id):
     30    with iotests.Timeout(3, "Timeout waiting for job to pause"):
     31        while True:
     32            result = vm.qmp('query-jobs')
     33            for job in result['return']:
     34                if job['id'] == job_id and job['status'] in ['paused', 'standby']:
     35                    return job
     36
     37# Test that block-job-pause/resume and job-pause/resume can be mixed
     38def test_pause_resume(vm):
     39    for pause_cmd, pause_arg in [('block-job-pause', 'device'),
     40                                 ('job-pause', 'id')]:
     41        for resume_cmd, resume_arg in [('block-job-resume', 'device'),
     42                                       ('job-resume', 'id')]:
     43            iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
     44
     45            iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
     46            pause_wait(vm, 'job0')
     47            iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
     48            result = vm.qmp('query-jobs')
     49            iotests.log(result)
     50
     51            old_progress = result['return'][0]['current-progress']
     52            total_progress = result['return'][0]['total-progress']
     53
     54            iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
     55            iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
     56            if old_progress < total_progress:
     57                # Wait for the job to advance
     58                while result['return'][0]['current-progress'] == old_progress:
     59                    result = vm.qmp('query-jobs')
     60                iotests.log(result)
     61            else:
     62                # Already reached the end, so the job cannot advance
     63                # any further; therefore, the query-jobs result can be
     64                # logged immediately
     65                iotests.log(vm.qmp('query-jobs'))
     66
     67def test_job_lifecycle(vm, job, job_args, has_ready=False, is_mirror=False):
     68    global img_size
     69
     70    iotests.log('')
     71    iotests.log('')
     72    iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
     73                (job,
     74                 job_args.get('auto-finalize', True),
     75                 job_args.get('auto-dismiss', True)))
     76    iotests.log(vm.qmp(job, job_id='job0', **job_args))
     77
     78    # Depending on the storage, the first request may or may not have completed
     79    # yet (and the total progress may not have been fully determined yet), so
     80    # filter out the progress. Later query-job calls don't need the filtering
     81    # because the progress is made deterministic by the block job speed
     82    result = vm.qmp('query-jobs')
     83    for j in result['return']:
     84        j['current-progress'] = 'FILTERED'
     85        j['total-progress'] = 'FILTERED'
     86    iotests.log(result)
     87
     88    # undefined -> created -> running
     89    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
     90    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
     91
     92    # Wait for total-progress to stabilize
     93    while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size:
     94        pass
     95
     96    # RUNNING state:
     97    # pause/resume should work, complete/finalize/dismiss should error out
     98    iotests.log('')
     99    iotests.log('Pause/resume in RUNNING')
    100    test_pause_resume(vm)
    101
    102    iotests.log(vm.qmp('job-complete', id='job0'))
    103    iotests.log(vm.qmp('job-finalize', id='job0'))
    104    iotests.log(vm.qmp('job-dismiss', id='job0'))
    105
    106    iotests.log(vm.qmp('block-job-complete', device='job0'))
    107    iotests.log(vm.qmp('block-job-finalize', id='job0'))
    108    iotests.log(vm.qmp('block-job-dismiss', id='job0'))
    109
    110    # Let the job complete (or transition to READY if it supports that)
    111    iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
    112    if has_ready:
    113        iotests.log('')
    114        iotests.log('Waiting for READY state...')
    115        vm.event_wait('BLOCK_JOB_READY')
    116        iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    117        iotests.log(vm.qmp('query-jobs'))
    118
    119        # READY state:
    120        # pause/resume/complete should work, finalize/dismiss should error out
    121        iotests.log('')
    122        iotests.log('Pause/resume in READY')
    123        test_pause_resume(vm)
    124
    125        iotests.log(vm.qmp('job-finalize', id='job0'))
    126        iotests.log(vm.qmp('job-dismiss', id='job0'))
    127
    128        iotests.log(vm.qmp('block-job-finalize', id='job0'))
    129        iotests.log(vm.qmp('block-job-dismiss', id='job0'))
    130
    131        # Transition to WAITING
    132        iotests.log(vm.qmp('job-complete', id='job0'))
    133
    134    # Move to WAITING and PENDING state
    135    iotests.log('')
    136    iotests.log('Waiting for PENDING state...')
    137    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    138    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    139    if is_mirror:
    140        iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    141        iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    142
    143    if not job_args.get('auto-finalize', True):
    144        # PENDING state:
    145        # finalize should work, pause/complete/dismiss should error out
    146        iotests.log(vm.qmp('query-jobs'))
    147
    148        iotests.log(vm.qmp('job-pause', id='job0'))
    149        iotests.log(vm.qmp('job-complete', id='job0'))
    150        iotests.log(vm.qmp('job-dismiss', id='job0'))
    151
    152        iotests.log(vm.qmp('block-job-pause', device='job0'))
    153        iotests.log(vm.qmp('block-job-complete', device='job0'))
    154        iotests.log(vm.qmp('block-job-dismiss', id='job0'))
    155
    156        # Transition to CONCLUDED
    157        iotests.log(vm.qmp('job-finalize', id='job0'))
    158
    159
    160    # Move to CONCLUDED state
    161    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    162
    163    if not job_args.get('auto-dismiss', True):
    164        # CONCLUDED state:
    165        # dismiss should work, pause/complete/finalize should error out
    166        iotests.log(vm.qmp('query-jobs'))
    167
    168        iotests.log(vm.qmp('job-pause', id='job0'))
    169        iotests.log(vm.qmp('job-complete', id='job0'))
    170        iotests.log(vm.qmp('job-finalize', id='job0'))
    171
    172        iotests.log(vm.qmp('block-job-pause', device='job0'))
    173        iotests.log(vm.qmp('block-job-complete', device='job0'))
    174        iotests.log(vm.qmp('block-job-finalize', id='job0'))
    175
    176        # Transition to NULL
    177        iotests.log(vm.qmp('job-dismiss', id='job0'))
    178
    179    # Move to NULL state
    180    iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
    181    iotests.log(vm.qmp('query-jobs'))
    182
    183
    184with iotests.FilePath('disk.img') as disk_path, \
    185     iotests.FilePath('copy.img') as copy_path, \
    186     iotests.VM() as vm:
    187
    188    iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size))
    189    iotests.qemu_io('-c', 'write 0 %i' % (img_size),
    190                    '-f', iotests.imgfmt, disk_path)
    191
    192    iotests.log('Launching VM...')
    193    vm.add_blockdev(vm.qmp_to_opts({
    194        'driver': iotests.imgfmt,
    195        'node-name': 'drive0-node',
    196        'file': {
    197            'driver': 'file',
    198            'filename': disk_path,
    199        },
    200    }))
    201    vm.launch()
    202
    203    # In order to keep things deterministic (especially progress in query-job,
    204    # but related to this also automatic state transitions like job
    205    # completion), but still get pause points often enough to avoid making this
    206    # test very slow, it's important to have the right ratio between speed and
    207    # copy-chunk-size.
    208    #
    209    # Chose 64k copy-chunk-size both for mirror (by buf_size) and backup (by
    210    # x-max-chunk). The slice time, i.e. the granularity of the rate limiting
    211    # is 100ms. With a speed of 256k per second, we can get four pause points
    212    # per second. This gives us 250ms per iteration, which should be enough to
    213    # stay deterministic.
    214
    215    test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
    216        'device': 'drive0-node',
    217        'target': copy_path,
    218        'sync': 'full',
    219        'speed': 262144,
    220        'buf_size': 65536,
    221    })
    222
    223    for auto_finalize in [True, False]:
    224        for auto_dismiss in [True, False]:
    225            test_job_lifecycle(vm, 'drive-backup', is_mirror=True, job_args={
    226                'device': 'drive0-node',
    227                'target': copy_path,
    228                'sync': 'full',
    229                'speed': 262144,
    230                'x-perf': {'max-chunk': 65536},
    231                'auto-finalize': auto_finalize,
    232                'auto-dismiss': auto_dismiss,
    233            })
    234
    235    vm.shutdown()