Migration Protocol

From Open-IOV
Revision as of 04:10, 12 October 2026 by WikiBot (talk | contribs) (Re-sync page from tracked wiki source: fix cross-document links and diagram; refresh status)
Jump to navigation Jump to search

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/