/* * Block activation tracking for migration purpose * * SPDX-License-Identifier: GPL-2.0-or-later * * Copyright (C) 2024 Red Hat, Inc. */ #include "qemu/osdep.h" #include "block/block.h" #include "qapi/error.h" #include "migration/migration.h" #include "qemu/error-report.h" #include "trace.h" /* * Migration-only cache to remember the block layer activation status. * Protected by BQL. * * We need this because.. * * - Migration can fail after block devices are invalidated (during * switchover phase). When that happens, we need to be able to recover * the block drive status by re-activating them. * * - Currently bdrv_inactivate_all() is not safe to be invoked on top of * invalidated drives (even if bdrv_activate_all() is actually safe to be * called any time!). It means remembering this could help migration to * make sure it won't invalidate twice in a row, crashing QEMU. It can * happen when we migrate a PAUSED VM from host1 to host2, then migrate * again to host3 without starting it. TODO: a cleaner solution is to * allow safe invoke of bdrv_inactivate_all() at anytime, like * bdrv_activate_all(). * * For freshly started QEMU, the flag is initialized to TRUE reflecting the * scenario where QEMU owns block device ownerships. * * For incoming QEMU taking a migration stream, the flag is initialized to * FALSE reflecting that the incoming side doesn't own the block devices, * not until switchover happens. */ static bool migration_block_active; /* Setup the disk activation status */ void migration_block_active_setup(bool active) { migration_block_active = active; } bool migration_block_activate(Error **errp) { ERRP_GUARD(); assert(bql_locked()); if (migration_block_active) { trace_migration_block_activation("active-skipped"); return true; } trace_migration_block_activation("active"); bdrv_activate_all(errp); if (*errp) { error_report_err(error_copy(*errp)); return false; } migration_block_active = true; return true; } bool migration_block_inactivate(void) { int ret; assert(bql_locked()); if (!migration_block_active) { trace_migration_block_activation("inactive-skipped"); return true; } trace_migration_block_activation("inactive"); ret = bdrv_inactivate_all(); if (ret) { error_report("%s: bdrv_inactivate_all() failed: %d", __func__, ret); return false; } migration_block_active = false; return true; }