| # Copyright 2020 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Workaround for qemu-img bug on arm64 platforms with multiple cores. |
| |
| Runs qemu-img command with timeout and retries the command if it hangs. |
| |
| See: |
| crbug.com/1046861 QEMU is out of date; current version of qemu-img |
| is unstable |
| |
| https://bugs.launchpad.net/qemu/+bug/1805256 qemu-img hangs on |
| rcu_call_ready_event logic in Aarch64 when converting images |
| |
| TODO(crbug.com/1046861): Remove this workaround when the bug is fixed. |
| """ |
| |
| import logging |
| import subprocess |
| import tempfile |
| import time |
| |
| |
| # qemu-img p99 run time on Cavium ThunderX2 servers is 26 seconds. |
| # Using 2x the p99 time as the timeout. |
| QEMU_IMG_TIMEOUT_SEC = 52 |
| |
| |
| def _ExecQemuImgWithTimeout(command): |
| """Execute qemu-img command in subprocess with timeout. |
| |
| Returns: None if command timed out or return code if command completed. |
| """ |
| |
| logging.info('qemu-img starting') |
| command_output_file = tempfile.NamedTemporaryFile('w') |
| p = subprocess.Popen(command, stdout=command_output_file, |
| stderr=subprocess.STDOUT) |
| start_sec = time.time() |
| while p.poll() is None and time.time() - start_sec < QEMU_IMG_TIMEOUT_SEC: |
| time.sleep(1) |
| stop_sec = time.time() |
| logging.info('qemu-img duration: %f' % float(stop_sec - start_sec)) |
| |
| if p.poll() is None: |
| returncode = None |
| p.kill() |
| p.wait() |
| else: |
| returncode = p.returncode |
| |
| log_level = logging.WARN if returncode else logging.DEBUG |
| for line in open(command_output_file.name, 'r'): |
| logging.log(log_level, 'qemu-img stdout: ' + line.strip()) |
| |
| return returncode |
| |
| |
| def ExecQemuImgWithRetry(command): |
| """ Execute qemu-img command in subprocess with 2 retries. |
| |
| Raises CalledProcessError if command does not complete successfully. |
| """ |
| |
| tries = 0 |
| status = None |
| while status is None and tries <= 2: |
| tries += 1 |
| status = _ExecQemuImgWithTimeout(command) |
| |
| if status is None: |
| raise subprocess.CalledProcessError(-1, command) |
| if status: |
| raise subprocess.CalledProcessError(status, command) |