aboutsummaryrefslogtreecommitdiffstats
path: root/virt/kvm/ioapic.c
diff options
context:
space:
mode:
Diffstat (limited to 'virt/kvm/ioapic.c')
-rw-r--r--virt/kvm/ioapic.c75
1 files changed, 56 insertions, 19 deletions
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index 38a2d20..0b9df83 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2001 MandrakeSoft S.A.
+ * Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* MandrakeSoft S.A.
* 43, rue d'Aboukir
@@ -33,6 +34,7 @@
#include <linux/smp.h>
#include <linux/hrtimer.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/current.h>
@@ -100,6 +102,19 @@ static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx)
return injected;
}
+static void update_handled_vectors(struct kvm_ioapic *ioapic)
+{
+ DECLARE_BITMAP(handled_vectors, 256);
+ int i;
+
+ memset(handled_vectors, 0, sizeof(handled_vectors));
+ for (i = 0; i < IOAPIC_NUM_PINS; ++i)
+ __set_bit(ioapic->redirtbl[i].fields.vector, handled_vectors);
+ memcpy(ioapic->handled_vectors, handled_vectors,
+ sizeof(handled_vectors));
+ smp_wmb();
+}
+
static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
{
unsigned index;
@@ -134,9 +149,10 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
e->bits |= (u32) val;
e->fields.remote_irr = 0;
}
+ update_handled_vectors(ioapic);
mask_after = e->fields.mask;
if (mask_before != mask_after)
- kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after);
+ kvm_fire_mask_notifiers(ioapic->kvm, KVM_IRQCHIP_IOAPIC, index, mask_after);
if (e->fields.trig_mode == IOAPIC_LEVEL_TRIG
&& ioapic->irr & (1 << index))
ioapic_service(ioapic, index);
@@ -177,12 +193,13 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
{
- u32 old_irr = ioapic->irr;
+ u32 old_irr;
u32 mask = 1 << irq;
union kvm_ioapic_redirect_entry entry;
int ret = 1;
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
+ old_irr = ioapic->irr;
if (irq >= 0 && irq < IOAPIC_NUM_PINS) {
entry = ioapic->redirtbl[irq];
level ^= entry.fields.polarity;
@@ -199,7 +216,7 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
}
trace_kvm_ioapic_set_irq(entry.bits, irq, ret == 0);
}
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
return ret;
}
@@ -223,9 +240,9 @@ static void __kvm_ioapic_update_eoi(struct kvm_ioapic *ioapic, int vector,
* is dropped it will be put into irr and will be delivered
* after ack notifier returns.
*/
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
kvm_notify_acked_irq(ioapic->kvm, KVM_IRQCHIP_IOAPIC, i);
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
if (trigger_mode != IOAPIC_LEVEL_TRIG)
continue;
@@ -241,9 +258,12 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode)
{
struct kvm_ioapic *ioapic = kvm->arch.vioapic;
- mutex_lock(&ioapic->lock);
+ smp_rmb();
+ if (!test_bit(vector, ioapic->handled_vectors))
+ return;
+ spin_lock(&ioapic->lock);
__kvm_ioapic_update_eoi(ioapic, vector, trigger_mode);
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
}
static inline struct kvm_ioapic *to_ioapic(struct kvm_io_device *dev)
@@ -269,7 +289,7 @@ static int ioapic_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
ASSERT(!(addr & 0xf)); /* check alignment */
addr &= 0xff;
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
switch (addr) {
case IOAPIC_REG_SELECT:
result = ioapic->ioregsel;
@@ -283,7 +303,7 @@ static int ioapic_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
result = 0;
break;
}
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
switch (len) {
case 8:
@@ -320,7 +340,7 @@ static int ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
}
addr &= 0xff;
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
switch (addr) {
case IOAPIC_REG_SELECT:
ioapic->ioregsel = data;
@@ -338,7 +358,7 @@ static int ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
default:
break;
}
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
return 0;
}
@@ -352,6 +372,7 @@ void kvm_ioapic_reset(struct kvm_ioapic *ioapic)
ioapic->ioregsel = 0;
ioapic->irr = 0;
ioapic->id = 0;
+ update_handled_vectors(ioapic);
}
static const struct kvm_io_device_ops ioapic_mmio_ops = {
@@ -367,27 +388,42 @@ int kvm_ioapic_init(struct kvm *kvm)
ioapic = kzalloc(sizeof(struct kvm_ioapic), GFP_KERNEL);
if (!ioapic)
return -ENOMEM;
- mutex_init(&ioapic->lock);
+ spin_lock_init(&ioapic->lock);
kvm->arch.vioapic = ioapic;
kvm_ioapic_reset(ioapic);
kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops);
ioapic->kvm = kvm;
- ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &ioapic->dev);
- if (ret < 0)
+ mutex_lock(&kvm->slots_lock);
+ ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
+ mutex_unlock(&kvm->slots_lock);
+ if (ret < 0) {
+ kvm->arch.vioapic = NULL;
kfree(ioapic);
+ }
return ret;
}
+void kvm_ioapic_destroy(struct kvm *kvm)
+{
+ struct kvm_ioapic *ioapic = kvm->arch.vioapic;
+
+ if (ioapic) {
+ kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
+ kvm->arch.vioapic = NULL;
+ kfree(ioapic);
+ }
+}
+
int kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
{
struct kvm_ioapic *ioapic = ioapic_irqchip(kvm);
if (!ioapic)
return -EINVAL;
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
memcpy(state, ioapic, sizeof(struct kvm_ioapic_state));
- mutex_unlock(&ioapic->lock);
+ spin_unlock(&ioapic->lock);
return 0;
}
@@ -397,8 +433,9 @@ int kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
if (!ioapic)
return -EINVAL;
- mutex_lock(&ioapic->lock);
+ spin_lock(&ioapic->lock);
memcpy(ioapic, state, sizeof(struct kvm_ioapic_state));
- mutex_unlock(&ioapic->lock);
+ update_handled_vectors(ioapic);
+ spin_unlock(&ioapic->lock);
return 0;
}