/* * Return value ideally indicates how the interrupt was handled, but no * callers look at it (given that we don't implement KVM_IRQ_LINE_STATUS), * so just return 0. */ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level) { struct ics_irq_state *state; struct kvmppc_ics *ics; u16 src; XICS_DBG("ics deliver %#x (level: %d)\n", irq, level); ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) { XICS_DBG("ics_deliver_irq: IRQ 0x%06x not found !\n", irq); return -EINVAL; } state = &ics->irq_state[src]; if (!state->exists) return -EINVAL; /* * We set state->asserted locklessly. This should be fine as * we are the only setter, thus concurrent access is undefined * to begin with. */ if (level == 1 || level == KVM_INTERRUPT_SET_LEVEL) state->asserted = 1; else if (level == 0 || level == KVM_INTERRUPT_UNSET) { state->asserted = 0; return 0; } /* Attempt delivery */ icp_deliver_irq(xics, NULL, irq); return 0; }
int kvmppc_xics_int_on(struct kvm *kvm, u32 irq) { struct kvmppc_xics *xics = kvm->arch.xics; struct kvmppc_icp *icp; struct kvmppc_ics *ics; struct ics_irq_state *state; u16 src; if (!xics) return -ENODEV; ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) return -EINVAL; state = &ics->irq_state[src]; icp = kvmppc_xics_find_server(kvm, state->server); if (!icp) return -EINVAL; if (write_xive(xics, ics, state, state->server, state->saved_priority, state->saved_priority)) icp_deliver_irq(xics, icp, irq); return 0; }
int kvmppc_xics_get_xive(struct kvm *kvm, u32 irq, u32 *server, u32 *priority) { struct kvmppc_xics *xics = kvm->arch.xics; struct kvmppc_ics *ics; struct ics_irq_state *state; u16 src; unsigned long flags; if (!xics) return -ENODEV; ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) return -EINVAL; state = &ics->irq_state[src]; local_irq_save(flags); arch_spin_lock(&ics->lock); *server = state->server; *priority = state->priority; arch_spin_unlock(&ics->lock); local_irq_restore(flags); return 0; }
int kvmppc_xics_set_xive(struct kvm *kvm, u32 irq, u32 server, u32 priority) { struct kvmppc_xics *xics = kvm->arch.xics; struct kvmppc_icp *icp; struct kvmppc_ics *ics; struct ics_irq_state *state; u16 src; if (!xics) return -ENODEV; ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) return -EINVAL; state = &ics->irq_state[src]; icp = kvmppc_xics_find_server(kvm, server); if (!icp) return -EINVAL; XICS_DBG("set_xive %#x server %#x prio %#x MP:%d RS:%d\n", irq, server, priority, state->masked_pending, state->resend); if (write_xive(xics, ics, state, server, priority, priority)) icp_deliver_irq(xics, icp, irq); return 0; }
/* * Return value ideally indicates how the interrupt was handled, but no * callers look at it (given that we don't implement KVM_IRQ_LINE_STATUS), * so just return 0. */ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level) { struct ics_irq_state *state; struct kvmppc_ics *ics; u16 src; u32 pq_old, pq_new; XICS_DBG("ics deliver %#x (level: %d)\n", irq, level); ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) { XICS_DBG("ics_deliver_irq: IRQ 0x%06x not found !\n", irq); return -EINVAL; } state = &ics->irq_state[src]; if (!state->exists) return -EINVAL; if (level == KVM_INTERRUPT_SET_LEVEL || level == KVM_INTERRUPT_SET) level = 1; else if (level == KVM_INTERRUPT_UNSET) level = 0; /* * Take other values the same as 1, consistent with original code. * maybe WARN here? */ if (!state->lsi && level == 0) /* noop for MSI */ return 0; do { pq_old = state->pq_state; if (state->lsi) { if (level) { if (pq_old & PQ_PRESENTED) /* Setting already set LSI ... */ return 0; pq_new = PQ_PRESENTED; } else pq_new = 0; } else pq_new = ((pq_old << 1) & 3) | PQ_PRESENTED; } while (cmpxchg(&state->pq_state, pq_old, pq_new) != pq_old); /* Test P=1, Q=0, this is the only case where we present */ if (pq_new == PQ_PRESENTED) icp_deliver_irq(xics, NULL, irq, false); /* Record which CPU this arrived on for passed-through interrupts */ if (state->host_irq) state->intr_cpu = raw_smp_processor_id(); return 0; }
int kvmppc_xics_int_off(struct kvm *kvm, u32 irq) { struct kvmppc_xics *xics = kvm->arch.xics; struct kvmppc_ics *ics; struct ics_irq_state *state; u16 src; if (!xics) return -ENODEV; ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) return -EINVAL; state = &ics->irq_state[src]; write_xive(xics, ics, state, state->server, MASKED, state->priority); return 0; }
int kvmppc_xics_get_xive(struct kvm *kvm, u32 irq, u32 *server, u32 *priority) { struct kvmppc_xics *xics = kvm->arch.xics; struct kvmppc_ics *ics; struct ics_irq_state *state; u16 src; if (!xics) return -ENODEV; ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) return -EINVAL; state = &ics->irq_state[src]; mutex_lock(&ics->lock); *server = state->server; *priority = state->priority; mutex_unlock(&ics->lock); return 0; }
/* * Return value ideally indicates how the interrupt was handled, but no * callers look at it (given that we don't implement KVM_IRQ_LINE_STATUS), * so just return 0. */ static int ics_deliver_irq(struct kvmppc_xics *xics, u32 irq, u32 level) { struct ics_irq_state *state; struct kvmppc_ics *ics; u16 src; XICS_DBG("ics deliver %#x (level: %d)\n", irq, level); ics = kvmppc_xics_find_ics(xics, irq, &src); if (!ics) { XICS_DBG("ics_deliver_irq: IRQ 0x%06x not found !\n", irq); return -EINVAL; } state = &ics->irq_state[src]; if (!state->exists) return -EINVAL; /* * We set state->asserted locklessly. This should be fine as * we are the only setter, thus concurrent access is undefined * to begin with. */ if ((level == 1 && state->lsi) || level == KVM_INTERRUPT_SET_LEVEL) state->asserted = 1; else if (level == 0 || level == KVM_INTERRUPT_UNSET) { state->asserted = 0; return 0; } /* Record which CPU this arrived on for passed-through interrupts */ if (state->host_irq) state->intr_cpu = raw_smp_processor_id(); /* Attempt delivery */ icp_deliver_irq(xics, NULL, irq); return 0; }