Commit e8676ce5 authored by Cédric Le Goater's avatar Cédric Le Goater Committed by Paul Mackerras

KVM: PPC: Book3S HV: XIVE: Add a control to configure a source

This control will be used by the H_INT_SET_SOURCE_CONFIG hcall from
QEMU to configure the target of a source and also to restore the
configuration of a source when migrating the VM.

The XIVE source interrupt structure is extended with the value of the
Effective Interrupt Source Number. The EISN is the interrupt number
pushed in the event queue that the guest OS will use to dispatch
events internally. Caching the EISN value in KVM eases the test when
checking if a reconfiguration is indeed needed.
Signed-off-by: default avatarCédric Le Goater <clg@kaod.org>
Reviewed-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
parent 4131f83c
...@@ -32,3 +32,24 @@ the legacy interrupt mode, referred as XICS (POWER7/8). ...@@ -32,3 +32,24 @@ the legacy interrupt mode, referred as XICS (POWER7/8).
-ENOMEM: Could not create a new source block -ENOMEM: Could not create a new source block
-EFAULT: Invalid user pointer for attr->addr. -EFAULT: Invalid user pointer for attr->addr.
-ENXIO: Could not allocate underlying HW interrupt -ENXIO: Could not allocate underlying HW interrupt
3. KVM_DEV_XIVE_GRP_SOURCE_CONFIG (write only)
Configures source targeting
Attributes:
Interrupt source number (64-bit)
The kvm_device_attr.addr points to a __u64 value:
bits: | 63 .... 33 | 32 | 31 .. 3 | 2 .. 0
values: | eisn | mask | server | priority
- priority: 0-7 interrupt priority level
- server: CPU number chosen to handle the interrupt
- mask: mask flag (unused)
- eisn: Effective Interrupt Source Number
Errors:
-ENOENT: Unknown source number
-EINVAL: Not initialized source number
-EINVAL: Invalid priority
-EINVAL: Invalid CPU number.
-EFAULT: Invalid user pointer for attr->addr.
-ENXIO: CPU event queues not configured or configuration of the
underlying HW interrupt failed
-EBUSY: No CPU available to serve interrupt
...@@ -680,9 +680,20 @@ struct kvm_ppc_cpu_char { ...@@ -680,9 +680,20 @@ struct kvm_ppc_cpu_char {
/* POWER9 XIVE Native Interrupt Controller */ /* POWER9 XIVE Native Interrupt Controller */
#define KVM_DEV_XIVE_GRP_CTRL 1 #define KVM_DEV_XIVE_GRP_CTRL 1
#define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */ #define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */
#define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */
/* Layout of 64-bit XIVE source attribute values */ /* Layout of 64-bit XIVE source attribute values */
#define KVM_XIVE_LEVEL_SENSITIVE (1ULL << 0) #define KVM_XIVE_LEVEL_SENSITIVE (1ULL << 0)
#define KVM_XIVE_LEVEL_ASSERTED (1ULL << 1) #define KVM_XIVE_LEVEL_ASSERTED (1ULL << 1)
/* Layout of 64-bit XIVE source configuration attribute values */
#define KVM_XIVE_SOURCE_PRIORITY_SHIFT 0
#define KVM_XIVE_SOURCE_PRIORITY_MASK 0x7
#define KVM_XIVE_SOURCE_SERVER_SHIFT 3
#define KVM_XIVE_SOURCE_SERVER_MASK 0xfffffff8ULL
#define KVM_XIVE_SOURCE_MASKED_SHIFT 32
#define KVM_XIVE_SOURCE_MASKED_MASK 0x100000000ULL
#define KVM_XIVE_SOURCE_EISN_SHIFT 33
#define KVM_XIVE_SOURCE_EISN_MASK 0xfffffffe00000000ULL
#endif /* __LINUX_KVM_POWERPC_H */ #endif /* __LINUX_KVM_POWERPC_H */
...@@ -342,7 +342,7 @@ static int xive_try_pick_queue(struct kvm_vcpu *vcpu, u8 prio) ...@@ -342,7 +342,7 @@ static int xive_try_pick_queue(struct kvm_vcpu *vcpu, u8 prio)
return atomic_add_unless(&q->count, 1, max) ? 0 : -EBUSY; return atomic_add_unless(&q->count, 1, max) ? 0 : -EBUSY;
} }
static int xive_select_target(struct kvm *kvm, u32 *server, u8 prio) int kvmppc_xive_select_target(struct kvm *kvm, u32 *server, u8 prio)
{ {
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
int i, rc; int i, rc;
...@@ -530,7 +530,7 @@ static int xive_target_interrupt(struct kvm *kvm, ...@@ -530,7 +530,7 @@ static int xive_target_interrupt(struct kvm *kvm,
* priority. The count for that new target will have * priority. The count for that new target will have
* already been incremented. * already been incremented.
*/ */
rc = xive_select_target(kvm, &server, prio); rc = kvmppc_xive_select_target(kvm, &server, prio);
/* /*
* We failed to find a target ? Not much we can do * We failed to find a target ? Not much we can do
...@@ -1504,6 +1504,7 @@ struct kvmppc_xive_src_block *kvmppc_xive_create_src_block( ...@@ -1504,6 +1504,7 @@ struct kvmppc_xive_src_block *kvmppc_xive_create_src_block(
for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) { for (i = 0; i < KVMPPC_XICS_IRQ_PER_ICS; i++) {
sb->irq_state[i].number = (bid << KVMPPC_XICS_ICS_SHIFT) | i; sb->irq_state[i].number = (bid << KVMPPC_XICS_ICS_SHIFT) | i;
sb->irq_state[i].eisn = 0;
sb->irq_state[i].guest_priority = MASKED; sb->irq_state[i].guest_priority = MASKED;
sb->irq_state[i].saved_priority = MASKED; sb->irq_state[i].saved_priority = MASKED;
sb->irq_state[i].act_priority = MASKED; sb->irq_state[i].act_priority = MASKED;
......
...@@ -61,6 +61,9 @@ struct kvmppc_xive_irq_state { ...@@ -61,6 +61,9 @@ struct kvmppc_xive_irq_state {
bool saved_p; bool saved_p;
bool saved_q; bool saved_q;
u8 saved_scan_prio; u8 saved_scan_prio;
/* Xive native */
u32 eisn; /* Guest Effective IRQ number */
}; };
/* Select the "right" interrupt (IPI vs. passthrough) */ /* Select the "right" interrupt (IPI vs. passthrough) */
...@@ -268,6 +271,7 @@ int kvmppc_xive_debug_show_queues(struct seq_file *m, struct kvm_vcpu *vcpu); ...@@ -268,6 +271,7 @@ int kvmppc_xive_debug_show_queues(struct seq_file *m, struct kvm_vcpu *vcpu);
struct kvmppc_xive_src_block *kvmppc_xive_create_src_block( struct kvmppc_xive_src_block *kvmppc_xive_create_src_block(
struct kvmppc_xive *xive, int irq); struct kvmppc_xive *xive, int irq);
void kvmppc_xive_free_sources(struct kvmppc_xive_src_block *sb); void kvmppc_xive_free_sources(struct kvmppc_xive_src_block *sb);
int kvmppc_xive_select_target(struct kvm *kvm, u32 *server, u8 prio);
#endif /* CONFIG_KVM_XICS */ #endif /* CONFIG_KVM_XICS */
#endif /* _KVM_PPC_BOOK3S_XICS_H */ #endif /* _KVM_PPC_BOOK3S_XICS_H */
...@@ -242,6 +242,99 @@ static int kvmppc_xive_native_set_source(struct kvmppc_xive *xive, long irq, ...@@ -242,6 +242,99 @@ static int kvmppc_xive_native_set_source(struct kvmppc_xive *xive, long irq,
return rc; return rc;
} }
static int kvmppc_xive_native_update_source_config(struct kvmppc_xive *xive,
struct kvmppc_xive_src_block *sb,
struct kvmppc_xive_irq_state *state,
u32 server, u8 priority, bool masked,
u32 eisn)
{
struct kvm *kvm = xive->kvm;
u32 hw_num;
int rc = 0;
arch_spin_lock(&sb->lock);
if (state->act_server == server && state->act_priority == priority &&
state->eisn == eisn)
goto unlock;
pr_devel("new_act_prio=%d new_act_server=%d mask=%d act_server=%d act_prio=%d\n",
priority, server, masked, state->act_server,
state->act_priority);
kvmppc_xive_select_irq(state, &hw_num, NULL);
if (priority != MASKED && !masked) {
rc = kvmppc_xive_select_target(kvm, &server, priority);
if (rc)
goto unlock;
state->act_priority = priority;
state->act_server = server;
state->eisn = eisn;
rc = xive_native_configure_irq(hw_num,
kvmppc_xive_vp(xive, server),
priority, eisn);
} else {
state->act_priority = MASKED;
state->act_server = 0;
state->eisn = 0;
rc = xive_native_configure_irq(hw_num, 0, MASKED, 0);
}
unlock:
arch_spin_unlock(&sb->lock);
return rc;
}
static int kvmppc_xive_native_set_source_config(struct kvmppc_xive *xive,
long irq, u64 addr)
{
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u64 __user *ubufp = (u64 __user *) addr;
u16 src;
u64 kvm_cfg;
u32 server;
u8 priority;
bool masked;
u32 eisn;
sb = kvmppc_xive_find_source(xive, irq, &src);
if (!sb)
return -ENOENT;
state = &sb->irq_state[src];
if (!state->valid)
return -EINVAL;
if (get_user(kvm_cfg, ubufp))
return -EFAULT;
pr_devel("%s irq=0x%lx cfg=%016llx\n", __func__, irq, kvm_cfg);
priority = (kvm_cfg & KVM_XIVE_SOURCE_PRIORITY_MASK) >>
KVM_XIVE_SOURCE_PRIORITY_SHIFT;
server = (kvm_cfg & KVM_XIVE_SOURCE_SERVER_MASK) >>
KVM_XIVE_SOURCE_SERVER_SHIFT;
masked = (kvm_cfg & KVM_XIVE_SOURCE_MASKED_MASK) >>
KVM_XIVE_SOURCE_MASKED_SHIFT;
eisn = (kvm_cfg & KVM_XIVE_SOURCE_EISN_MASK) >>
KVM_XIVE_SOURCE_EISN_SHIFT;
if (priority != xive_prio_from_guest(priority)) {
pr_err("invalid priority for queue %d for VCPU %d\n",
priority, server);
return -EINVAL;
}
return kvmppc_xive_native_update_source_config(xive, sb, state, server,
priority, masked, eisn);
}
static int kvmppc_xive_native_set_attr(struct kvm_device *dev, static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
struct kvm_device_attr *attr) struct kvm_device_attr *attr)
{ {
...@@ -253,6 +346,9 @@ static int kvmppc_xive_native_set_attr(struct kvm_device *dev, ...@@ -253,6 +346,9 @@ static int kvmppc_xive_native_set_attr(struct kvm_device *dev,
case KVM_DEV_XIVE_GRP_SOURCE: case KVM_DEV_XIVE_GRP_SOURCE:
return kvmppc_xive_native_set_source(xive, attr->attr, return kvmppc_xive_native_set_source(xive, attr->attr,
attr->addr); attr->addr);
case KVM_DEV_XIVE_GRP_SOURCE_CONFIG:
return kvmppc_xive_native_set_source_config(xive, attr->attr,
attr->addr);
} }
return -ENXIO; return -ENXIO;
} }
...@@ -270,6 +366,7 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev, ...@@ -270,6 +366,7 @@ static int kvmppc_xive_native_has_attr(struct kvm_device *dev,
case KVM_DEV_XIVE_GRP_CTRL: case KVM_DEV_XIVE_GRP_CTRL:
break; break;
case KVM_DEV_XIVE_GRP_SOURCE: case KVM_DEV_XIVE_GRP_SOURCE:
case KVM_DEV_XIVE_GRP_SOURCE_CONFIG:
if (attr->attr >= KVMPPC_XIVE_FIRST_IRQ && if (attr->attr >= KVMPPC_XIVE_FIRST_IRQ &&
attr->attr < KVMPPC_XIVE_NR_IRQS) attr->attr < KVMPPC_XIVE_NR_IRQS)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment