Difference between revisions of "Migration Protocol"

From Open-IOV
Jump to navigation Jump to search
(Remove unpaired blockquote tags left by reference cleanup)
 
m (Fix repository links: point to the cgit tree URLs)
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
<span id="migration-protocol-rpc-0x0b"></span>
{{About message}}
 
= Migration Protocol (RPC 0x0B) =
= Migration Protocol (RPC 0x0B) =


<span id="overview"></span>
<blockquote>'''Historical document — superseded.''' This page records the MIGRATE RPC (type 0x0B) as seen in firmware analysis; in emulation mode the MIGRATE RPC returns <code>-ENOTSUPP</code>. The live migration implementation is [https://git.vgpu.io/open-iov.git/tree/kernel/migration.c <code>../kernel/migration.c</code>]; design and status are documented in [https://git.vgpu.io/open-iov.git/tree/docs/04-implementation <code>../docs/04-implementation/</code>].</blockquote>
 
== Overview ==
== Overview ==


The MIGRATE RPC (type 0x0B) implements live migration for vGPU virtual functions. It uses '''dual jump tables''' for two-phase migration operations, accessed via the primary dispatcher at 0x057A1AC0.
The MIGRATE RPC (type 0x0B) implements live migration for vGPU virtual functions. It uses '''dual jump tables''' for two-phase migration operations, accessed via the primary dispatcher at 0x057A1AC0.
<span id="key-addresses"></span>
 
== Key Addresses ==
== Key Addresses ==


<span id="migration-jump-tables"></span>
=== Migration Jump Tables ===
=== Migration Jump Tables ===


<span id="table-1-0x04179ed8-primary-migrate-branch-at-0x057a1bdc"></span>
==== Table 1: 0x04179ED8 (Primary MIGRATE branch at 0x057A1BDC) ====
==== Table 1: 0x04179ED8 (Primary MIGRATE branch at 0x057A1BDC) ====


Line 103: Line 103:
|}
|}


<span id="table-2-0x04179fbc-primary-migrate-branch-at-0x057a1d5c"></span>
==== Table 2: 0x04179FBC (Primary MIGRATE branch at 0x057A1D5C) ====
==== Table 2: 0x04179FBC (Primary MIGRATE branch at 0x057A1D5C) ====


Line 194: Line 193:
|}
|}


<span id="migration-phases"></span>
== Migration Phases ==
== Migration Phases ==


<span id="phase-0-discovery-pre-migration"></span>
=== Phase 0: Discovery (Pre-migration) ===
=== Phase 0: Discovery (Pre-migration) ===


<syntaxhighlight lang="c">// Query VF migratability
<pre>// Query VF migratability
int gsp_migrate_query(struct gsp_vf *vf, struct migrate_query *out) {
int gsp_migrate_query(struct gsp_vf *vf, struct migrate_query *out) {
     migrate_payload_t payload = {.phase = MIGRATE_PHASE_QUERY,.subcmd = 0,.src_vf = vf->id,.dst_vf = 0,
     migrate_payload_t payload = {
        .phase = MIGRATE_PHASE_QUERY,
        .subcmd = 0,
        .src_vf = vf-&gt;id,
        .dst_vf = 0,
     };
     };
     return gsp_rpc_send(vf->gsp, RPC_MIGRATE, &payload, sizeof(payload), out, sizeof(*out));
     return gsp_rpc_send(vf-&gt;gsp, RPC_MIGRATE, &amp;payload, sizeof(payload), out, sizeof(*out));
}</syntaxhighlight>
}</pre>
<span id="phase-1-prepare-table-1-indices-0-11"></span>
 
=== Phase 1: Prepare (Table 1, indices 0-11) ===
=== Phase 1: Prepare (Table 1, indices 0-11) ===


'''Goal''': Quiesce VF, prepare memory state for transfer
'''Goal''': Quiesce VF, prepare memory state for transfer


<syntaxhighlight lang="c">// Subcmd 3 (0x057A22F8) - Returns ID 0x10
<pre>// Subcmd 3 (0x057A22F8) - Returns ID 0x10
int migrate_prepare_quiesce(vf_id) {
int migrate_prepare_quiesce(vf_id) {
     // 1. Pause VF scheduling
     // 1. Pause VF scheduling
Line 226: Line 227:
     // 4. Snapshot page tables
     // 4. Snapshot page tables
     // 5. Return snapshot handle
     // 5. Return snapshot handle
}</syntaxhighlight>
}</pre>
<span id="phase-2-transfer-table-1-index-13-table-2-indices-0-11"></span>
 
=== Phase 2: Transfer (Table 1 index 13, Table 2 indices 0-11) ===
=== Phase 2: Transfer (Table 1 index 13, Table 2 indices 0-11) ===


'''Goal''': Transfer memory pages to destination
'''Goal''': Transfer memory pages to destination


<syntaxhighlight lang="c">// Table 2, subcmd 0 (0x057A257C)
<pre>// Table 2, subcmd 0 (0x057A257C)
int migrate_transfer_pages(vf_id, page_list, *progress) {
int migrate_transfer_pages(vf_id, page_list, *progress) {
     // 1. Iterate page list
     // 1. Iterate page list
Line 245: Line 246:
int migrate_track_dirty(vf_id, *dirty_bitmap) {
int migrate_track_dirty(vf_id, *dirty_bitmap) {
     // Return bitmap of pages dirtied since last transfer
     // Return bitmap of pages dirtied since last transfer
}</syntaxhighlight>
}</pre>
<span id="phase-3-commit-table-2-indices-12-15"></span>
 
=== Phase 3: Commit (Table 2 indices 12-15) ===
=== Phase 3: Commit (Table 2 indices 12-15) ===


'''Goal''': Final synchronization, cutover to destination
'''Goal''': Final synchronization, cutover to destination


<syntaxhighlight lang="c">// Subcmd 12 (0x057A345C)
<pre>// Subcmd 12 (0x057A345C)
int migrate_commit_finalize(vf_id, dst_vf_id) {
int migrate_commit_finalize(vf_id, dst_vf_id) {
     // 1. Final dirty page transfer
     // 1. Final dirty page transfer
Line 266: Line 267:
     // 3. Verify device state
     // 3. Verify device state
     // 4. Return PASS/FAIL
     // 4. Return PASS/FAIL
}</syntaxhighlight>
}</pre>
<span id="phase-4-cleanup-post-migration"></span>
 
=== Phase 4: Cleanup (Post-migration) ===
=== Phase 4: Cleanup (Post-migration) ===


<syntaxhighlight lang="c">int migrate_cleanup(src_vf_id) {
<pre>int migrate_cleanup(src_vf_id) {
     // 1. Release pinned memory
     // 1. Release pinned memory
     // 2. Free snapshot buffers
     // 2. Free snapshot buffers
     // 3. Remove WPR regions
     // 3. Remove WPR regions
     // 4. Destroy migration context
     // 4. Destroy migration context
}</syntaxhighlight>
}</pre>
<span id="migration-message-format"></span>
 
== Migration Message Format ==
== Migration Message Format ==


<syntaxhighlight lang="c">typedef struct {
<pre>typedef struct {
     uint32_t phase;        // MIGRATE_PHASE_*
     uint32_t phase;        // MIGRATE_PHASE_*
     uint32_t subcmd;        // Sub-command index (0-15 per table)
     uint32_t subcmd;        // Sub-command index (0-15 per table)
Line 297: Line 298:
#define MIGRATE_PHASE_CLEANUP  4
#define MIGRATE_PHASE_CLEANUP  4


#define MIGRATE_FLAG_LIVE      (1<<0)    // Live migration
#define MIGRATE_FLAG_LIVE      (1&lt;&lt;0)    // Live migration
#define MIGRATE_FLAG_POSTCOPY  (1<<1)    // Post-copy mode
#define MIGRATE_FLAG_POSTCOPY  (1&lt;&lt;1)    // Post-copy mode
#define MIGRATE_FLAG_AUTO_COMMIT (1<<2)  // Auto-commit after transfer
#define MIGRATE_FLAG_AUTO_COMMIT (1&lt;&lt;2)  // Auto-commit after transfer
#define MIGRATE_FLAG_VERIFY    (1<<3)    // Verify integrity</syntaxhighlight>
#define MIGRATE_FLAG_VERIFY    (1&lt;&lt;3)    // Verify integrity</pre>
<span id="sub-command-descriptors-returned-by-handlers"></span>
 
== Sub-Command Descriptors (Returned by Handlers) ==
== Sub-Command Descriptors (Returned by Handlers) ==


All migration handlers follow the primary dispatcher pattern:
All migration handlers follow the primary dispatcher pattern:


<syntaxhighlight lang="c">typedef struct {
<pre>typedef struct {
     uint32_t subcmd_id;    // Constant: 0x10, 0x14, etc.
     uint32_t subcmd_id;    // Constant: 0x10, 0x14, etc.
     uint8_t  echo_flags_b;  // Copy of input flags_b
     uint8_t  echo_flags_b;  // Copy of input flags_b
     uint8_t  echo_flags_a;  // Copy of input flags_a
     uint8_t  echo_flags_a;  // Copy of input flags_a
     uint8_t  echo_flags_c;  // Copy of input flags_c
     uint8_t  echo_flags_c;  // Copy of input flags_c
} migrate_descriptor_t;</syntaxhighlight>
} migrate_descriptor_t;</pre>
'''Known Subcmd IDs''': | Subcmd | ID | Phase | |--------|-----|-------| | 0x057A22F8 (Table1[3]) | 0x10 | Prepare | | 0x057A2344 (Table1[0]) | 0x14 | Prepare | | Others | TBD | Transfer/Commit |
 
'''Known Subcmd IDs''':
 
{| class="wikitable"
|-
! Subcmd
! ID
! Phase
|-
| 0x057A22F8 (Table1[3])
| 0x10
| Prepare
|-
| 0x057A2344 (Table1[0])
| 0x14
| Prepare
|-
| Others
| TBD
| Transfer/Commit
|}


<span id="vgpu-io-integration"></span>
== vGPU-IO Integration ==
== vGPU-IO Integration ==


<span id="migration-state-machine"></span>
=== Migration State Machine ===
=== Migration State Machine ===


<syntaxhighlight lang="c">typedef enum {
<pre>typedef enum {
     MIG_STATE_IDLE = 0,
     MIG_STATE_IDLE = 0,
     MIG_STATE_QUERY,
     MIG_STATE_QUERY,
Line 339: Line 358:
     struct completion comp;
     struct completion comp;
     struct mutex lock;
     struct mutex lock;
};</syntaxhighlight>
};</pre>
<span id="migration-coordinator"></span>
 
=== Migration Coordinator ===
=== Migration Coordinator ===


<syntaxhighlight lang="c">int gsp_migration_start(struct gsp_vf *vf, uint64_t dst_vf) {
<pre>int gsp_migration_start(struct gsp_vf *vf, uint64_t dst_vf) {
     struct gsp_migration *mig = kzalloc(sizeof(*mig), GFP_KERNEL);
     struct gsp_migration *mig = kzalloc(sizeof(*mig), GFP_KERNEL);
     vf->migration = mig;
     vf-&gt;migration = mig;
     mig->vf = vf;
     mig-&gt;vf = vf;
     mig->dst_vf = dst_vf;
     mig-&gt;dst_vf = dst_vf;
     mig->state = MIG_STATE_QUERY;
     mig-&gt;state = MIG_STATE_QUERY;
      
      
     // Phase 0: Query
     // Phase 0: Query
     struct migrate_query qry;
     struct migrate_query qry;
     gsp_migrate_query(vf, &qry);
     gsp_migrate_query(vf, &amp;qry);
     if (!qry.migratable) return -EINVAL;
     if (!qry.migratable) return -EINVAL;
      
      
     // Phase 1: Prepare
     // Phase 1: Prepare
     mig->state = MIG_STATE_PREPARE;
     mig-&gt;state = MIG_STATE_PREPARE;
     migrate_payload_t prep = {.phase = MIGRATE_PHASE_PREPARE,.subcmd = 3,  // Quiesce.src_vf = vf->id,.dst_vf = dst_vf,
     migrate_payload_t prep = {
        .phase = MIGRATE_PHASE_PREPARE,
        .subcmd = 3,  // Quiesce
        .src_vf = vf-&gt;id,
        .dst_vf = dst_vf,
     };
     };
     migrate_descriptor_t desc;
     migrate_descriptor_t desc;
     gsp_rpc_send(vf->gsp, RPC_MIGRATE, &prep, sizeof(prep), &desc, sizeof(desc));
     gsp_rpc_send(vf-&gt;gsp, RPC_MIGRATE, &amp;prep, sizeof(prep), &amp;desc, sizeof(desc));
     mig->token = desc.subcmd_id;  // Use returned token
     mig-&gt;token = desc.subcmd_id;  // Use returned token
      
      
     // Phase 2: Transfer (iterative)
     // Phase 2: Transfer (iterative)
     mig->state = MIG_STATE_TRANSFER;
     mig-&gt;state = MIG_STATE_TRANSFER;
     while (!gsp_migration_transfer_iter(mig)) {
     while (!gsp_migration_transfer_iter(mig)) {
         msleep(10);  // Throttle
         msleep(10);  // Throttle
Line 371: Line 394:
      
      
     // Phase 3: Commit
     // Phase 3: Commit
     mig->state = MIG_STATE_COMMIT;
     mig-&gt;state = MIG_STATE_COMMIT;
     migrate_payload_t commit = {.phase = MIGRATE_PHASE_COMMIT,.subcmd = 12,.src_vf = vf->id,.dst_vf = dst_vf,.token = mig->token,.flags = MIGRATE_FLAG_VERIFY,
     migrate_payload_t commit = {
        .phase = MIGRATE_PHASE_COMMIT,
        .subcmd = 12,
        .src_vf = vf-&gt;id,
        .dst_vf = dst_vf,
        .token = mig-&gt;token,
        .flags = MIGRATE_FLAG_VERIFY,
     };
     };
     gsp_rpc_send(vf->gsp, RPC_MIGRATE, &commit, sizeof(commit), NULL, 0);
     gsp_rpc_send(vf-&gt;gsp, RPC_MIGRATE, &amp;commit, sizeof(commit), NULL, 0);
      
      
     // Phase 4: Cleanup
     // Phase 4: Cleanup
     mig->state = MIG_STATE_CLEANUP;
     mig-&gt;state = MIG_STATE_CLEANUP;
     gsp_migration_cleanup(mig);
     gsp_migration_cleanup(mig);
      
      
     mig->state = MIG_STATE_COMPLETED;
     mig-&gt;state = MIG_STATE_COMPLETED;
     return 0;
     return 0;
}</syntaxhighlight>
}</pre>
<span id="transfer-iteration"></span>
 
=== Transfer Iteration ===
=== Transfer Iteration ===


<syntaxhighlight lang="c">bool gsp_migration_transfer_iter(struct gsp_migration *mig) {
<pre>bool gsp_migration_transfer_iter(struct gsp_migration *mig) {
     migrate_payload_t xfer = {.phase = MIGRATE_PHASE_TRANSFER,.subcmd = 0,.src_vf = mig->vf->id,.dst_vf = mig->dst_vf,.data_ptr = (uint64_t)mig->progress.page_list,.data_size = mig->progress.page_count * sizeof(uint64_t),.token = mig->token,
     migrate_payload_t xfer = {
        .phase = MIGRATE_PHASE_TRANSFER,
        .subcmd = 0,
        .src_vf = mig-&gt;vf-&gt;id,
        .dst_vf = mig-&gt;dst_vf,
        .data_ptr = (uint64_t)mig-&gt;progress.page_list,
        .data_size = mig-&gt;progress.page_count * sizeof(uint64_t),
        .token = mig-&gt;token,
     };
     };
      
      
     struct migrate_progress prog;
     struct migrate_progress prog;
     int ret = gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE,  
     int ret = gsp_rpc_send(mig-&gt;vf-&gt;gsp, RPC_MIGRATE,  
                           &xfer, sizeof(xfer), &prog, sizeof(prog));
                           &amp;xfer, sizeof(xfer), &amp;prog, sizeof(prog));
      
      
     if (ret) return true;  // Error = done
     if (ret) return true;  // Error = done
      
      
     mig->progress.bytes_transferred += prog.bytes_transferred;
     mig-&gt;progress.bytes_transferred += prog.bytes_transferred;
     mig->progress.pages_transferred += prog.pages_transferred;
     mig-&gt;progress.pages_transferred += prog.pages_transferred;
     mig->progress.dirty_pages = prog.dirty_pages;
     mig-&gt;progress.dirty_pages = prog.dirty_pages;
      
      
     // Check if more dirty pages to transfer
     // Check if more dirty pages to transfer
     if (prog.dirty_pages > 0 && !(mig->flags & MIGRATE_FLAG_POSTCOPY)) {
     if (prog.dirty_pages &gt; 0 &amp;&amp; !(mig-&gt;flags &amp; MIGRATE_FLAG_POSTCOPY)) {
         return false;  // Continue iterating
         return false;  // Continue iterating
     }
     }
      
      
     return true;  // Transfer complete
     return true;  // Transfer complete
}</syntaxhighlight>
}</pre>
<span id="post-copy-mode"></span>
 
=== Post-Copy Mode ===
=== Post-Copy Mode ===


<syntaxhighlight lang="c">int gsp_migration_postcopy(struct gsp_migration *mig) {
<pre>int gsp_migration_postcopy(struct gsp_migration *mig) {
     // Enable post-copy on destination
     // Enable post-copy on destination
     migrate_payload_t pc = {.phase = MIGRATE_PHASE_TRANSFER,.subcmd = 11,  // Dirty tracking.src_vf = mig->vf->id,.dst_vf = mig->dst_vf,.flags = MIGRATE_FLAG_POSTCOPY,.token = mig->token,
     migrate_payload_t pc = {
        .phase = MIGRATE_PHASE_TRANSFER,
        .subcmd = 11,  // Dirty tracking
        .src_vf = mig-&gt;vf-&gt;id,
        .dst_vf = mig-&gt;dst_vf,
        .flags = MIGRATE_FLAG_POSTCOPY,
        .token = mig-&gt;token,
     };
     };
      
      
Line 418: Line 460:
     gsp_page_fault_handler = gsp_migration_page_fault;
     gsp_page_fault_handler = gsp_migration_page_fault;
      
      
     return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &pc, sizeof(pc), NULL, 0);
     return gsp_rpc_send(mig-&gt;vf-&gt;gsp, RPC_MIGRATE, &amp;pc, sizeof(pc), NULL, 0);
}</syntaxhighlight>
}</pre>
<span id="integration-with-barwpr"></span>
 
== Integration with BAR/WPR ==
== Integration with BAR/WPR ==


<span id="bar-state-migration"></span>
=== BAR State Migration ===
=== BAR State Migration ===


<syntaxhighlight lang="c">int gsp_migration_bar_save(struct gsp_migration *mig) {
<pre>int gsp_migration_bar_save(struct gsp_migration *mig) {
     // Save BAR1 aperture, force access state
     // Save BAR1 aperture, force access state
     struct bar_save_msg bar_save = {.vf_id = mig->vf->id,.bar = 1,
     struct bar_save_msg bar_save = {
        .vf_id = mig-&gt;vf-&gt;id,
        .bar = 1,
     };
     };
     return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP,
     return gsp_rpc_send(mig-&gt;vf-&gt;gsp, RPC_BAR_TRAP,
                         BAR_SUBCMD_SAVE, &bar_save, sizeof(bar_save),
                         BAR_SUBCMD_SAVE, &amp;bar_save, sizeof(bar_save),
                         mig->bar_state_buf, &mig->bar_state_size);
                         mig-&gt;bar_state_buf, &amp;mig-&gt;bar_state_size);
}
}


int gsp_migration_bar_restore(struct gsp_migration *mig) {
int gsp_migration_bar_restore(struct gsp_migration *mig) {
     // Restore BAR1 on destination
     // Restore BAR1 on destination
     struct bar_restore_msg bar_restore = {.vf_id = mig->dst_vf,.bar = 1,.data = mig->bar_state_buf,.data_size = mig->bar_state_size,
     struct bar_restore_msg bar_restore = {
        .vf_id = mig-&gt;dst_vf,
        .bar = 1,
        .data = mig-&gt;bar_state_buf,
        .data_size = mig-&gt;bar_state_size,
     };
     };
     return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP,
     return gsp_rpc_send(mig-&gt;vf-&gt;gsp, RPC_BAR_TRAP,
                         BAR_SUBCMD_RESTORE, &bar_restore, sizeof(bar_restore));
                         BAR_SUBCMD_RESTORE, &amp;bar_restore, sizeof(bar_restore));
}</syntaxhighlight>
}</pre>
<span id="wpr-region-migration"></span>
 
=== WPR Region Migration ===
=== WPR Region Migration ===


<syntaxhighlight lang="c">int gsp_migration_wpr_save(struct gsp_migration *mig) {
<pre>int gsp_migration_wpr_save(struct gsp_migration *mig) {
     // Save WPR regions for VF
     // Save WPR regions for VF
     migrate_payload_t wpr = {.phase = MIGRATE_PHASE_PREPARE,.subcmd = 4,  // WPR snapshot.src_vf = mig->vf->id,.dst_vf = mig->dst_vf,.token = mig->token,
     migrate_payload_t wpr = {
        .phase = MIGRATE_PHASE_PREPARE,
        .subcmd = 4,  // WPR snapshot
        .src_vf = mig-&gt;vf-&gt;id,
        .dst_vf = mig-&gt;dst_vf,
        .token = mig-&gt;token,
     };
     };
     return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &wpr, sizeof(wpr), NULL, 0);
     return gsp_rpc_send(mig-&gt;vf-&gt;gsp, RPC_MIGRATE, &amp;wpr, sizeof(wpr), NULL, 0);
}</syntaxhighlight>
}</pre>
<span id="error-handling"></span>
 
== Error Handling ==
== Error Handling ==


Line 485: Line 537:
|}
|}


<span id="debugging"></span>
== Debugging ==
== Debugging ==


<span id="migration-trace-points"></span>
=== Migration Trace Points ===
=== Migration Trace Points ===


<syntaxhighlight lang="c">trace_gsp_mig_phase(vf_id, phase, subcmd, token);
<pre>trace_gsp_mig_phase(vf_id, phase, subcmd, token);
trace_gsp_mig_transfer(vf_id, pages, bytes, dirty);
trace_gsp_mig_transfer(vf_id, pages, bytes, dirty);
trace_gsp_mig_commit(vf_id, status, verify);
trace_gsp_mig_commit(vf_id, status, verify);
trace_gsp_mig_error(vf_id, phase, error);</syntaxhighlight>
trace_gsp_mig_error(vf_id, phase, error);</pre>
<span id="gsp-log-strings"></span>
 
=== GSP Log Strings ===
=== GSP Log Strings ===


Line 504: Line 554:
* Commit verification results
* Commit verification results


-----
----


<span id="reference"></span>
== Reference ==
== Reference ==



Latest revision as of 04:40, 12 October 2026

An absence of critical technical documentation has historically slowed growth and adoption of developer ecosystems for GPU virtualization.

This CC-BY-4.0 licensed content can either be used with attribution, or used as inspiration for new documentation, created by GPU vendors for public commercial distribution as developer documentation.

Where possible, this documentation will clearly label dates and versions of observed-but-not-guaranteed behaviour vs. vendor-documented stable interfaces/behaviour with guarantees of forward or backward compatibility.

Migration Protocol (RPC 0x0B)

Historical document — superseded. This page records the MIGRATE RPC (type 0x0B) as seen in firmware analysis; in emulation mode the MIGRATE RPC returns -ENOTSUPP. The live migration implementation is ../kernel/migration.c; design and status are documented in ../docs/04-implementation/.

Overview

The MIGRATE RPC (type 0x0B) implements live migration for vGPU virtual functions. It uses dual jump tables for two-phase migration operations, accessed via the primary dispatcher at 0x057A1AC0.

Key Addresses

Migration Jump Tables

Table 1: 0x04179ED8 (Primary MIGRATE branch at 0x057A1BDC)

Index Target Decompiled Description
0 0x057A2344 ✅ Phase 1: Prepare - returns ID 0x14, checks flag at +0x7F9
1 0x057A2320 ✅ Phase 1: Prepare variant
2 0x057A230C ✅ Phase 1: Prepare variant
3 0x057A22F8 ✅ Phase 1: Prepare - returns ID 0x10
4 0x057A2388 ✅ Phase 1: Prepare variant
5 0x057A23D8 ✅ Phase 1: Prepare variant
6 0x057A23AC ✅ Phase 1: Prepare variant
7 0x057A2478 ✅ Phase 1: Prepare variant
8 0x057A2428 ✅ Phase 1: Prepare variant
9 0x057A2454 ✅ Phase 1: Prepare variant
10 0x057A2404 ✅ Phase 1: Prepare variant
11 0x057A2364 ✅ Phase 1: Prepare variant
12 0x057A1F3C (dup) Duplicate entry
13 0x057A299C — Phase 2: Transfer
14 0x057A1F3C (dup) Duplicate entry
15 0x057A1F3C (dup) Duplicate entry

Table 2: 0x04179FBC (Primary MIGRATE branch at 0x057A1D5C)

Index Target Decompiled Description
0 0x057A257C ✅ Phase 2: Transfer
1 0x057A25E4 ✅ Phase 2: Transfer
2 0x057A24EC ✅ Phase 2: Transfer
3 0x057A2608 ✅ Phase 2: Transfer
4 0x057A2534 ✅ Phase 2: Transfer
5 0x057A25C0 ✅ Phase 2: Transfer
6 0x057A24A4 ✅ Phase 2: Transfer
7 0x057A259C ✅ Phase 2: Transfer
8 0x057A2510 ✅ Phase 2: Transfer
9 0x057A2558 ✅ Phase 2: Transfer
10 0x057A24C8 ✅ Phase 2: Transfer
11 0x057A262C ✅ Phase 2: Transfer
12 0x057A345C ✅ Phase 3: Commit
13 0x057A36D8 ✅ Phase 3: Commit
14 0x057A35D8 ✅ Phase 3: Commit
15 0x057A3658 ✅ Phase 3: Commit

Migration Phases

Phase 0: Discovery (Pre-migration)

// Query VF migratability
int gsp_migrate_query(struct gsp_vf *vf, struct migrate_query *out) {
    migrate_payload_t payload = {
        .phase = MIGRATE_PHASE_QUERY,
        .subcmd = 0,
        .src_vf = vf->id,
        .dst_vf = 0,
    };
    return gsp_rpc_send(vf->gsp, RPC_MIGRATE, &payload, sizeof(payload), out, sizeof(*out));
}

Phase 1: Prepare (Table 1, indices 0-11)

Goal: Quiesce VF, prepare memory state for transfer

// Subcmd 3 (0x057A22F8) - Returns ID 0x10
int migrate_prepare_quiesce(vf_id) {
    // 1. Pause VF scheduling
    // 2. Flush pending commands
    // 3. Pin all VF memory
    // 4. Return quiesce token
}

// Subcmd 0 (0x057A2344) - Returns ID 0x14, checks flag at in_t1+0x7F9
int migrate_prepare_snapshot(vf_id, *token) {
    // 1. Snapshot VF register state
    // 2. Snapshot BAR configurations
    // 3. Snapshot WPR regions
    // 4. Snapshot page tables
    // 5. Return snapshot handle
}

Phase 2: Transfer (Table 1 index 13, Table 2 indices 0-11)

Goal: Transfer memory pages to destination

// Table 2, subcmd 0 (0x057A257C)
int migrate_transfer_pages(vf_id, page_list, *progress) {
    // 1. Iterate page list
    // 2. For each page:
    //    a. Copy via DMA (0x0588F9C4)
    //    b. Verify checksum
    //    c. Mark page dirty if modified
    // 3. Return bytes transferred
}

// Table 2, subcmd 11 (0x057A262C) - Dirty page tracking
int migrate_track_dirty(vf_id, *dirty_bitmap) {
    // Return bitmap of pages dirtied since last transfer
}

Phase 3: Commit (Table 2 indices 12-15)

Goal: Final synchronization, cutover to destination

// Subcmd 12 (0x057A345C)
int migrate_commit_finalize(vf_id, dst_vf_id) {
    // 1. Final dirty page transfer
    // 2. Transfer register state
    // 3. Activate destination VF
    // 4. Release source VF resources
    // 5. Notify completion
}

// Subcmd 13 (0x057A36D8)
int migrate_commit_verify(vf_id, *verify_result) {
    // 1. Verify memory integrity
    // 2. Verify register state
    // 3. Verify device state
    // 4. Return PASS/FAIL
}

Phase 4: Cleanup (Post-migration)

int migrate_cleanup(src_vf_id) {
    // 1. Release pinned memory
    // 2. Free snapshot buffers
    // 3. Remove WPR regions
    // 4. Destroy migration context
}

Migration Message Format

typedef struct {
    uint32_t phase;         // MIGRATE_PHASE_*
    uint32_t subcmd;        // Sub-command index (0-15 per table)
    uint64_t src_vf;        // Source VF ID
    uint64_t dst_vf;        // Destination VF ID
    uint64_t data_ptr;      // Data buffer (page list, bitmap, etc.)
    uint64_t data_size;     // Buffer size
    uint32_t flags;         // MIGRATE_LIVE, MIGRATE_POSTCOPY, etc.
    uint32_t reserved;
    uint64_t token;         // Opaque migration token
} __attribute__((packed)) migrate_payload_t;

#define MIGRATE_PHASE_QUERY     0
#define MIGRATE_PHASE_PREPARE   1
#define MIGRATE_PHASE_TRANSFER  2
#define MIGRATE_PHASE_COMMIT    3
#define MIGRATE_PHASE_CLEANUP   4

#define MIGRATE_FLAG_LIVE       (1<<0)    // Live migration
#define MIGRATE_FLAG_POSTCOPY   (1<<1)    // Post-copy mode
#define MIGRATE_FLAG_AUTO_COMMIT (1<<2)  // Auto-commit after transfer
#define MIGRATE_FLAG_VERIFY     (1<<3)    // Verify integrity

Sub-Command Descriptors (Returned by Handlers)

All migration handlers follow the primary dispatcher pattern:

typedef struct {
    uint32_t subcmd_id;     // Constant: 0x10, 0x14, etc.
    uint8_t  echo_flags_b;  // Copy of input flags_b
    uint8_t  echo_flags_a;  // Copy of input flags_a
    uint8_t  echo_flags_c;  // Copy of input flags_c
} migrate_descriptor_t;

Known Subcmd IDs:

Subcmd ID Phase
0x057A22F8 (Table1[3]) 0x10 Prepare
0x057A2344 (Table1[0]) 0x14 Prepare
Others TBD Transfer/Commit

vGPU-IO Integration

Migration State Machine

typedef enum {
    MIG_STATE_IDLE = 0,
    MIG_STATE_QUERY,
    MIG_STATE_PREPARE,
    MIG_STATE_TRANSFER,
    MIG_STATE_COMMIT,
    MIG_STATE_CLEANUP,
    MIG_STATE_FAILED,
    MIG_STATE_COMPLETED
} mig_state_t;

struct gsp_migration {
    struct gsp_vf *vf;
    mig_state_t state;
    uint64_t token;
    uint64_t dst_vf;
    struct migrate_progress progress;
    struct completion comp;
    struct mutex lock;
};

Migration Coordinator

int gsp_migration_start(struct gsp_vf *vf, uint64_t dst_vf) {
    struct gsp_migration *mig = kzalloc(sizeof(*mig), GFP_KERNEL);
    vf->migration = mig;
    mig->vf = vf;
    mig->dst_vf = dst_vf;
    mig->state = MIG_STATE_QUERY;
    
    // Phase 0: Query
    struct migrate_query qry;
    gsp_migrate_query(vf, &qry);
    if (!qry.migratable) return -EINVAL;
    
    // Phase 1: Prepare
    mig->state = MIG_STATE_PREPARE;
    migrate_payload_t prep = {
        .phase = MIGRATE_PHASE_PREPARE,
        .subcmd = 3,  // Quiesce
        .src_vf = vf->id,
        .dst_vf = dst_vf,
    };
    migrate_descriptor_t desc;
    gsp_rpc_send(vf->gsp, RPC_MIGRATE, &prep, sizeof(prep), &desc, sizeof(desc));
    mig->token = desc.subcmd_id;  // Use returned token
    
    // Phase 2: Transfer (iterative)
    mig->state = MIG_STATE_TRANSFER;
    while (!gsp_migration_transfer_iter(mig)) {
        msleep(10);  // Throttle
        if (signal_pending(current)) break;
    }
    
    // Phase 3: Commit
    mig->state = MIG_STATE_COMMIT;
    migrate_payload_t commit = {
        .phase = MIGRATE_PHASE_COMMIT,
        .subcmd = 12,
        .src_vf = vf->id,
        .dst_vf = dst_vf,
        .token = mig->token,
        .flags = MIGRATE_FLAG_VERIFY,
    };
    gsp_rpc_send(vf->gsp, RPC_MIGRATE, &commit, sizeof(commit), NULL, 0);
    
    // Phase 4: Cleanup
    mig->state = MIG_STATE_CLEANUP;
    gsp_migration_cleanup(mig);
    
    mig->state = MIG_STATE_COMPLETED;
    return 0;
}

Transfer Iteration

bool gsp_migration_transfer_iter(struct gsp_migration *mig) {
    migrate_payload_t xfer = {
        .phase = MIGRATE_PHASE_TRANSFER,
        .subcmd = 0,
        .src_vf = mig->vf->id,
        .dst_vf = mig->dst_vf,
        .data_ptr = (uint64_t)mig->progress.page_list,
        .data_size = mig->progress.page_count * sizeof(uint64_t),
        .token = mig->token,
    };
    
    struct migrate_progress prog;
    int ret = gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, 
                           &xfer, sizeof(xfer), &prog, sizeof(prog));
    
    if (ret) return true;  // Error = done
    
    mig->progress.bytes_transferred += prog.bytes_transferred;
    mig->progress.pages_transferred += prog.pages_transferred;
    mig->progress.dirty_pages = prog.dirty_pages;
    
    // Check if more dirty pages to transfer
    if (prog.dirty_pages > 0 && !(mig->flags & MIGRATE_FLAG_POSTCOPY)) {
        return false;  // Continue iterating
    }
    
    return true;  // Transfer complete
}

Post-Copy Mode

int gsp_migration_postcopy(struct gsp_migration *mig) {
    // Enable post-copy on destination
    migrate_payload_t pc = {
        .phase = MIGRATE_PHASE_TRANSFER,
        .subcmd = 11,  // Dirty tracking
        .src_vf = mig->vf->id,
        .dst_vf = mig->dst_vf,
        .flags = MIGRATE_FLAG_POSTCOPY,
        .token = mig->token,
    };
    
    // Page fault handler on destination
    gsp_page_fault_handler = gsp_migration_page_fault;
    
    return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &pc, sizeof(pc), NULL, 0);
}

Integration with BAR/WPR

BAR State Migration

int gsp_migration_bar_save(struct gsp_migration *mig) {
    // Save BAR1 aperture, force access state
    struct bar_save_msg bar_save = {
        .vf_id = mig->vf->id,
        .bar = 1,
    };
    return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP,
                        BAR_SUBCMD_SAVE, &bar_save, sizeof(bar_save),
                        mig->bar_state_buf, &mig->bar_state_size);
}

int gsp_migration_bar_restore(struct gsp_migration *mig) {
    // Restore BAR1 on destination
    struct bar_restore_msg bar_restore = {
        .vf_id = mig->dst_vf,
        .bar = 1,
        .data = mig->bar_state_buf,
        .data_size = mig->bar_state_size,
    };
    return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP,
                        BAR_SUBCMD_RESTORE, &bar_restore, sizeof(bar_restore));
}

WPR Region Migration

int gsp_migration_wpr_save(struct gsp_migration *mig) {
    // Save WPR regions for VF
    migrate_payload_t wpr = {
        .phase = MIGRATE_PHASE_PREPARE,
        .subcmd = 4,  // WPR snapshot
        .src_vf = mig->vf->id,
        .dst_vf = mig->dst_vf,
        .token = mig->token,
    };
    return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &wpr, sizeof(wpr), NULL, 0);
}

Error Handling

Phase Error Recovery
Prepare EBUSY Wait, retry
Prepare ENOMEM Reduce snapshot size
Transfer ETIMEDOUT Checkpoint, retry
Transfer EIO Failover to post-copy
Commit EINVAL Rollback to source
Commit ENOSPC Expand destination

Debugging

Migration Trace Points

trace_gsp_mig_phase(vf_id, phase, subcmd, token);
trace_gsp_mig_transfer(vf_id, pages, bytes, dirty);
trace_gsp_mig_commit(vf_id, status, verify);
trace_gsp_mig_error(vf_id, phase, error);

GSP Log Strings

Search for migration-related strings in firmware:

  • Migration phase markers
  • Page transfer progress
  • Commit verification results

Reference

  • Primary Dispatcher MIGRATE branches: 0x057A1BDC, 0x057A1D54
  • Jump Table 1: 0x04179ED8 (16 entries)
  • Jump Table 2: 0x04179FBC (16 entries)
  • Decompiled handlers: 28 functions in decompiled/