aboutsummaryrefslogtreecommitdiffstats
path: root/unmodified_drivers
diff options
context:
space:
mode:
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-04-11 09:16:04 +0100
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>2007-04-11 09:16:04 +0100
commitfc91db2baf8316ee6bb7c4c505917c121d4bdb21 (patch)
tree38949928f3d4b064779ef0857d57c1323c1efe98 /unmodified_drivers
parent083ba719d99aec77d605f02fbd03350d9a4596c1 (diff)
downloadxen-fc91db2baf8316ee6bb7c4c505917c121d4bdb21.tar.gz
xen-fc91db2baf8316ee6bb7c4c505917c121d4bdb21.tar.bz2
xen-fc91db2baf8316ee6bb7c4c505917c121d4bdb21.zip
PV-on-HVM: More save/restore fixes.
Signed-off-by: Keir Fraser <keir@xensource.com>
Diffstat (limited to 'unmodified_drivers')
-rw-r--r--unmodified_drivers/linux-2.6/platform-pci/evtchn.c142
-rw-r--r--unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c73
-rw-r--r--unmodified_drivers/linux-2.6/platform-pci/platform-pci.c34
-rw-r--r--unmodified_drivers/linux-2.6/platform-pci/platform-pci.h11
4 files changed, 172 insertions, 88 deletions
diff --git a/unmodified_drivers/linux-2.6/platform-pci/evtchn.c b/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
index 5e7a3dfcde..8373056999 100644
--- a/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
+++ b/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
@@ -28,8 +28,10 @@
* IN THE SOFTWARE.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/spinlock.h>
#include <xen/evtchn.h>
#include <xen/interface/hvm/ioreq.h>
#include <xen/features.h>
@@ -41,29 +43,37 @@
void *shared_info_area;
-static DEFINE_MUTEX(irq_evtchn_mutex);
-
#define is_valid_evtchn(x) ((x) != 0)
#define evtchn_from_irq(x) (irq_evtchn[irq].evtchn)
static struct {
+ spinlock_t lock;
irqreturn_t(*handler) (int, void *, struct pt_regs *);
void *dev_id;
int evtchn;
int close:1; /* close on unbind_from_irqhandler()? */
int inuse:1;
+ int in_handler:1;
} irq_evtchn[256];
static int evtchn_to_irq[NR_EVENT_CHANNELS] = {
[0 ... NR_EVENT_CHANNELS-1] = -1 };
-static int find_unbound_irq(void)
+static DEFINE_SPINLOCK(irq_alloc_lock);
+
+static int alloc_xen_irq(void)
{
static int warned;
int irq;
- for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)
- if (!irq_evtchn[irq].inuse)
- return irq;
+ spin_lock(&irq_alloc_lock);
+
+ for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++) {
+ if (irq_evtchn[irq].inuse)
+ continue;
+ irq_evtchn[irq].inuse = 1;
+ spin_unlock(&irq_alloc_lock);
+ return irq;
+ }
if (!warned) {
warned = 1;
@@ -71,9 +81,18 @@ static int find_unbound_irq(void)
"increase irq_evtchn[] size in evtchn.c.\n");
}
+ spin_unlock(&irq_alloc_lock);
+
return -ENOSPC;
}
+static void free_xen_irq(int irq)
+{
+ spin_lock(&irq_alloc_lock);
+ irq_evtchn[irq].inuse = 0;
+ spin_unlock(&irq_alloc_lock);
+}
+
int irq_to_evtchn_port(int irq)
{
return irq_evtchn[irq].evtchn;
@@ -93,8 +112,7 @@ void unmask_evtchn(int port)
shared_info_t *s = shared_info_area;
vcpu_info_t *vcpu_info;
- preempt_disable();
- cpu = smp_processor_id();
+ cpu = get_cpu();
vcpu_info = &s->vcpu_info[cpu];
/* Slow path (hypercall) if this is a non-local port. We only
@@ -103,7 +121,7 @@ void unmask_evtchn(int port)
evtchn_unmask_t op = { .port = port };
(void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask,
&op);
- preempt_enable();
+ put_cpu();
return;
}
@@ -121,7 +139,8 @@ void unmask_evtchn(int port)
if (!vcpu_info->evtchn_upcall_mask)
force_evtchn_callback();
}
- preempt_enable();
+
+ put_cpu();
}
EXPORT_SYMBOL(unmask_evtchn);
@@ -135,20 +154,19 @@ int bind_listening_port_to_irqhandler(
struct evtchn_alloc_unbound alloc_unbound;
int err, irq;
- mutex_lock(&irq_evtchn_mutex);
-
- irq = find_unbound_irq();
- if (irq < 0) {
- mutex_unlock(&irq_evtchn_mutex);
+ irq = alloc_xen_irq();
+ if (irq < 0)
return irq;
- }
+
+ spin_lock_irq(&irq_evtchn[irq].lock);
alloc_unbound.dom = DOMID_SELF;
alloc_unbound.remote_dom = remote_domain;
err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
&alloc_unbound);
if (err) {
- mutex_unlock(&irq_evtchn_mutex);
+ spin_unlock_irq(&irq_evtchn[irq].lock);
+ free_xen_irq(irq);
return err;
}
@@ -156,13 +174,13 @@ int bind_listening_port_to_irqhandler(
irq_evtchn[irq].dev_id = dev_id;
irq_evtchn[irq].evtchn = alloc_unbound.port;
irq_evtchn[irq].close = 1;
- irq_evtchn[irq].inuse = 1;
evtchn_to_irq[alloc_unbound.port] = irq;
unmask_evtchn(alloc_unbound.port);
- mutex_unlock(&irq_evtchn_mutex);
+ spin_unlock_irq(&irq_evtchn[irq].lock);
+
return irq;
}
EXPORT_SYMBOL(bind_listening_port_to_irqhandler);
@@ -176,34 +194,34 @@ int bind_caller_port_to_irqhandler(
{
int irq;
- mutex_lock(&irq_evtchn_mutex);
-
- irq = find_unbound_irq();
- if (irq < 0) {
- mutex_unlock(&irq_evtchn_mutex);
+ irq = alloc_xen_irq();
+ if (irq < 0)
return irq;
- }
+
+ spin_lock_irq(&irq_evtchn[irq].lock);
irq_evtchn[irq].handler = handler;
irq_evtchn[irq].dev_id = dev_id;
irq_evtchn[irq].evtchn = caller_port;
irq_evtchn[irq].close = 0;
- irq_evtchn[irq].inuse = 1;
evtchn_to_irq[caller_port] = irq;
unmask_evtchn(caller_port);
- mutex_unlock(&irq_evtchn_mutex);
+ spin_unlock_irq(&irq_evtchn[irq].lock);
+
return irq;
}
EXPORT_SYMBOL(bind_caller_port_to_irqhandler);
void unbind_from_irqhandler(unsigned int irq, void *dev_id)
{
- int evtchn = evtchn_from_irq(irq);
+ int evtchn;
+
+ spin_lock_irq(&irq_evtchn[irq].lock);
- mutex_lock(&irq_evtchn_mutex);
+ evtchn = evtchn_from_irq(irq);
if (is_valid_evtchn(evtchn)) {
evtchn_to_irq[irq] = -1;
@@ -216,21 +234,28 @@ void unbind_from_irqhandler(unsigned int irq, void *dev_id)
irq_evtchn[irq].handler = NULL;
irq_evtchn[irq].evtchn = 0;
- irq_evtchn[irq].inuse = 0;
- mutex_unlock(&irq_evtchn_mutex);
+ spin_unlock_irq(&irq_evtchn[irq].lock);
+
+ while (irq_evtchn[irq].in_handler)
+ cpu_relax();
+
+ free_xen_irq(irq);
}
EXPORT_SYMBOL(unbind_from_irqhandler);
void notify_remote_via_irq(int irq)
{
- int evtchn = evtchn_from_irq(irq);
+ int evtchn;
+
+ evtchn = evtchn_from_irq(irq);
if (is_valid_evtchn(evtchn))
notify_remote_via_evtchn(evtchn);
}
EXPORT_SYMBOL(notify_remote_via_irq);
-irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t evtchn_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
{
unsigned int l1i, port;
/* XXX: All events are bound to vcpu0 but irq may be redirected. */
@@ -249,13 +274,30 @@ irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
while ((l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i])) {
port = (l1i * BITS_PER_LONG) + __ffs(l2);
synch_clear_bit(port, &s->evtchn_pending[0]);
+
irq = evtchn_to_irq[port];
- if ((irq >= 0) &&
- ((handler = irq_evtchn[irq].handler) != NULL))
- handler(irq, irq_evtchn[irq].dev_id, regs);
- else
- printk(KERN_WARNING "unexpected event channel "
- "upcall on port %d!\n", port);
+ if (irq < 0)
+ continue;
+
+ spin_lock(&irq_evtchn[irq].lock);
+ handler = irq_evtchn[irq].handler;
+ dev_id = irq_evtchn[irq].dev_id;
+ if (unlikely(handler == NULL)) {
+ printk("Xen IRQ%d (port %d) has no handler!\n",
+ irq, port);
+ spin_unlock(&irq_evtchn[irq].lock);
+ continue;
+ }
+ irq_evtchn[irq].in_handler = 1;
+ spin_unlock(&irq_evtchn[irq].lock);
+
+ local_irq_enable();
+ handler(irq, irq_evtchn[irq].dev_id, regs);
+ local_irq_disable();
+
+ spin_lock(&irq_evtchn[irq].lock);
+ irq_evtchn[irq].in_handler = 0;
+ spin_unlock(&irq_evtchn[irq].lock);
}
}
@@ -268,16 +310,6 @@ void force_evtchn_callback(void)
}
EXPORT_SYMBOL(force_evtchn_callback);
-void irq_suspend(void)
-{
- mutex_lock(&irq_evtchn_mutex);
-}
-
-void irq_suspend_cancel(void)
-{
- mutex_unlock(&irq_evtchn_mutex);
-}
-
void irq_resume(void)
{
int evtchn, irq;
@@ -289,6 +321,16 @@ void irq_resume(void)
for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)
irq_evtchn[irq].evtchn = 0;
+}
+
+int xen_irq_init(struct pci_dev *pdev)
+{
+ int irq;
+
+ for (irq = 0; irq < ARRAY_SIZE(irq_evtchn); irq++)
+ spin_lock_init(&irq_evtchn[irq].lock);
- mutex_unlock(&irq_evtchn_mutex);
+ return request_irq(pdev->irq, evtchn_interrupt,
+ SA_SHIRQ | SA_SAMPLE_RANDOM | SA_INTERRUPT,
+ "xen-platform-pci", pdev);
}
diff --git a/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c b/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c
index d14bdbf1d9..c6b1f91b7f 100644
--- a/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c
+++ b/unmodified_drivers/linux-2.6/platform-pci/machine_reboot.c
@@ -1,24 +1,81 @@
#include <linux/config.h>
+#include <linux/stop_machine.h>
+#include <xen/evtchn.h>
+#include <xen/gnttab.h>
#include <xen/xenbus.h>
#include "platform-pci.h"
#include <asm/hypervisor.h>
-int __xen_suspend(int fast_suspend)
+/*
+ * Spinning prevents, for example, APs touching grant table entries while
+ * the shared grant table is not mapped into the address space imemdiately
+ * after resume.
+ */
+static void ap_suspend(void *_ap_spin)
+{
+ int *ap_spin = _ap_spin;
+
+ BUG_ON(!irqs_disabled());
+
+ while (*ap_spin) {
+ cpu_relax();
+ HYPERVISOR_yield();
+ }
+}
+
+static int bp_suspend(void)
{
int suspend_cancelled;
- xenbus_suspend();
- platform_pci_suspend();
+ BUG_ON(!irqs_disabled());
suspend_cancelled = HYPERVISOR_shutdown(SHUTDOWN_suspend);
- if (suspend_cancelled) {
- platform_pci_suspend_cancel();
- xenbus_suspend_cancel();
- } else {
+ if (!suspend_cancelled) {
platform_pci_resume();
- xenbus_resume();
+ gnttab_resume();
+ irq_resume();
+ }
+
+ return suspend_cancelled;
+}
+
+int __xen_suspend(int fast_suspend)
+{
+ int err, suspend_cancelled, ap_spin;
+
+ xenbus_suspend();
+
+ preempt_disable();
+
+ /* Prevent any races with evtchn_interrupt() handler. */
+ disable_irq(xen_platform_pdev->irq);
+
+ ap_spin = 1;
+ smp_mb();
+
+ err = smp_call_function(ap_suspend, &ap_spin, 0, 0);
+ if (err < 0) {
+ preempt_enable();
+ xenbus_suspend_cancel();
+ return err;
}
+ local_irq_disable();
+ suspend_cancelled = bp_suspend();
+ local_irq_enable();
+
+ smp_mb();
+ ap_spin = 0;
+
+ enable_irq(xen_platform_pdev->irq);
+
+ preempt_enable();
+
+ if (!suspend_cancelled)
+ xenbus_resume();
+ else
+ xenbus_suspend_cancel();
+
return 0;
}
diff --git a/unmodified_drivers/linux-2.6/platform-pci/platform-pci.c b/unmodified_drivers/linux-2.6/platform-pci/platform-pci.c
index 8dc2f73153..5e218686ba 100644
--- a/unmodified_drivers/linux-2.6/platform-pci/platform-pci.c
+++ b/unmodified_drivers/linux-2.6/platform-pci/platform-pci.c
@@ -40,7 +40,6 @@
#include <xen/interface/hvm/params.h>
#include <xen/features.h>
#include <xen/evtchn.h>
-#include <xen/gnttab.h>
#ifdef __ia64__
#include <asm/xen/xencomm.h>
#endif
@@ -62,6 +61,8 @@ MODULE_AUTHOR("ssmith@xensource.com");
MODULE_DESCRIPTION("Xen platform PCI device");
MODULE_LICENSE("GPL");
+struct pci_dev *xen_platform_pdev;
+
static unsigned long shared_info_frame;
static uint64_t callback_via;
@@ -89,8 +90,6 @@ static int __devinit init_xen_info(void)
if (shared_info_area == NULL)
panic("can't map shared info\n");
- gnttab_init();
-
return 0;
}
@@ -199,8 +198,10 @@ static int set_callback_via(uint64_t via)
return HYPERVISOR_hvm_op(HVMOP_set_param, &a);
}
+int xen_irq_init(struct pci_dev *pdev);
int xenbus_init(void);
int xen_reboot_init(void);
+int gnttab_init(void);
static int __devinit platform_pci_init(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -209,6 +210,10 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
long ioaddr, iolen;
long mmio_addr, mmio_len;
+ if (xen_platform_pdev)
+ return -EBUSY;
+ xen_platform_pdev = pdev;
+
i = pci_enable_device(pdev);
if (i)
return i;
@@ -249,9 +254,10 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
if ((ret = init_xen_info()))
goto out;
- if ((ret = request_irq(pdev->irq, evtchn_interrupt,
- SA_SHIRQ | SA_SAMPLE_RANDOM,
- "xen-platform-pci", pdev)))
+ if ((ret = gnttab_init()))
+ goto out;
+
+ if ((ret = xen_irq_init(pdev)))
goto out;
if ((ret = set_callback_via(callback_via)))
@@ -292,18 +298,6 @@ static struct pci_driver platform_driver = {
static int pci_device_registered;
-void platform_pci_suspend(void)
-{
- gnttab_suspend();
- irq_suspend();
-}
-
-void platform_pci_suspend_cancel(void)
-{
- irq_suspend_cancel();
- gnttab_resume();
-}
-
void platform_pci_resume(void)
{
struct xen_add_to_physmap xatp;
@@ -319,12 +313,8 @@ void platform_pci_resume(void)
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
BUG();
- irq_resume();
-
if (set_callback_via(callback_via))
printk("platform_pci_resume failure!\n");
-
- gnttab_resume();
}
static int __init platform_pci_module_init(void)
diff --git a/unmodified_drivers/linux-2.6/platform-pci/platform-pci.h b/unmodified_drivers/linux-2.6/platform-pci/platform-pci.h
index 098db4525b..25372136bd 100644
--- a/unmodified_drivers/linux-2.6/platform-pci/platform-pci.h
+++ b/unmodified_drivers/linux-2.6/platform-pci/platform-pci.h
@@ -22,16 +22,11 @@
#ifndef _XEN_PLATFORM_PCI_H
#define _XEN_PLATFORM_PCI_H
-#include <linux/interrupt.h>
+#include <linux/pci.h>
unsigned long alloc_xen_mmio(unsigned long len);
-int gnttab_init(void);
-irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-void irq_suspend(void);
-void irq_suspend_cancel(void);
-
-void platform_pci_suspend(void);
-void platform_pci_suspend_cancel(void);
void platform_pci_resume(void);
+extern struct pci_dev *xen_platform_pdev;
+
#endif /* _XEN_PLATFORM_PCI_H */