Difference between revisions of "Migration Protocol"
(Remove unpaired blockquote tags left by reference cleanup) |
m (Re-sync page from tracked wiki source: fix cross-document links and diagram; refresh status) |
||
| Line 1: | Line 1: | ||
= Migration Protocol (RPC 0x0B) = | = Migration Protocol (RPC 0x0B) = | ||
< | <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/blob/master/kernel/migration.c <code>../kernel/migration.c</code>]; design and status are documented in [https://git.vgpu.io/open-iov/blob/master/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. | ||
== Key Addresses == | == Key Addresses == | ||
=== Migration Jump Tables === | === Migration Jump Tables === | ||
==== Table 1: 0x04179ED8 (Primary MIGRATE branch at 0x057A1BDC) ==== | ==== Table 1: 0x04179ED8 (Primary MIGRATE branch at 0x057A1BDC) ==== | ||
| Line 103: | Line 101: | ||
|} | |} | ||
==== Table 2: 0x04179FBC (Primary MIGRATE branch at 0x057A1D5C) ==== | ==== Table 2: 0x04179FBC (Primary MIGRATE branch at 0x057A1D5C) ==== | ||
| Line 194: | Line 191: | ||
|} | |} | ||
== Migration Phases == | == Migration Phases == | ||
=== Phase 0: Discovery (Pre-migration) === | === Phase 0: Discovery (Pre-migration) === | ||
< | <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- | migrate_payload_t payload = { | ||
.phase = MIGRATE_PHASE_QUERY, | |||
.subcmd = 0, | |||
.src_vf = vf->id, | |||
.dst_vf = 0, | |||
}; | }; | ||
return gsp_rpc_send(vf- | return gsp_rpc_send(vf->gsp, RPC_MIGRATE, &payload, sizeof(payload), out, sizeof(*out)); | ||
}</ | }</pre> | ||
=== 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 | ||
< | <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 225: | ||
// 4. Snapshot page tables | // 4. Snapshot page tables | ||
// 5. Return snapshot handle | // 5. Return snapshot handle | ||
}</ | }</pre> | ||
=== 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 | ||
< | <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 244: | ||
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 | ||
}</ | }</pre> | ||
=== 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 | ||
< | <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 265: | ||
// 3. Verify device state | // 3. Verify device state | ||
// 4. Return PASS/FAIL | // 4. Return PASS/FAIL | ||
}</ | }</pre> | ||
=== Phase 4: Cleanup (Post-migration) === | === Phase 4: Cleanup (Post-migration) === | ||
< | <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 | ||
}</ | }</pre> | ||
== Migration Message Format == | == Migration Message Format == | ||
< | <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 296: | ||
#define MIGRATE_PHASE_CLEANUP 4 | #define MIGRATE_PHASE_CLEANUP 4 | ||
#define MIGRATE_FLAG_LIVE (1 | #define MIGRATE_FLAG_LIVE (1<<0) // Live migration | ||
#define MIGRATE_FLAG_POSTCOPY (1 | #define MIGRATE_FLAG_POSTCOPY (1<<1) // Post-copy mode | ||
#define MIGRATE_FLAG_AUTO_COMMIT (1 | #define MIGRATE_FLAG_AUTO_COMMIT (1<<2) // Auto-commit after transfer | ||
#define MIGRATE_FLAG_VERIFY (1 | #define MIGRATE_FLAG_VERIFY (1<<3) // Verify integrity</pre> | ||
== 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: | ||
< | <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;</ | } migrate_descriptor_t;</pre> | ||
'''Known Subcmd IDs''': | Subcmd | |||
'''Known Subcmd IDs''': | |||
{| class="wikitable" | |||
|- | |||
! Subcmd | |||
! ID | |||
! Phase | |||
|- | |||
| 0x057A22F8 (Table1[3]) | |||
| 0x10 | |||
| Prepare | |||
|- | |||
| 0x057A2344 (Table1[0]) | |||
| 0x14 | |||
| Prepare | |||
|- | |||
| Others | |||
| TBD | |||
| Transfer/Commit | |||
|} | |||
== vGPU-IO Integration == | == vGPU-IO Integration == | ||
=== Migration State Machine === | === Migration State Machine === | ||
< | <pre>typedef enum { | ||
MIG_STATE_IDLE = 0, | MIG_STATE_IDLE = 0, | ||
MIG_STATE_QUERY, | MIG_STATE_QUERY, | ||
| Line 339: | Line 356: | ||
struct completion comp; | struct completion comp; | ||
struct mutex lock; | struct mutex lock; | ||
};</ | };</pre> | ||
=== Migration Coordinator === | === Migration Coordinator === | ||
< | <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- | vf->migration = mig; | ||
mig- | mig->vf = vf; | ||
mig- | mig->dst_vf = dst_vf; | ||
mig- | mig->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, &qry); | ||
if (!qry.migratable) return -EINVAL; | if (!qry.migratable) return -EINVAL; | ||
// Phase 1: Prepare | // Phase 1: Prepare | ||
mig- | mig->state = MIG_STATE_PREPARE; | ||
migrate_payload_t prep = {.phase = MIGRATE_PHASE_PREPARE,.subcmd = 3, // Quiesce.src_vf = vf- | migrate_payload_t prep = { | ||
.phase = MIGRATE_PHASE_PREPARE, | |||
.subcmd = 3, // Quiesce | |||
.src_vf = vf->id, | |||
.dst_vf = dst_vf, | |||
}; | }; | ||
migrate_descriptor_t desc; | migrate_descriptor_t desc; | ||
gsp_rpc_send(vf- | gsp_rpc_send(vf->gsp, RPC_MIGRATE, &prep, sizeof(prep), &desc, sizeof(desc)); | ||
mig- | mig->token = desc.subcmd_id; // Use returned token | ||
// Phase 2: Transfer (iterative) | // Phase 2: Transfer (iterative) | ||
mig- | mig->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 392: | ||
// Phase 3: Commit | // Phase 3: Commit | ||
mig- | mig->state = MIG_STATE_COMMIT; | ||
migrate_payload_t commit = {.phase = MIGRATE_PHASE_COMMIT,.subcmd = 12,.src_vf = vf- | 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_send(vf->gsp, RPC_MIGRATE, &commit, sizeof(commit), NULL, 0); | ||
// Phase 4: Cleanup | // Phase 4: Cleanup | ||
mig- | mig->state = MIG_STATE_CLEANUP; | ||
gsp_migration_cleanup(mig); | gsp_migration_cleanup(mig); | ||
mig- | mig->state = MIG_STATE_COMPLETED; | ||
return 0; | return 0; | ||
}</ | }</pre> | ||
=== Transfer Iteration === | === Transfer Iteration === | ||
< | <pre>bool gsp_migration_transfer_iter(struct gsp_migration *mig) { | ||
migrate_payload_t xfer = {.phase = MIGRATE_PHASE_TRANSFER,.subcmd = 0,.src_vf = 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; | struct migrate_progress prog; | ||
int ret = gsp_rpc_send(mig- | int ret = gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, | ||
&xfer, sizeof(xfer), &prog, sizeof(prog)); | &xfer, sizeof(xfer), &prog, sizeof(prog)); | ||
if (ret) return true; // Error = done | if (ret) return true; // Error = done | ||
mig- | mig->progress.bytes_transferred += prog.bytes_transferred; | ||
mig- | mig->progress.pages_transferred += prog.pages_transferred; | ||
mig- | mig->progress.dirty_pages = prog.dirty_pages; | ||
// Check if more dirty pages to transfer | // Check if more dirty pages to transfer | ||
if (prog.dirty_pages | if (prog.dirty_pages > 0 && !(mig->flags & MIGRATE_FLAG_POSTCOPY)) { | ||
return false; // Continue iterating | return false; // Continue iterating | ||
} | } | ||
return true; // Transfer complete | return true; // Transfer complete | ||
}</ | }</pre> | ||
=== Post-Copy Mode === | === Post-Copy Mode === | ||
< | <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- | 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, | |||
}; | }; | ||
| Line 418: | Line 458: | ||
gsp_page_fault_handler = gsp_migration_page_fault; | gsp_page_fault_handler = gsp_migration_page_fault; | ||
return gsp_rpc_send(mig- | return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &pc, sizeof(pc), NULL, 0); | ||
}</ | }</pre> | ||
== Integration with BAR/WPR == | == Integration with BAR/WPR == | ||
=== BAR State Migration === | === BAR State Migration === | ||
< | <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- | struct bar_save_msg bar_save = { | ||
.vf_id = mig->vf->id, | |||
.bar = 1, | |||
}; | }; | ||
return gsp_rpc_send(mig- | return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP, | ||
BAR_SUBCMD_SAVE, &bar_save, sizeof(bar_save), | BAR_SUBCMD_SAVE, &bar_save, sizeof(bar_save), | ||
mig- | mig->bar_state_buf, &mig->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- | 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- | return gsp_rpc_send(mig->vf->gsp, RPC_BAR_TRAP, | ||
BAR_SUBCMD_RESTORE, &bar_restore, sizeof(bar_restore)); | BAR_SUBCMD_RESTORE, &bar_restore, sizeof(bar_restore)); | ||
}</ | }</pre> | ||
=== WPR Region Migration === | === WPR Region Migration === | ||
< | <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- | 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- | return gsp_rpc_send(mig->vf->gsp, RPC_MIGRATE, &wpr, sizeof(wpr), NULL, 0); | ||
}</ | }</pre> | ||
== Error Handling == | == Error Handling == | ||
| Line 485: | Line 535: | ||
|} | |} | ||
== Debugging == | == Debugging == | ||
=== Migration Trace Points === | === Migration Trace Points === | ||
< | <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);</ | trace_gsp_mig_error(vf_id, phase, error);</pre> | ||
=== GSP Log Strings === | === GSP Log Strings === | ||
| Line 504: | Line 552: | ||
* Commit verification results | * Commit verification results | ||
---- | |||
== Reference == | == Reference == | ||
Revision as of 04:10, 12 October 2026
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/