xref: /qemu/migration/block-active.c (revision 6b8f40c61bbfef1abe77eeb9c716ec642927c12c)
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