1
0
Fork 0

KVM: PPC: Book3S HV: XIVE: Add a control to dirty the XIVE EQ pages

When migration of a VM is initiated, a first copy of the RAM is
transferred to the destination before the VM is stopped, but there is
no guarantee that the EQ pages in which the event notifications are
queued have not been modified.

To make sure migration will capture a consistent memory state, the
XIVE device should perform a XIVE quiesce sequence to stop the flow of
event notifications and stabilize the EQs. This is the purpose of the
KVM_DEV_XIVE_EQ_SYNC control which will also marks the EQ pages dirty
to force their transfer.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
hifive-unleashed-5.2
Cédric Le Goater 2019-04-18 12:39:34 +02:00 committed by Paul Mackerras
parent 7b46b6169a
commit e6714bd167
3 changed files with 115 additions and 0 deletions

View File

@ -23,6 +23,12 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
queues. To be used by kexec and kdump.
Errors: none
1.2 KVM_DEV_XIVE_EQ_SYNC (write only)
Sync all the sources and queues and mark the EQ pages dirty. This
to make sure that a consistent memory state is captured when
migrating the VM.
Errors: none
2. KVM_DEV_XIVE_GRP_SOURCE (write only)
Initializes a new source in the XIVE device and mask it.
Attributes:
@ -100,3 +106,26 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
Errors:
-ENOENT: Unknown source number
-EINVAL: Not initialized source number
* Migration:
Saving the state of a VM using the XIVE native exploitation mode
should follow a specific sequence. When the VM is stopped :
1. Mask all sources (PQ=01) to stop the flow of events.
2. Sync the XIVE device with the KVM control KVM_DEV_XIVE_EQ_SYNC to
flush any in-flight event notification and to stabilize the EQs. At
this stage, the EQ pages are marked dirty to make sure they are
transferred in the migration sequence.
3. Capture the state of the source targeting, the EQs configuration
and the state of thread interrupt context registers.
Restore is similar :
1. Restore the EQ configuration. As targeting depends on it.
2. Restore targeting
3. Restore the thread interrupt contexts
4. Restore the source states
5. Let the vCPU run

View File

@ -680,6 +680,7 @@ struct kvm_ppc_cpu_char {
/* POWER9 XIVE Native Interrupt Controller */
#define KVM_DEV_XIVE_GRP_CTRL 1
#define KVM_DEV_XIVE_RESET 1
#define KVM_DEV_XIVE_EQ_SYNC 2
#define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */
#define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */
#define KVM_DEV_XIVE_GRP_EQ_CONFIG 4 /* 64-bit EQ identifier */

View File

@ -681,6 +681,88 @@ static int kvmppc_xive_reset(struct kvmppc_xive *xive)
return 0;
}
static void kvmppc_xive_native_sync_sources(struct kvmppc_xive_src_block *sb)
{
int j;
for (j = 0; j < KVMPPC_XICS_IRQ_PER_ICS; j++) {
struct kvmppc_xive_irq_state *state = &sb->irq_state[j];
struct xive_irq_data *xd;
u32 hw_num;
if (!state->valid)
continue;
/*
* The struct kvmppc_xive_irq_state reflects the state
* of the EAS configuration and not the state of the
* source. The source is masked setting the PQ bits to
* '-Q', which is what is being done before calling
* the KVM_DEV_XIVE_EQ_SYNC control.
*
* If a source EAS is configured, OPAL syncs the XIVE
* IC of the source and the XIVE IC of the previous
* target if any.
*
* So it should be fine ignoring MASKED sources as
* they have been synced already.
*/
if (state->act_priority == MASKED)
continue;
kvmppc_xive_select_irq(state, &hw_num, &xd);
xive_native_sync_source(hw_num);
xive_native_sync_queue(hw_num);
}
}
static int kvmppc_xive_native_vcpu_eq_sync(struct kvm_vcpu *vcpu)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
unsigned int prio;
if (!xc)
return -ENOENT;
for (prio = 0; prio < KVMPPC_XIVE_Q_COUNT; prio++) {
struct xive_q *q = &xc->queues[prio];
if (!q->qpage)
continue;
/* Mark EQ page dirty for migration */
mark_page_dirty(vcpu->kvm, gpa_to_gfn(q->guest_qaddr));
}
return 0;
}
static int kvmppc_xive_native_eq_sync(struct kvmppc_xive *xive)
{
struct kvm *kvm = xive->kvm;
struct kvm_vcpu *vcpu;
unsigned int i;
pr_devel("%s\n", __func__);
mutex_lock(&kvm->lock);
for (i = 0; i <= xive->max_sbid; i++) {
struct kvmppc_xive_src_block *sb = xive->src_blocks[i];
if (sb) {
arch_spin_lock(&sb->lock);
kvmppc_xive_native_sync_sources(sb);
arch_spin_unlock(&sb->lock);
}
}
kvm_for_each_vcpu(i, vcpu, kvm) {
kvmppc_xive_native_vcpu_eq_sync(vcpu);
}
mutex_unlock(&kvm->lock);
return 0;
}
static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
struct kvm_device_attr *attr)
{
@ -691,6 +773,8 @@ static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
switch (attr->attr) {
case KVM_DEV_XIVE_RESET:
return kvmppc_xive_reset(xive);
case KVM_DEV_XIVE_EQ_SYNC:
return kvmppc_xive_native_eq_sync(xive);
}
break;
case KVM_DEV_XIVE_GRP_SOURCE:
@ -729,6 +813,7 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev,
case KVM_DEV_XIVE_GRP_CTRL:
switch (attr->attr) {
case KVM_DEV_XIVE_RESET:
case KVM_DEV_XIVE_EQ_SYNC:
return 0;
}
break;