Skip to the content.

qemu 存在哪些状态控制

  1. hmp : stop / cont
  2. snapshot

snapshot_blkdev snapshot_blkdev_internal snapshot_delete_blkdev_internal

  1. savevm / loadvm

  2. migration

    • cpr 优化
    • file / socket

严格来说,接近“操作可恢复整机状态”的还有:

下面这些看起来类似,但不是完整可恢复状态:

最核心的判断标准是:

整机状态 = runstate + CPU + RAM + device state

QEMU 的 migration/savevm 核心主要解决前四项;后三项分别由内部磁盘快照、共享存储/管理层、CPR FD 保留以及重新创建兼容命令行来补齐。

QEMU 的 RunState

qapi/run-state.json 中

  { 'enum': 'RunState',
    'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused',
              'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm',
              'running', 'save-vm', 'shutdown', 'suspended', 'watchdog',
              'guest-panicked', 'colo' ] }

可以自动生成这些东西:

typedef enum RunState {
    RUN_STATE_DEBUG,
    RUN_STATE_INMIGRATE,
    RUN_STATE_INTERNAL_ERROR,
    RUN_STATE_IO_ERROR,
    RUN_STATE_PAUSED,
    RUN_STATE_POSTMIGRATE,
    RUN_STATE_PRELAUNCH,
    RUN_STATE_FINISH_MIGRATE,
    RUN_STATE_RESTORE_VM,
    RUN_STATE_RUNNING,
    RUN_STATE_SAVE_VM,
    RUN_STATE_SHUTDOWN,
    RUN_STATE_SUSPENDED,
    RUN_STATE_WATCHDOG,
    RUN_STATE_GUEST_PANICKED,
    RUN_STATE_COLO,
    RUN_STATE__MAX,
} RunState;
  1. 常规生命周期状态

状态 含义 ━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ prelaunch 启动后尚未运行,典型情况是使用了 -S ─────────── ────────────────────────────────────────────────────── running Guest 正在正常运行 ─────────── ────────────────────────────────────────────────────── paused 被 QMP/HMP stop 命令暂停 ─────────── ────────────────────────────────────────────────────── suspended Guest 进入 ACPI S3 suspend ─────────── ────────────────────────────────────────────────────── shutdown Guest 已关机,但因为 -no-shutdown,QEMU 进程没有退出

  1. 调试及异常停止状态

状态 含义 ━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ debug 因 GDB 调试、断点或单步而停止 ──────────────── ────────────────────────────────────────── internal-error QEMU/KVM 内部错误导致 Guest 无法继续执行 ──────────────── ────────────────────────────────────────── io-error 块设备 I/O 失败,设备配置为遇错暂停 ──────────────── ────────────────────────────────────────── watchdog Watchdog 触发,配置动作为暂停 ──────────────── ────────────────────────────────────────── guest-panicked Guest OS 报告 panic,配置动作为暂停

  1. 迁移、快照状态

状态 含义 ━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ inmigrate 目标端正在等待或接收 incoming migration ──────────────── ───────────────────────────────────────── finish-migrate 源端暂停 Guest,完成迁移最后阶段 ──────────────── ───────────────────────────────────────── postmigrate 源端迁移成功后保持暂停 ──────────────── ───────────────────────────────────────── save-vm 暂停 Guest,保存 VM state/snapshot ──────────────── ───────────────────────────────────────── restore-vm 暂停 Guest,恢复 VM state/snapshot ──────────────── ───────────────────────────────────────── colo COLO checkpoint 保存或恢复状态

QEMU 显式维护允许的迁移矩阵,见 system/runstate.c:82。

例如:

PRELAUNCH → RUNNING RUNNING → PAUSED RUNNING → IO_ERROR RUNNING → FINISH_MIGRATE PAUSED → RUNNING FINISH_MIGRATE → POSTMIGRATE

说明下最经典的状态转移过程:

soruce 端:

runstate_set current_run_state 6 (prelaunch) new_state 9 (running)

runstate_set current_run_state 9 (running) new_state 7 (finish-migrate)
runstate_set current_run_state 7 (finish-migrate) new_state 5 (postmigrate)

(SIGTERM)
qemu-system-x86_64: terminating on signal 15 from pid 4147494 (/home/martins3/.nix-profile/bin/python3)

target 端:

runstate_set current_run_state 1 (inmigrate) new_state 9 (running)

(hmp 发送 q)
runstate_set current_run_state 9 (running) new_state 11 (shutdown)

热迁移的基本流程

基本流程

进行 iteration 只有一个 thread 的,也就是

嵌入状态转换

将 notification 和状态转换嵌入之后

typedef enum PrecopyNotifyReason {
    PRECOPY_NOTIFY_SETUP = 0,
    PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC = 1,
    PRECOPY_NOTIFY_AFTER_BITMAP_SYNC = 2,
    PRECOPY_NOTIFY_COMPLETE = 3,
    PRECOPY_NOTIFY_CLEANUP = 4,
    PRECOPY_NOTIFY_MAX = 5,
} PrecopyNotifyReason;
typedef enum MigrationStatus {
    MIGRATION_STATUS_NONE,
    MIGRATION_STATUS_SETUP,
    MIGRATION_STATUS_CANCELLING,
    MIGRATION_STATUS_CANCELLED,
    MIGRATION_STATUS_ACTIVE,
    MIGRATION_STATUS_POSTCOPY_DEVICE,
    MIGRATION_STATUS_POSTCOPY_ACTIVE,
    MIGRATION_STATUS_POSTCOPY_PAUSED,
    MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP,
    MIGRATION_STATUS_POSTCOPY_RECOVER,
    MIGRATION_STATUS_COMPLETED,
    MIGRATION_STATUS_FAILING,
    MIGRATION_STATUS_FAILED,
    MIGRATION_STATUS_COLO,
    MIGRATION_STATUS_PRE_SWITCHOVER,
    MIGRATION_STATUS_DEVICE,
    MIGRATION_STATUS_WAIT_UNPLUG,
    MIGRATION_STATUS__MAX,
} MigrationStatus;

将 notification 和状态转换 (仅 precopy) 嵌入调用链后:

所以,可以看到 DEVICE 状态就是 switchover 状态了。

核心流程

输入:

彻底完成 precopy

那么中间发送资源的时间在什么地方?

应该观察下这个就可以了: br qemu_savevm_state br qemu_loadvm_state_main (<—- 原来)

migration_switchover_start 的东西

void migration_bitmap_sync_precopy(bool last_stage)
{
    Error *local_err = NULL;
    assert(ram_state);

    /*
     * The current notifier usage is just an optimization to migration, so we
     * don't stop the normal migration process in the error case.
     */
    if (precopy_notify(PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC, &local_err)) {
        error_report_err(local_err);
        local_err = NULL;
    }

    migration_bitmap_sync(ram_state, last_stage);

    if (precopy_notify(PRECOPY_NOTIFY_AFTER_BITMAP_SYNC, &local_err)) {
        error_report_err(local_err);
    }
}

switchover 不是一个点,而是一个阶段

Switchover 就是热迁移的”收尾/切换”阶段——从源端把执行权切到目的端的那一步。它不是迁移状态机里的一个正式状态,而是 QEMU 内部对这段流程的称呼 (代码见 migration/migration.c:migration_switchover_start())。

以默认的 precopy 为例,整个热迁移分两个阶段:

也就是说,switchover 期间 guest 是停止的,这段时间就是你测到的迁移 downtime。

状态机上的位置是:active → (pre-switchover) → device → completed。其中:

另外 postcopy 模式下也有 switchover:postcopy-active 期间可以决定切回收尾流程(postcopy → pre-switchover → device),把目的端缺的页拉齐后完成切换。

- __clone3
  - start_thread
    - qemu_thread_start
      - migration_thread
        - migration_iteration_run
          - migration_completion
            - migration_completion_precopy
              - migration_switchover_start
                - migration_switchover_prepare

iter 和 non-iter 的区别

可以观察到:

这两个名字描述的是设备状态在迁移过程中能不能在 VM 还运行时进行多轮迭代传输。

iterable(可迭代的)

指那些注册了 SaveVMHandlers 并且带 save_setup / save_live_iterate / save_complete 回调的设备状态。典型例子:

这些状态的特点是:

non-iterable(不可迭代的)

指普通的 VMState 设备状态,没有 save_setup,只在 VM 停下来之后一次性保存。典型例子:

qemu_savevm_state_non_iterable() 里会先 cpu_synchronize_all_states(),然后遍历所有 non-iterable 的 SaveStateEntry, 调用 vmstate_save() 把它们写到迁移流 。

整体调用顺序

在 precopy 迁移里,大致流程是:

  1. qemu_savevm_state_do_setup()
    • non_iterable_early():先保存标记了 early_setup 的 VMState;
    • setup():调用 iterable 设备的 save_setup()。
  2. 迁移线程循环调用 qemu_savevm_state_iterate(),让 iterable 设备反复 save_live_iterate()。
  3. 决定 switchover 后,源 VM 停止。
  4. qemu_savevm_state_complete_precopy():
    • 先 complete_precopy_iterable():停止 VM 后,对 iterable 设备做最后一轮 save_complete();
    • 再 non_iterable():一次性保存 CPU 等不可迭代状态。

所以简单说:

如何区分

判定逻辑在 migration/savevm.c 里:

/* Is a save state entry iterable (e.g. RAM)? */
static bool qemu_savevm_se_iterable(SaveStateEntry *se)
{
    return se->ops && se->ops->save_setup;
}

Iterabl

Non-iterable(一把梭):qemu_savevm_state_non_iterable(savevm.c:1740)在停机后对所有 entry 调 vmstate_save,发 QEMU_VM_SECTION_FULL,一次存完

所以两种情况:

  1. 注册了 SaveVMHandlers 但只提供 save_state/load_state(老式 register_savevm)→ 仍然是 non-iterable。vmstate_save 里会走 ops->save_state 分支 (savevm.c:1069、1086-1087),在停机阶段一次性保存。
  2. 没注册 SaveVMHandlers,用 VMStateDescription(绝大多数设备,经 vmstate_register/DCATEGORY)→ se->ops == NULL,自然也是 non-iterable,走 vmstate_save_vmsd。

也就是说,真正的 iter 设备必须注册带 save_setup + save_live_iterate + save_live_complete_precopy 这一整套 live 回调的 handlers——RAM 是典型,还 有 block、VFIO、部分 virtio 设备等。只注册了 SaveVMHandlers 但没提供 live 回调的,语义上还是 non-iterable。

注册了 SaveVMHandlers 但只提供 save_state/load_state 的情况

一般来说,就是 se->ops 来判断了就可以了,但是存在一些特殊设备

  1. slirp 用户态网络(net/slirp.c:427)
  static SaveVMHandlers savevm_slirp_state = {
      .save_state = net_slirp_state_save,
      .load_state = net_slirp_state_load,
  };
  ...
  register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY,
                       slirp_state_version(), &savevm_slirp_state, s->slirp);

注意这里虽然用的注册函数名叫 register_savevm_live,但 handlers 里没有 save_setup/save_live_iterate/save_complete 任何一个 live 回调,所以按 qemu_savevm_se_iterable() 的判定(se->ops && se->ops->save_setup)它就是 non-iterable——停机后在 qemu_savevm_state_non_iterable 里通过 vmstate_save 的 ops->save_state 分支一次性存完。

  1. s390x TOD 时钟(hw/s390x/tod.c:106)
  static SaveVMHandlers savevm_tod = {
      .save_state = s390_tod_save,
      .load_state = s390_tod_load,
  };

  static void s390_tod_realize(DeviceState *dev, Error **errp)
  {
      S390TODState *td = S390_TOD(dev);

      /* Legacy migration interface */
      register_savevm_live("todclock", 0, 1, &savevm_tod, td);
  }

kimi 的分析有一定的道理的:

有些状态本质上就不适合 VMState

VMState 是”静态描述 struct 字段 → 自动生成序列化”的模型,前提是状态能用 QEMU 的 C struct 字段描述清楚。但有些设备做不到:

一个小小的实验

给 trace_vmstate_downtime_save 挂上 trace ,一共两个位置,结果应该是很清晰了:

vmstate_downtime_save type=iterable idstr=ram instance_id=0 downtime=570
vmstate_downtime_save type=iterable idstr=dirty-bitmap instance_id=0 downtime=0

vmstate_downtime_save type=non-iterable idstr=apic instance_id=0 downtime=11
vmstate_downtime_save type=non-iterable idstr=apic instance_id=1 downtime=7
vmstate_downtime_save type=non-iterable idstr=0000:00:0b.0/pcie-root-port instance_id=0 downtime=14
vmstate_downtime_save type=non-iterable idstr=timer instance_id=0 downtime=6
vmstate_downtime_save type=non-iterable idstr=slirp instance_id=0 downtime=4
vmstate_downtime_save type=non-iterable idstr=ram instance_id=0 downtime=0
vmstate_downtime_save type=non-iterable idstr=dirty-bitmap instance_id=0 downtime=0
vmstate_downtime_save type=non-iterable idstr=cpu_common instance_id=0 downtime=3
vmstate_downtime_save type=non-iterable idstr=cpu instance_id=0 downtime=72
vmstate_downtime_save type=non-iterable idstr=kvm-tpr-opt instance_id=0 downtime=7
vmstate_downtime_save type=non-iterable idstr=cpu_common instance_id=1 downtime=1
vmstate_downtime_save type=non-iterable idstr=cpu instance_id=1 downtime=56
vmstate_downtime_save type=non-iterable idstr=kvmclock instance_id=0 downtime=3
vmstate_downtime_save type=non-iterable idstr=0000:00:00.0/I440FX instance_id=0 downtime=3
vmstate_downtime_save type=non-iterable idstr=PCIHost instance_id=0 downtime=2
vmstate_downtime_save type=non-iterable idstr=PCIBUS instance_id=0 downtime=3
vmstate_downtime_save type=non-iterable idstr=pflash_cfi01 instance_id=0 downtime=2
vmstate_downtime_save type=non-iterable idstr=pflash_cfi01 instance_id=1 downtime=2
vmstate_downtime_save type=non-iterable idstr=fw_cfg instance_id=0 downtime=5
vmstate_downtime_save type=non-iterable idstr=dma instance_id=0 downtime=7
vmstate_downtime_save type=non-iterable idstr=dma instance_id=1 downtime=6
vmstate_downtime_save type=non-iterable idstr=mc146818rtc instance_id=0 downtime=8
vmstate_downtime_save type=non-iterable idstr=0000:00:01.1/ide instance_id=0 downtime=28
vmstate_downtime_save type=non-iterable idstr=i2c_bus instance_id=0 downtime=2
vmstate_downtime_save type=non-iterable idstr=0000:00:01.3/piix4_pm instance_id=0 downtime=241
vmstate_downtime_save type=non-iterable idstr=0000:00:01.0/PIIX3 instance_id=0 downtime=4
vmstate_downtime_save type=non-iterable idstr=i8259 instance_id=0 downtime=7
vmstate_downtime_save type=non-iterable idstr=i8259 instance_id=1 downtime=4
vmstate_downtime_save type=non-iterable idstr=ioapic instance_id=0 downtime=3
vmstate_downtime_save type=non-iterable idstr=i8254 instance_id=0 downtime=6
vmstate_downtime_save type=non-iterable idstr=pcspk instance_id=0 downtime=2
vmstate_downtime_save type=non-iterable idstr=serial instance_id=0 downtime=6
vmstate_downtime_save type=non-iterable idstr=fdc instance_id=0 downtime=15
vmstate_downtime_save type=non-iterable idstr=ps2kbd instance_id=0 downtime=4
vmstate_downtime_save type=non-iterable idstr=ps2mouse instance_id=0 downtime=4
vmstate_downtime_save type=non-iterable idstr=pckbd instance_id=0 downtime=4
vmstate_downtime_save type=non-iterable idstr=vmmouse instance_id=0 downtime=18
vmstate_downtime_save type=non-iterable idstr=port92 instance_id=0 downtime=1
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=0 downtime=1
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=1 downtime=0
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=2 downtime=1
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=3 downtime=0
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=4 downtime=1
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=5 downtime=0
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=6 downtime=0
vmstate_downtime_save type=non-iterable idstr=smbus-eeprom instance_id=7 downtime=0
vmstate_downtime_save type=non-iterable idstr=0000:00:02.0/virtio-scsi instance_id=0 downtime=290
vmstate_downtime_save type=non-iterable idstr=0000:00:02.0/0:1:0/scsi-disk instance_id=0 downtime=7
vmstate_downtime_save type=non-iterable idstr=0000:00:02.0/0:2:0/scsi-disk instance_id=0 downtime=6
vmstate_downtime_save type=non-iterable idstr=0000:00:03.0/virtio-blk instance_id=0 downtime=277
vmstate_downtime_save type=non-iterable idstr=0000:00:04.0/virtio-net instance_id=0 downtime=270
vmstate_downtime_save type=non-iterable idstr=0000:00:05.0/virtio-net instance_id=0 downtime=288
vmstate_downtime_save type=non-iterable idstr=0000:00:06.0/virtio-vhost_vsock instance_id=0 downtime=279
vmstate_downtime_save type=non-iterable idstr=0000:00:07.0/vhost-user-fs instance_id=0 downtime=4166
vmstate_downtime_save type=non-iterable idstr=0000:00:08.0/virtio-balloon instance_id=0 downtime=464
vmstate_downtime_save type=non-iterable idstr=0000:00:0a.0/virtio-console instance_id=0 downtime=482
vmstate_downtime_save type=non-iterable idstr=PCIBUS instance_id=1 downtime=3
vmstate_downtime_save type=non-iterable idstr=0000:00:0c.0/virtio-rng instance_id=0 downtime=509
vmstate_downtime_save type=non-iterable idstr=acpi_build instance_id=0 downtime=2
vmstate_downtime_save type=non-iterable idstr=globalstate instance_id=0 downtime=8

基本 hmp 命令

migrate                 migrate_cancel          migrate_continue
migrate_incoming        migrate_pause           migrate_recover
migrate_set_capability  migrate_set_parameter   migrate_start_postcopy

[ ] post 相关的

(qemu) migrate_pause
Error: migrate-pause is currently only supported during postcopy-active or postcopy-recover state

确定一个问题,热迁移的时候,内核中只有一个东西:

info status

info status 一共有下面 16 种

VM status: running
VM status: paused
VM status: paused (debug)
VM status: paused (inmigrate)
VM status: paused (internal-error)
VM status: paused (io-error)
VM status: paused (postmigrate)
VM status: paused (prelaunch)
VM status: paused (finish-migrate)
VM status: paused (restore-vm)
VM status: paused (save-vm)
VM status: paused (shutdown)
VM status: paused (suspended)
VM status: paused (watchdog)
VM status: paused (guest-panicked)
VM status: paused (colo)

各状态含义:

状态 含义 ━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ running Guest 正在运行 ──────────────── ──────────────────────────────────────────────── paused 通过 HMP stop 等操作主动暂停 ──────────────── ──────────────────────────────────────────────── debug 被 GDB/debugger 暂停 ──────────────── ──────────────────────────────────────────────── inmigrate 目标端正在等待或接收迁移 ──────────────── ──────────────────────────────────────────────── internal-error QEMU 内部错误导致 Guest 无法继续 ──────────────── ──────────────────────────────────────────────── io-error 磁盘 I/O 错误,设备策略配置为暂停 VM ──────────────── ──────────────────────────────────────────────── postmigrate 源端成功完成迁移后保持暂停 ──────────────── ──────────────────────────────────────────────── prelaunch 使用 -S 启动,Guest 尚未开始执行 ──────────────── ──────────────────────────────────────────────── finish-migrate 正在完成迁移的停机阶段 ──────────────── ──────────────────────────────────────────────── restore-vm 正在恢复 VM 快照/状态 ──────────────── ──────────────────────────────────────────────── save-vm 正在保存 VM 快照/状态 ──────────────── ──────────────────────────────────────────────── shutdown Guest 已关机,但 QEMU 因 -no-shutdown 没有退出 ──────────────── ──────────────────────────────────────────────── suspended Guest 处于挂起状态,例如 ACPI S3 ──────────────── ──────────────────────────────────────────────── watchdog Watchdog 触发,且动作配置为暂停 ──────────────── ──────────────────────────────────────────────── guest-panicked QEMU 收到 Guest OS panic 通知 ──────────────── ──────────────────────────────────────────────── colo VM 处于 COLO checkpoint 保存/恢复阶段

输出规则在 system/runstate-hmp-cmds.c:hmp_info_status

完整枚举定义见 qapi/run-state.json 中。

calc_dirty_rate

(qemu) help calc_dirty_rate
calc_dirty_rate [-r] [-b] second [sample_pages_per_GB] -- start a round of guest dirty rate measurement (using -r to
                         specify dirty ring as the method of calculation and
                         -b to specify dirty bitmap as method of calculation)

migration/dirtyrate.c

info dirty_rate

info migrate

在热迁移的过程中:

(qemu) info migrate -a
Status:                 active
Time (ms):              total=6092, setup=9, exp_down=300
RAM info:
  Throughput (Mbps):    1169.84
  Sizes:                pagesize=4 KiB, total=8.13 GiB
  Transfers:            transferred=459 MiB, remain=278 MiB
    Channels:           precopy=744 B, multifd=459 MiB, postcopy=0 B
    Page Types:         normal=112561, zero=1946319
  Page Rates (pps):     transfer=66560
  Others:               dirty_syncs=1
Globals:
  store-global-state: on
  only-migratable: on
  send-configuration: on
  send-section-footer: on
  send-switchover-start: on
  clear-bitmap-shift: 18

在热迁移之后:

info migrate -a
Status:                 completed
Time (ms):              total=6728, setup=8, down=32
RAM info:
  Throughput (Mbps):    649.36
  Sizes:                pagesize=4 KiB, total=8.13 GiB
  Transfers:            transferred=520 MiB, remain=0 B
    Channels:           precopy=808 B, multifd=520 MiB, postcopy=0 B
    Page Types:         normal=127568, zero=2003477
  Page Rates (pps):     transfer=40960
  Others:               dirty_syncs=3
Globals:
  store-global-state: on
  only-migratable: on
  send-configuration: on
  send-section-footer: on
  send-switchover-start: on
  clear-bitmap-shift: 18

现在source 以及结束了,然

info migrate_parameters

(qemu) info migrate_parameters
announce-initial: 50 ms
announce-max: 550 ms
announce-rounds: 5
announce-step: 100 ms
throttle-trigger-threshold: 50
cpu-throttle-initial: 20
cpu-throttle-increment: 10
cpu-throttle-tailslow: off
max-cpu-throttle: 99
tls-creds: ''
tls-hostname: ''
max-bandwidth: 134217728 bytes/second
avail-switchover-bandwidth: 0 bytes/second
downtime-limit: 300 ms
x-checkpoint-delay: 20000 ms
multifd-channels: 2
multifd-compression: none
zero-page-detection: multifd
xbzrle-cache-size: 67108864 bytes
max-postcopy-bandwidth: 0
tls-authz: ''
x-vcpu-dirty-limit-period: 1000 ms
vcpu-dirty-limit: 1 MB/s
mode: normal
direct-io: off

postcopy 专用

严格来说,以下 3 个命令仅用于 postcopy:

 HMP 命令                  使用位置        作用
━━━━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 migrate_start_postcopy    源端            将已经开始的 precopy 迁移切换到 postcopy
────────────────────────  ──────────────  ────────────────────────────────
 migrate_pause             源端或目标端    主动中断当前 postcopy 迁移通道
────────────────────────  ──────────────  ────────────────────────────────
 migrate_recover URI       目标端          为处于 postcopy-paused 的迁移建立新接收通道

其余命令不是 postcopy 专用:

典型 postcopy 流程:

# 源端和目标端都设置
migrate_set_capability postcopy-ram on

# 源端先正常发起迁移
migrate -d tcp:<目标IP>:4444

# 再切换到 postcopy
migrate_start_postcopy

断线后恢复:
# 目标端
migrate_recover tcp:0:4445
# 源端
migrate -d -r tcp:<目标IP>:4445

因此,可以简单记为:

postcopy 专用命令: migrate_start_postcopy migrate_pause migrate_recover

postcopy 专用选项: migrate -r

migrate_pause

migrate_pause 的核心用途是:主动切断当前 postcopy 通道,让 迁移进入一个“可以重新连接”的 postcopy-paused 状态。

它暂停的是迁移,不是直接执行 vm_stop 暂停 VM。

为什么需要它?因为 postcopy 有一个“无法简单回滚”的阶段:

  1. 目标端 VM 已经开始运行。
  2. 一部分内存页已经在目标端。
  3. 另一部分内存页仍留在源端,目标端按缺页请求获取。
  4. 此时网络异常后:
    • 源端不能恢复运行,因为最新内存状态可能已经在目标 端;
    • 目标端也不能独立运行,因为它仍可能缺少内存页;
    • migrate_cancel 也不再安全。

因此只能保留两端状态,修复网络后重新建立迁移通道。

正常情况下,明确的 socket I/O 错误会让 QEMU自动进入 postcopy-paused,不需要手工执行 migrate_pause。这个命令主 要是一个管理层“逃生开关”,用于以下情况:

执行时,它实际会关闭当前迁移 QEMUFile 通道,促使迁移线程进 入暂停状态。migration/migration.c:1895

典型处理流程:

# 发现迁移长时间卡在 postcopy-active
(qemu) info migrate

# 在任意一端主动切断旧迁移通道
(qemu) migrate_pause

通常断开事件会传播到另一端。如果没有传播,可以在另一          端也执
行:

(qemu) migrate_pause

确认两端:

(qemu) info migrate
Migration status: postcopy-paused

然后重新连接:

# 目标端监听新通道
(qemu) migrate_recover tcp:0:4445

# 源端连接新通道并恢复
(qemu) migrate -d -r tcp:<目标IP>:4445

需要注意:目标端的 info status 可能仍显示 running,但访问 尚未传输页面的 vCPU/线程会阻塞,因此 guest 可能部分或完 全 卡住。migrate_pause 不适合作为普通的迁移限速或长期暂停手 段,它主要服务于 postcopy 故障恢复。

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。