![Sam Bobroff](/assets/img/avatar_default.png)
In KVM's XICS-on-XIVE emulation, kvmppc_xive_get_xive() returns the
value of state->guest_server as "server". However, this value is not
set by it's counterpart kvmppc_xive_set_xive(). When the guest uses
this interface to migrate interrupts away from a CPU that is going
offline, it sees all interrupts as belonging to CPU 0, so they are
left assigned to (now) offline CPUs.
This patch removes the guest_server field from the state, and returns
act_server in it's place (that is, the CPU actually handling the
interrupt, which may differ from the one requested).
Fixes: 5af5099385
("KVM: PPC: Book3S HV: Native usage of the XIVE interrupt controller")
Cc: stable@vger.kernel.org
Signed-off-by: Sam Bobroff <sam.bobroff@au1.ibm.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
256 lines
6.6 KiB
C
256 lines
6.6 KiB
C
/*
|
|
* Copyright 2017 Benjamin Herrenschmidt, IBM Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2, as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#ifndef _KVM_PPC_BOOK3S_XIVE_H
|
|
#define _KVM_PPC_BOOK3S_XIVE_H
|
|
|
|
#ifdef CONFIG_KVM_XICS
|
|
#include "book3s_xics.h"
|
|
|
|
/*
|
|
* State for one guest irq source.
|
|
*
|
|
* For each guest source we allocate a HW interrupt in the XIVE
|
|
* which we use for all SW triggers. It will be unused for
|
|
* pass-through but it's easier to keep around as the same
|
|
* guest interrupt can alternatively be emulated or pass-through
|
|
* if a physical device is hot unplugged and replaced with an
|
|
* emulated one.
|
|
*
|
|
* This state structure is very similar to the XICS one with
|
|
* additional XIVE specific tracking.
|
|
*/
|
|
struct kvmppc_xive_irq_state {
|
|
bool valid; /* Interrupt entry is valid */
|
|
|
|
u32 number; /* Guest IRQ number */
|
|
u32 ipi_number; /* XIVE IPI HW number */
|
|
struct xive_irq_data ipi_data; /* XIVE IPI associated data */
|
|
u32 pt_number; /* XIVE Pass-through number if any */
|
|
struct xive_irq_data *pt_data; /* XIVE Pass-through associated data */
|
|
|
|
/* Targetting as set by guest */
|
|
u8 guest_priority; /* Guest set priority */
|
|
u8 saved_priority; /* Saved priority when masking */
|
|
|
|
/* Actual targetting */
|
|
u32 act_server; /* Actual server */
|
|
u8 act_priority; /* Actual priority */
|
|
|
|
/* Various state bits */
|
|
bool in_eoi; /* Synchronize with H_EOI */
|
|
bool old_p; /* P bit state when masking */
|
|
bool old_q; /* Q bit state when masking */
|
|
bool lsi; /* level-sensitive interrupt */
|
|
bool asserted; /* Only for emulated LSI: current state */
|
|
|
|
/* Saved for migration state */
|
|
bool in_queue;
|
|
bool saved_p;
|
|
bool saved_q;
|
|
u8 saved_scan_prio;
|
|
};
|
|
|
|
/* Select the "right" interrupt (IPI vs. passthrough) */
|
|
static inline void kvmppc_xive_select_irq(struct kvmppc_xive_irq_state *state,
|
|
u32 *out_hw_irq,
|
|
struct xive_irq_data **out_xd)
|
|
{
|
|
if (state->pt_number) {
|
|
if (out_hw_irq)
|
|
*out_hw_irq = state->pt_number;
|
|
if (out_xd)
|
|
*out_xd = state->pt_data;
|
|
} else {
|
|
if (out_hw_irq)
|
|
*out_hw_irq = state->ipi_number;
|
|
if (out_xd)
|
|
*out_xd = &state->ipi_data;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This corresponds to an "ICS" in XICS terminology, we use it
|
|
* as a mean to break up source information into multiple structures.
|
|
*/
|
|
struct kvmppc_xive_src_block {
|
|
arch_spinlock_t lock;
|
|
u16 id;
|
|
struct kvmppc_xive_irq_state irq_state[KVMPPC_XICS_IRQ_PER_ICS];
|
|
};
|
|
|
|
|
|
struct kvmppc_xive {
|
|
struct kvm *kvm;
|
|
struct kvm_device *dev;
|
|
struct dentry *dentry;
|
|
|
|
/* VP block associated with the VM */
|
|
u32 vp_base;
|
|
|
|
/* Blocks of sources */
|
|
struct kvmppc_xive_src_block *src_blocks[KVMPPC_XICS_MAX_ICS_ID + 1];
|
|
u32 max_sbid;
|
|
|
|
/*
|
|
* For state save, we lazily scan the queues on the first interrupt
|
|
* being migrated. We don't have a clean way to reset that flags
|
|
* so we keep track of the number of valid sources and how many of
|
|
* them were migrated so we can reset when all of them have been
|
|
* processed.
|
|
*/
|
|
u32 src_count;
|
|
u32 saved_src_count;
|
|
|
|
/*
|
|
* Some irqs are delayed on restore until the source is created,
|
|
* keep track here of how many of them
|
|
*/
|
|
u32 delayed_irqs;
|
|
|
|
/* Which queues (priorities) are in use by the guest */
|
|
u8 qmap;
|
|
|
|
/* Queue orders */
|
|
u32 q_order;
|
|
u32 q_page_order;
|
|
|
|
};
|
|
|
|
#define KVMPPC_XIVE_Q_COUNT 8
|
|
|
|
struct kvmppc_xive_vcpu {
|
|
struct kvmppc_xive *xive;
|
|
struct kvm_vcpu *vcpu;
|
|
bool valid;
|
|
|
|
/* Server number. This is the HW CPU ID from a guest perspective */
|
|
u32 server_num;
|
|
|
|
/*
|
|
* HW VP corresponding to this VCPU. This is the base of the VP
|
|
* block plus the server number.
|
|
*/
|
|
u32 vp_id;
|
|
u32 vp_chip_id;
|
|
u32 vp_cam;
|
|
|
|
/* IPI used for sending ... IPIs */
|
|
u32 vp_ipi;
|
|
struct xive_irq_data vp_ipi_data;
|
|
|
|
/* Local emulation state */
|
|
uint8_t cppr; /* guest CPPR */
|
|
uint8_t hw_cppr;/* Hardware CPPR */
|
|
uint8_t mfrr;
|
|
uint8_t pending;
|
|
|
|
/* Each VP has 8 queues though we only provision some */
|
|
struct xive_q queues[KVMPPC_XIVE_Q_COUNT];
|
|
u32 esc_virq[KVMPPC_XIVE_Q_COUNT];
|
|
char *esc_virq_names[KVMPPC_XIVE_Q_COUNT];
|
|
|
|
/* Stash a delayed irq on restore from migration (see set_icp) */
|
|
u32 delayed_irq;
|
|
|
|
/* Stats */
|
|
u64 stat_rm_h_xirr;
|
|
u64 stat_rm_h_ipoll;
|
|
u64 stat_rm_h_cppr;
|
|
u64 stat_rm_h_eoi;
|
|
u64 stat_rm_h_ipi;
|
|
u64 stat_vm_h_xirr;
|
|
u64 stat_vm_h_ipoll;
|
|
u64 stat_vm_h_cppr;
|
|
u64 stat_vm_h_eoi;
|
|
u64 stat_vm_h_ipi;
|
|
};
|
|
|
|
static inline struct kvm_vcpu *kvmppc_xive_find_server(struct kvm *kvm, u32 nr)
|
|
{
|
|
struct kvm_vcpu *vcpu = NULL;
|
|
int i;
|
|
|
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
|
if (vcpu->arch.xive_vcpu && nr == vcpu->arch.xive_vcpu->server_num)
|
|
return vcpu;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct kvmppc_xive_src_block *kvmppc_xive_find_source(struct kvmppc_xive *xive,
|
|
u32 irq, u16 *source)
|
|
{
|
|
u32 bid = irq >> KVMPPC_XICS_ICS_SHIFT;
|
|
u16 src = irq & KVMPPC_XICS_SRC_MASK;
|
|
|
|
if (source)
|
|
*source = src;
|
|
if (bid > KVMPPC_XICS_MAX_ICS_ID)
|
|
return NULL;
|
|
return xive->src_blocks[bid];
|
|
}
|
|
|
|
/*
|
|
* Mapping between guest priorities and host priorities
|
|
* is as follow.
|
|
*
|
|
* Guest request for 0...6 are honored. Guest request for anything
|
|
* higher results in a priority of 7 being applied.
|
|
*
|
|
* However, when XIRR is returned via H_XIRR, 7 is translated to 0xb
|
|
* in order to match AIX expectations
|
|
*
|
|
* Similar mapping is done for CPPR values
|
|
*/
|
|
static inline u8 xive_prio_from_guest(u8 prio)
|
|
{
|
|
if (prio == 0xff || prio < 8)
|
|
return prio;
|
|
return 7;
|
|
}
|
|
|
|
static inline u8 xive_prio_to_guest(u8 prio)
|
|
{
|
|
if (prio == 0xff || prio < 7)
|
|
return prio;
|
|
return 0xb;
|
|
}
|
|
|
|
static inline u32 __xive_read_eq(__be32 *qpage, u32 msk, u32 *idx, u32 *toggle)
|
|
{
|
|
u32 cur;
|
|
|
|
if (!qpage)
|
|
return 0;
|
|
cur = be32_to_cpup(qpage + *idx);
|
|
if ((cur >> 31) == *toggle)
|
|
return 0;
|
|
*idx = (*idx + 1) & msk;
|
|
if (*idx == 0)
|
|
(*toggle) ^= 1;
|
|
return cur & 0x7fffffff;
|
|
}
|
|
|
|
extern unsigned long xive_rm_h_xirr(struct kvm_vcpu *vcpu);
|
|
extern unsigned long xive_rm_h_ipoll(struct kvm_vcpu *vcpu, unsigned long server);
|
|
extern int xive_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
|
unsigned long mfrr);
|
|
extern int xive_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr);
|
|
extern int xive_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr);
|
|
|
|
extern unsigned long (*__xive_vm_h_xirr)(struct kvm_vcpu *vcpu);
|
|
extern unsigned long (*__xive_vm_h_ipoll)(struct kvm_vcpu *vcpu, unsigned long server);
|
|
extern int (*__xive_vm_h_ipi)(struct kvm_vcpu *vcpu, unsigned long server,
|
|
unsigned long mfrr);
|
|
extern int (*__xive_vm_h_cppr)(struct kvm_vcpu *vcpu, unsigned long cppr);
|
|
extern int (*__xive_vm_h_eoi)(struct kvm_vcpu *vcpu, unsigned long xirr);
|
|
|
|
#endif /* CONFIG_KVM_XICS */
|
|
#endif /* _KVM_PPC_BOOK3S_XICS_H */
|