Release 4.14 arch/powerpc/kvm/book3s_xive.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.
*/
#define pr_fmt(fmt) "xive-kvm: " fmt
#include <linux/kernel.h>
#include <linux/kvm_host.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/percpu.h>
#include <linux/cpumask.h>
#include <asm/uaccess.h>
#include <asm/kvm_book3s.h>
#include <asm/kvm_ppc.h>
#include <asm/hvcall.h>
#include <asm/xics.h>
#include <asm/xive.h>
#include <asm/xive-regs.h>
#include <asm/debug.h>
#include <asm/debugfs.h>
#include <asm/time.h>
#include <asm/opal.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "book3s_xive.h"
/*
* Virtual mode variants of the hcalls for use on radix/radix
* with AIL. They require the VCPU's VP to be "pushed"
*
* We still instanciate them here because we use some of the
* generated utility functions as well in this file.
*/
#define XIVE_RUNTIME_CHECKS
#define X_PFX xive_vm_
#define X_STATIC static
#define X_STAT_PFX stat_vm_
#define __x_tima xive_tima
#define __x_eoi_page(xd) ((void __iomem *)((xd)->eoi_mmio))
#define __x_trig_page(xd) ((void __iomem *)((xd)->trig_mmio))
#define __x_writeb __raw_writeb
#define __x_readw __raw_readw
#define __x_readq __raw_readq
#define __x_writeq __raw_writeq
#include "book3s_xive_template.c"
/*
* We leave a gap of a couple of interrupts in the queue to
* account for the IPI and additional safety guard.
*/
#define XIVE_Q_GAP 2
/*
* This is a simple trigger for a generic XIVE IRQ. This must
* only be called for interrupts that support a trigger page
*/
static bool xive_irq_trigger(struct xive_irq_data *xd)
{
/* This should be only for MSIs */
if (WARN_ON(xd->flags & XIVE_IRQ_FLAG_LSI))
return false;
/* Those interrupts should always have a trigger page */
if (WARN_ON(!xd->trig_mmio))
return false;
out_be64(xd->trig_mmio, 0);
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 52 | 100.00% | 1 | 100.00% |
Total | 52 | 100.00% | 1 | 100.00% |
static irqreturn_t xive_esc_irq(int irq, void *data)
{
struct kvm_vcpu *vcpu = data;
/* We use the existing H_PROD mechanism to wake up the target */
vcpu->arch.prodded = 1;
smp_mb();
if (vcpu->arch.ceded)
kvmppc_fast_vcpu_kick(vcpu);
return IRQ_HANDLED;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 48 | 100.00% | 1 | 100.00% |
Total | 48 | 100.00% | 1 | 100.00% |
static int xive_attach_escalation(struct kvm_vcpu *vcpu, u8 prio)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
struct xive_q *q = &xc->queues[prio];
char *name = NULL;
int rc;
/* Already there ? */
if (xc->esc_virq[prio])
return 0;
/* Hook up the escalation interrupt */
xc->esc_virq[prio] = irq_create_mapping(NULL, q->esc_irq);
if (!xc->esc_virq[prio]) {
pr_err("Failed to map escalation interrupt for queue %d of VCPU %d\n",
prio, xc->server_num);
return -EIO;
}
/*
* Future improvement: start with them disabled
* and handle DD2 and later scheme of merged escalation
* interrupts
*/
name = kasprintf(GFP_KERNEL, "kvm-%d-%d-%d",
vcpu->kvm->arch.lpid, xc->server_num, prio);
if (!name) {
pr_err("Failed to allocate escalation irq name for queue %d of VCPU %d\n",
prio, xc->server_num);
rc = -ENOMEM;
goto error;
}
rc = request_irq(xc->esc_virq[prio], xive_esc_irq,
IRQF_NO_THREAD, name, vcpu);
if (rc) {
pr_err("Failed to request escalation interrupt for queue %d of VCPU %d\n",
prio, xc->server_num);
goto error;
}
xc->esc_virq_names[prio] = name;
return 0;
error:
irq_dispose_mapping(xc->esc_virq[prio]);
xc->esc_virq[prio] = 0;
kfree(name);
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 235 | 100.00% | 1 | 100.00% |
Total | 235 | 100.00% | 1 | 100.00% |
static int xive_provision_queue(struct kvm_vcpu *vcpu, u8 prio)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
struct kvmppc_xive *xive = xc->xive;
struct xive_q *q = &xc->queues[prio];
void *qpage;
int rc;
if (WARN_ON(q->qpage))
return 0;
/* Allocate the queue and retrieve infos on current node for now */
qpage = (__be32 *)__get_free_pages(GFP_KERNEL, xive->q_page_order);
if (!qpage) {
pr_err("Failed to allocate queue %d for VCPU %d\n",
prio, xc->server_num);
return -ENOMEM;;
}
memset(qpage, 0, 1 << xive->q_order);
/*
* Reconfigure the queue. This will set q->qpage only once the
* queue is fully configured. This is a requirement for prio 0
* as we will stop doing EOIs for every IPI as soon as we observe
* qpage being non-NULL, and instead will only EOI when we receive
* corresponding queue 0 entries
*/
rc = xive_native_configure_queue(xc->vp_id, q, prio, qpage,
xive->q_order, true);
if (rc)
pr_err("Failed to configure queue %d for VCPU %d\n",
prio, xc->server_num);
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 158 | 100.00% | 1 | 100.00% |
Total | 158 | 100.00% | 1 | 100.00% |
/* Called with kvm_lock held */
static int xive_check_provisioning(struct kvm *kvm, u8 prio)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvm_vcpu *vcpu;
int i, rc;
lockdep_assert_held(&kvm->lock);
/* Already provisioned ? */
if (xive->qmap & (1 << prio))
return 0;
pr_devel("Provisioning prio... %d\n", prio);
/* Provision each VCPU and enable escalations */
kvm_for_each_vcpu(i, vcpu, kvm) {
if (!vcpu->arch.xive_vcpu)
continue;
rc = xive_provision_queue(vcpu, prio);
if (rc == 0)
xive_attach_escalation(vcpu, prio);
if (rc)
return rc;
}
/* Order previous stores and mark it as provisioned */
mb();
xive->qmap |= (1 << prio);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 133 | 100.00% | 1 | 100.00% |
Total | 133 | 100.00% | 1 | 100.00% |
static void xive_inc_q_pending(struct kvm *kvm, u32 server, u8 prio)
{
struct kvm_vcpu *vcpu;
struct kvmppc_xive_vcpu *xc;
struct xive_q *q;
/* Locate target server */
vcpu = kvmppc_xive_find_server(kvm, server);
if (!vcpu) {
pr_warn("%s: Can't find server %d\n", __func__, server);
return;
}
xc = vcpu->arch.xive_vcpu;
if (WARN_ON(!xc))
return;
q = &xc->queues[prio];
atomic_inc(&q->pending_count);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 94 | 100.00% | 1 | 100.00% |
Total | 94 | 100.00% | 1 | 100.00% |
static int xive_try_pick_queue(struct kvm_vcpu *vcpu, u8 prio)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
struct xive_q *q;
u32 max;
if (WARN_ON(!xc))
return -ENXIO;
if (!xc->valid)
return -ENXIO;
q = &xc->queues[prio];
if (WARN_ON(!q->qpage))
return -ENXIO;
/* Calculate max number of interrupts in that queue. */
max = (q->msk + 1) - XIVE_Q_GAP;
return atomic_add_unless(&q->count, 1, max) ? 0 : -EBUSY;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 111 | 100.00% | 1 | 100.00% |
Total | 111 | 100.00% | 1 | 100.00% |
static int xive_select_target(struct kvm *kvm, u32 *server, u8 prio)
{
struct kvm_vcpu *vcpu;
int i, rc;
/* Locate target server */
vcpu = kvmppc_xive_find_server(kvm, *server);
if (!vcpu) {
pr_devel("Can't find server %d\n", *server);
return -EINVAL;
}
pr_devel("Finding irq target on 0x%x/%d...\n", *server, prio);
/* Try pick it */
rc = xive_try_pick_queue(vcpu, prio);
if (rc == 0)
return rc;
pr_devel(" .. failed, looking up candidate...\n");
/* Failed, pick another VCPU */
kvm_for_each_vcpu(i, vcpu, kvm) {
if (!vcpu->arch.xive_vcpu)
continue;
rc = xive_try_pick_queue(vcpu, prio);
if (rc == 0) {
*server = vcpu->arch.xive_vcpu->server_num;
pr_devel(" found on 0x%x/%d\n", *server, prio);
return rc;
}
}
pr_devel(" no available target !\n");
/* No available target ! */
return -EBUSY;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 164 | 100.00% | 1 | 100.00% |
Total | 164 | 100.00% | 1 | 100.00% |
static u8 xive_lock_and_mask(struct kvmppc_xive *xive,
struct kvmppc_xive_src_block *sb,
struct kvmppc_xive_irq_state *state)
{
struct xive_irq_data *xd;
u32 hw_num;
u8 old_prio;
u64 val;
/*
* Take the lock, set masked, try again if racing
* with H_EOI
*/
for (;;) {
arch_spin_lock(&sb->lock);
old_prio = state->guest_priority;
state->guest_priority = MASKED;
mb();
if (!state->in_eoi)
break;
state->guest_priority = old_prio;
arch_spin_unlock(&sb->lock);
}
/* No change ? Bail */
if (old_prio == MASKED)
return old_prio;
/* Get the right irq */
kvmppc_xive_select_irq(state, &hw_num, &xd);
/*
* If the interrupt is marked as needing masking via
* firmware, we do it here. Firmware masking however
* is "lossy", it won't return the old p and q bits
* and won't set the interrupt to a state where it will
* record queued ones. If this is an issue we should do
* lazy masking instead.
*
* For now, we work around this in unmask by forcing
* an interrupt whenever we unmask a non-LSI via FW
* (if ever).
*/
if (xd->flags & OPAL_XIVE_IRQ_MASK_VIA_FW) {
xive_native_configure_irq(hw_num,
xive->vp_base + state->act_server,
MASKED, state->number);
/* set old_p so we can track if an H_EOI was done */
state->old_p = true;
state->old_q = false;
} else {
/* Set PQ to 10, return old P and old Q and remember them */
val = xive_vm_esb_load(xd, XIVE_ESB_SET_PQ_10);
state->old_p = !!(val & 2);
state->old_q = !!(val & 1);
/*
* Synchronize hardware to sensure the queues are updated
* when masking
*/
xive_native_sync_source(hw_num);
}
return old_prio;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 199 | 100.00% | 1 | 100.00% |
Total | 199 | 100.00% | 1 | 100.00% |
static void xive_lock_for_unmask(struct kvmppc_xive_src_block *sb,
struct kvmppc_xive_irq_state *state)
{
/*
* Take the lock try again if racing with H_EOI
*/
for (;;) {
arch_spin_lock(&sb->lock);
if (!state->in_eoi)
break;
arch_spin_unlock(&sb->lock);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 48 | 100.00% | 1 | 100.00% |
Total | 48 | 100.00% | 1 | 100.00% |
static void xive_finish_unmask(struct kvmppc_xive *xive,
struct kvmppc_xive_src_block *sb,
struct kvmppc_xive_irq_state *state,
u8 prio)
{
struct xive_irq_data *xd;
u32 hw_num;
/* If we aren't changing a thing, move on */
if (state->guest_priority != MASKED)
goto bail;
/* Get the right irq */
kvmppc_xive_select_irq(state, &hw_num, &xd);
/*
* See command in xive_lock_and_mask() concerning masking
* via firmware.
*/
if (xd->flags & OPAL_XIVE_IRQ_MASK_VIA_FW) {
xive_native_configure_irq(hw_num,
xive->vp_base + state->act_server,
state->act_priority, state->number);
/* If an EOI is needed, do it here */
if (!state->old_p)
xive_vm_source_eoi(hw_num, xd);
/* If this is not an LSI, force a trigger */
if (!(xd->flags & OPAL_XIVE_IRQ_LSI))
xive_irq_trigger(xd);
goto bail;
}
/* Old Q set, set PQ to 11 */
if (state->old_q)
xive_vm_esb_load(xd, XIVE_ESB_SET_PQ_11);
/*
* If not old P, then perform an "effective" EOI,
* on the source. This will handle the cases where
* FW EOI is needed.
*/
if (!state->old_p)
xive_vm_source_eoi(hw_num, xd);
/* Synchronize ordering and mark unmasked */
mb();
bail:
state->guest_priority = prio;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 164 | 100.00% | 1 | 100.00% |
Total | 164 | 100.00% | 1 | 100.00% |
/*
* Target an interrupt to a given server/prio, this will fallback
* to another server if necessary and perform the HW targetting
* updates as needed
*
* NOTE: Must be called with the state lock held
*/
static int xive_target_interrupt(struct kvm *kvm,
struct kvmppc_xive_irq_state *state,
u32 server, u8 prio)
{
struct kvmppc_xive *xive = kvm->arch.xive;
u32 hw_num;
int rc;
/*
* This will return a tentative server and actual
* priority. The count for that new target will have
* already been incremented.
*/
rc = xive_select_target(kvm, &server, prio);
/*
* We failed to find a target ? Not much we can do
* at least until we support the GIQ.
*/
if (rc)
return rc;
/*
* Increment the old queue pending count if there
* was one so that the old queue count gets adjusted later
* when observed to be empty.
*/
if (state->act_priority != MASKED)
xive_inc_q_pending(kvm,
state->act_server,
state->act_priority);
/*
* Update state and HW
*/
state->act_priority = prio;
state->act_server = server;
/* Get the right irq */
kvmppc_xive_select_irq(state, &hw_num, NULL);
return xive_native_configure_irq(hw_num,
xive->vp_base + server,
prio, state->number);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 124 | 100.00% | 1 | 100.00% |
Total | 124 | 100.00% | 1 | 100.00% |
/*
* Targetting rules: In order to avoid losing track of
* pending interrupts accross mask and unmask, which would
* allow queue overflows, we implement the following rules:
*
* - Unless it was never enabled (or we run out of capacity)
* an interrupt is always targetted at a valid server/queue
* pair even when "masked" by the guest. This pair tends to
* be the last one used but it can be changed under some
* circumstances. That allows us to separate targetting
* from masking, we only handle accounting during (re)targetting,
* this also allows us to let an interrupt drain into its target
* queue after masking, avoiding complex schemes to remove
* interrupts out of remote processor queues.
*
* - When masking, we set PQ to 10 and save the previous value
* of P and Q.
*
* - When unmasking, if saved Q was set, we set PQ to 11
* otherwise we leave PQ to the HW state which will be either
* 10 if nothing happened or 11 if the interrupt fired while
* masked. Effectively we are OR'ing the previous Q into the
* HW Q.
*
* Then if saved P is clear, we do an effective EOI (Q->P->Trigger)
* which will unmask the interrupt and shoot a new one if Q was
* set.
*
* Otherwise (saved P is set) we leave PQ unchanged (so 10 or 11,
* effectively meaning an H_EOI from the guest is still expected
* for that interrupt).
*
* - If H_EOI occurs while masked, we clear the saved P.
*
* - When changing target, we account on the new target and
* increment a separate "pending" counter on the old one.
* This pending counter will be used to decrement the old
* target's count when its queue has been observed empty.
*/
int kvmppc_xive_set_xive(struct kvm *kvm, u32 irq, u32 server,
u32 priority)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u8 new_act_prio;
int rc = 0;
u16 idx;
if (!xive)
return -ENODEV;
pr_devel("set_xive ! irq 0x%x server 0x%x prio %d\n",
irq, server, priority);
/* First, check provisioning of queues */
if (priority != MASKED)
rc = xive_check_provisioning(xive->kvm,
xive_prio_from_guest(priority));
if (rc) {
pr_devel(" provisioning failure %d !\n", rc);
return rc;
}
sb = kvmppc_xive_find_source(xive, irq, &idx);
if (!sb)
return -EINVAL;
state = &sb->irq_state[idx];
/*
* We first handle masking/unmasking since the locking
* might need to be retried due to EOIs, we'll handle
* targetting changes later. These functions will return
* with the SB lock held.
*
* xive_lock_and_mask() will also set state->guest_priority
* but won't otherwise change other fields of the state.
*
* xive_lock_for_unmask will not actually unmask, this will
* be done later by xive_finish_unmask() once the targetting
* has been done, so we don't try to unmask an interrupt
* that hasn't yet been targetted.
*/
if (priority == MASKED)
xive_lock_and_mask(xive, sb, state);
else
xive_lock_for_unmask(sb, state);
/*
* Then we handle targetting.
*
* First calculate a new "actual priority"
*/
new_act_prio = state->act_priority;
if (priority != MASKED)
new_act_prio = xive_prio_from_guest(priority);
pr_devel(" new_act_prio=%x act_server=%x act_prio=%x\n",
new_act_prio, state->act_server, state->act_priority);
/*
* Then check if we actually need to change anything,
*
* The condition for re-targetting the interrupt is that
* we have a valid new priority (new_act_prio is not 0xff)
* and either the server or the priority changed.
*
* Note: If act_priority was ff and the new priority is
* also ff, we don't do anything and leave the interrupt
* untargetted. An attempt of doing an int_on on an
* untargetted interrupt will fail. If that is a problem
* we could initialize interrupts with valid default
*/
if (new_act_prio != MASKED &&
(state->act_server != server ||
state->act_priority != new_act_prio))
rc = xive_target_interrupt(kvm, state, server, new_act_prio);
/*
* Perform the final unmasking of the interrupt source
* if necessary
*/
if (priority != MASKED)
xive_finish_unmask(xive, sb, state, priority);
/*
* Finally Update saved_priority to match. Only int_on/off
* set this field to a different value.
*/
state->saved_priority = priority;
arch_spin_unlock(&sb->lock);
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 268 | 100.00% | 1 | 100.00% |
Total | 268 | 100.00% | 1 | 100.00% |
int kvmppc_xive_get_xive(struct kvm *kvm, u32 irq, u32 *server,
u32 *priority)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u16 idx;
if (!xive)
return -ENODEV;
sb = kvmppc_xive_find_source(xive, irq, &idx);
if (!sb)
return -EINVAL;
state = &sb->irq_state[idx];
arch_spin_lock(&sb->lock);
*server = state->act_server;
*priority = state->guest_priority;
arch_spin_unlock(&sb->lock);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 117 | 99.15% | 1 | 50.00% |
Sam Bobroff | 1 | 0.85% | 1 | 50.00% |
Total | 118 | 100.00% | 2 | 100.00% |
int kvmppc_xive_int_on(struct kvm *kvm, u32 irq)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u16 idx;
if (!xive)
return -ENODEV;
sb = kvmppc_xive_find_source(xive, irq, &idx);
if (!sb)
return -EINVAL;
state = &sb->irq_state[idx];
pr_devel("int_on(irq=0x%x)\n", irq);
/*
* Check if interrupt was not targetted
*/
if (state->act_priority == MASKED) {
pr_devel("int_on on untargetted interrupt\n");
return -EINVAL;
}
/* If saved_priority is 0xff, do nothing */
if (state->saved_priority == MASKED)
return 0;
/*
* Lock and unmask it.
*/
xive_lock_for_unmask(sb, state);
xive_finish_unmask(xive, sb, state, state->saved_priority);
arch_spin_unlock(&sb->lock);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 148 | 100.00% | 1 | 100.00% |
Total | 148 | 100.00% | 1 | 100.00% |
int kvmppc_xive_int_off(struct kvm *kvm, u32 irq)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u16 idx;
if (!xive)
return -ENODEV;
sb = kvmppc_xive_find_source(xive, irq, &idx);
if (!sb)
return -EINVAL;
state = &sb->irq_state[idx];
pr_devel("int_off(irq=0x%x)\n", irq);
/*
* Lock and mask
*/
state->saved_priority = xive_lock_and_mask(xive, sb, state);
arch_spin_unlock(&sb->lock);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 109 | 100.00% | 1 | 100.00% |
Total | 109 | 100.00% | 1 | 100.00% |
static bool xive_restore_pending_irq(struct kvmppc_xive *xive, u32 irq)
{
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
u16 idx;
sb = kvmppc_xive_find_source(xive, irq, &idx);
if (!sb)
return false;
state = &sb->irq_state[idx];
if (!state->valid)
return false;
/*
* Trigger the IPI. This assumes we never restore a pass-through
* interrupt which should be safe enough
*/
xive_irq_trigger(&state->ipi_data);
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 79 | 100.00% | 1 | 100.00% |
Total | 79 | 100.00% | 1 | 100.00% |
u64 kvmppc_xive_get_icp(struct kvm_vcpu *vcpu)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
if (!xc)
return 0;
/* Return the per-cpu state for state saving/migration */
return (u64)xc->cppr << KVM_REG_PPC_ICP_CPPR_SHIFT |
(u64)xc->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 49 | 100.00% | 1 | 100.00% |
Total | 49 | 100.00% | 1 | 100.00% |
int kvmppc_xive_set_icp(struct kvm_vcpu *vcpu, u64 icpval)
{
struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu;
struct kvmppc_xive *xive = vcpu->kvm->arch.xive;
u8 cppr, mfrr;
u32 xisr;
if (!xc || !xive)
return -ENOENT;
/* Grab individual state fields. We don't use pending_pri */
cppr = icpval >> KVM_REG_PPC_ICP_CPPR_SHIFT;
xisr = (icpval >> KVM_REG_PPC_ICP_XISR_SHIFT) &
KVM_REG_PPC_ICP_XISR_MASK;
mfrr = icpval >> KVM_REG_PPC_ICP_MFRR_SHIFT;
pr_devel("set_icp vcpu %d cppr=0x%x mfrr=0x%x xisr=0x%x\n",
xc->server_num, cppr, mfrr, xisr);
/*
* We can't update the state of a "pushed" VCPU, but that
* shouldn't happen.
*/
if (WARN_ON(vcpu->arch.xive_pushed))
return -EIO;
/* Update VCPU HW saved state */
vcpu->arch.xive_saved_state.cppr = cppr;
xc->hw_cppr = xc->cppr = cppr;
/*
* Update MFRR state. If it's not 0xff, we mark the VCPU as
* having a pending MFRR change, which will re-evaluate the
* target. The VCPU will thus potentially get a spurious
* interrupt but that's not a big deal.
*/
xc->mfrr = mfrr;
if (mfrr < cppr)
xive_irq_trigger(&xc->vp_ipi_data);
/*
* Now saved XIRR is "interesting". It means there's something in
* the legacy "1 element" queue... for an IPI we simply ignore it,
* as the MFRR restore will handle that. For anything else we need
* to force a resend of the source.
* However the source may not have been setup yet. If that's the
* case, we keep that info and increment a counter in the xive to
* tell subsequent xive_set_source() to go look.
*/
if (xisr > XICS_IPI && !xive_restore_pending_irq(xive, xisr)) {
xc->delayed_irq = xisr;
xive->delayed_irqs++;
pr_devel(" xisr restore delayed\n");
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Benjamin Herrenschmidt | 189 | 100.00% | 1 | 100.00% |
Total | 189 | 100.00% | 1 | 100.00% |
int kvmppc_xive_set_mapped(struct kvm *kvm, unsigned long guest_irq,
struct irq_desc *host_desc)
{
struct kvmppc_xive *xive = kvm->arch.xive;
struct kvmppc_xive_src_block *sb;
struct kvmppc_xive_irq_state *state;
struct irq_data *host_data = irq_desc_get_irq_data(host_desc);
unsigned int host_irq = irq_desc_get_irq(host_desc);
unsigned int hw_irq = (unsigned int)irqd_to_hwirq(host_data);
u16 idx;
u8 prio;
int rc;
if (!xive)
return -ENODEV;
pr_devel("set_mapped girq 0x%lx host HW irq 0x%x...\n",guest_irq, hw_irq);
sb = kvmppc_xive_find_source(xive, guest_irq, &idx);
if (!sb)
return -EINVAL;
state = &sb->irq_state[idx];
/*
* Mark the passed-through interrupt as going to a VCPU,
* this will prevent further EOIs and similar operations
* from the XIVE code. It will also mask the interrupt
* to either PQ=10 or 11 state, the latter if the interrupt
* is pending. This will allow us to unmask or retrigger it
* after routing it to the guest with a simple EOI.
*
* The "state" argument is a "token", all it needs is to be
* non-NULL to switch to passed-through or NULL for the
* other way around. We may not yet have an actual VCPU
* target here and we don't really care.
*/
rc = irq_set_vcpu_affinity(host_irq, state);
if (rc) {
pr_err("Failed to set VCPU affinity for irq %d\n",