1 /* 2 * Block activation tracking for migration purpose 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * Copyright (C) 2024 Red Hat, Inc. 7 */ 8 #include "qemu/osdep.h" 9 #include "block/block.h" 10 #include "qapi/error.h" 11 #include "migration/migration.h" 12 #include "qemu/error-report.h" 13 #include "trace.h" 14 15 /* 16 * Migration-only cache to remember the block layer activation status. 17 * Protected by BQL. 18 * 19 * We need this because.. 20 * 21 * - Migration can fail after block devices are invalidated (during 22 * switchover phase). When that happens, we need to be able to recover 23 * the block drive status by re-activating them. 24 * 25 * - Currently bdrv_inactivate_all() is not safe to be invoked on top of 26 * invalidated drives (even if bdrv_activate_all() is actually safe to be 27 * called any time!). It means remembering this could help migration to 28 * make sure it won't invalidate twice in a row, crashing QEMU. It can 29 * happen when we migrate a PAUSED VM from host1 to host2, then migrate 30 * again to host3 without starting it. TODO: a cleaner solution is to 31 * allow safe invoke of bdrv_inactivate_all() at anytime, like 32 * bdrv_activate_all(). 33 * 34 * For freshly started QEMU, the flag is initialized to TRUE reflecting the 35 * scenario where QEMU owns block device ownerships. 36 * 37 * For incoming QEMU taking a migration stream, the flag is initialized to 38 * FALSE reflecting that the incoming side doesn't own the block devices, 39 * not until switchover happens. 40 */ 41 static bool migration_block_active; 42 43 /* Setup the disk activation status */ 44 void migration_block_active_setup(bool active) 45 { 46 migration_block_active = active; 47 } 48 49 bool migration_block_activate(Error **errp) 50 { 51 ERRP_GUARD(); 52 53 assert(bql_locked()); 54 55 if (migration_block_active) { 56 trace_migration_block_activation("active-skipped"); 57 return true; 58 } 59 60 trace_migration_block_activation("active"); 61 62 bdrv_activate_all(errp); 63 if (*errp) { 64 error_report_err(error_copy(*errp)); 65 return false; 66 } 67 68 migration_block_active = true; 69 return true; 70 } 71 72 bool migration_block_inactivate(void) 73 { 74 int ret; 75 76 assert(bql_locked()); 77 78 if (!migration_block_active) { 79 trace_migration_block_activation("inactive-skipped"); 80 return true; 81 } 82 83 trace_migration_block_activation("inactive"); 84 85 ret = bdrv_inactivate_all(); 86 if (ret) { 87 error_report("%s: bdrv_inactivate_all() failed: %d", 88 __func__, ret); 89 return false; 90 } 91 92 migration_block_active = false; 93 return true; 94 } 95